diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index b367c514232e..37343142625e 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -21,31 +21,29 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define CONDUCT_1 (1<<5) /// For machines and structures that should not break into parts, eg, holodeck stuff #define NODECONSTRUCT_1 (1<<7) -/// atom queued to SSoverlay -#define OVERLAY_QUEUED_1 (1<<8) /// item has priority to check when entering or leaving -#define ON_BORDER_1 (1<<9) +#define ON_BORDER_1 (1<<8) //Whether or not this atom shows screentips when hovered over -#define NO_SCREENTIPS_1 (1<<10) +#define NO_SCREENTIPS_1 (1<<9) /// Prevent clicking things below it on the same turf eg. doors/ fulltile windows -#define PREVENT_CLICK_UNDER_1 (1<<11) -#define HOLOGRAM_1 (1<<12) +#define PREVENT_CLICK_UNDER_1 (1<<10) +#define HOLOGRAM_1 (1<<11) /// Prevents mobs from getting chainshocked by teslas and the supermatter -#define SHOCKED_1 (1<<13) +#define SHOCKED_1 (1<<12) ///Whether /atom/Initialize() has already run for the object -#define INITIALIZED_1 (1<<14) +#define INITIALIZED_1 (1<<13) /// was this spawned by an admin? used for stat tracking stuff. -#define ADMIN_SPAWNED_1 (1<<15) +#define ADMIN_SPAWNED_1 (1<<14) /// should not get harmed if this gets caught by an explosion? -#define PREVENT_CONTENTS_EXPLOSION_1 (1<<16) +#define PREVENT_CONTENTS_EXPLOSION_1 (1<<15) /// should the contents of this atom be acted upon -#define RAD_PROTECT_CONTENTS_1 (1 << 17) +#define RAD_PROTECT_CONTENTS_1 (1 << 16) /// should this object be allowed to be contaminated -#define RAD_NO_CONTAMINATE_1 (1 << 18) +#define RAD_NO_CONTAMINATE_1 (1 << 17) ///Use when this shouldn't be obscured by large icons, like trees. -#define SHOW_BEHIND_LARGE_ICONS_1 (1<<12) +#define SHOW_BEHIND_LARGE_ICONS_1 (1<<18) /// Should we use the initial icon for display? Mostly used by overlay only objects -#define HTML_USE_INITAL_ICON_1 (1<<20) +#define HTML_USE_INITAL_ICON_1 (1<<19) // Update flags for [/atom/proc/update_appearance] /// Update the atom's name diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 7230e7cfab9b..9b8514d8bea8 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -444,3 +444,6 @@ //Saves a proc call, life is suffering. If who has no targets_from var, we assume it's just who #define GET_TARGETS_FROM(who) (who.targets_from ? who.get_targets_from() : who) + +/// In dynamic human icon gen we don't replace the held item. +#define NO_REPLACE 0 diff --git a/code/__DEFINES/overlays.dm b/code/__DEFINES/overlays.dm new file mode 100644 index 000000000000..b21bb5007672 --- /dev/null +++ b/code/__DEFINES/overlays.dm @@ -0,0 +1,27 @@ +/* I do want this however this currently only fails on decals specificly in create and destroy. + Updating the overlays in genral was already pretty unatomic.area + I've added porting some updated decal code from tg to resolve this to my list. + WARNING("Too many overlays on [changed_on.type] - [length(changed_on.overlays)], refusing to update and cutting.\ + \n What follows is a printout of all existing overlays at the time of the overflow \n[text_lays]"); \ +*/ + +// A reasonable number of maximum overlays an object needs +// If you think you need more, rethink it +#define MAX_ATOM_OVERLAYS 100 + +/// Checks if an atom has reached the overlay limit, and make a loud error if it does. +#define VALIDATE_OVERLAY_LIMIT(changed_on) \ + if(length(changed_on.overlays) >= MAX_ATOM_OVERLAYS) { \ + changed_on.overlays.Cut(); \ + } \ + +/// Performs any operations that ought to run after an appearance change +#define POST_OVERLAY_CHANGE(changed_on) \ + if(alternate_appearances) { \ + for(var/I in changed_on.alternate_appearances){\ + var/datum/atom_hud/alternate_appearance/AA = changed_on.alternate_appearances[I];\ + if(AA.transfer_overlays){\ + AA.copy_overlays(changed_on, TRUE);\ + }\ + } \ + } diff --git a/code/__DEFINES/stat_tracking.dm b/code/__DEFINES/stat_tracking.dm index 69ea3c598bee..a7be42d4c4e2 100644 --- a/code/__DEFINES/stat_tracking.dm +++ b/code/__DEFINES/stat_tracking.dm @@ -8,10 +8,6 @@ #define STAT_LOG_ENTRY(entrylist, entryname) \ var/list/STAT_ENTRY = entrylist[entryname] || (entrylist[entryname] = new /list(STAT_ENTRY_LENGTH)); \ STAT_ENTRY[STAT_ENTRY_TIME] += STAT_TIME; \ - var/STAT_INCR_AMOUNT = min(1, 2**round((STAT_ENTRY[STAT_ENTRY_COUNT] || 0)/SHORT_REAL_LIMIT)); \ - if (STAT_INCR_AMOUNT == 1 || prob(100/STAT_INCR_AMOUNT)) { \ - STAT_ENTRY[STAT_ENTRY_COUNT] += STAT_INCR_AMOUNT; \ - }; \ - + STAT_ENTRY[STAT_ENTRY_COUNT] += 1; diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index b702358a9977..e82c04f57904 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -241,31 +241,6 @@ #define SSEXPLOSIONS_TURFS 2 #define SSEXPLOSIONS_THROWS 3 -//! ## Overlays subsystem - -///Compile all the overlays for an atom from the cache lists -// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway. -#define COMPILE_OVERLAYS(A) \ - do{ \ - var/list/ad = A.add_overlays; \ - var/list/rm = A.remove_overlays; \ - if(LAZYLEN(rm)){ \ - A.overlays -= rm; \ - rm.Cut(); \ - } \ - if(LAZYLEN(ad)){ \ - A.overlays |= ad; \ - ad.Cut(); \ - } \ - for(var/I in A.alternate_appearances){ \ - var/datum/atom_hud/alternate_appearance/AA = A.alternate_appearances[I]; \ - if(AA.transfer_overlays){ \ - AA.copy_overlays(A, TRUE); \ - } \ - } \ - A.flags_1 &= ~OVERLAY_QUEUED_1; \ - }while(FALSE) - // Vote subsystem counting methods /// First past the post. One selection per person, and the selection with the most votes wins. #define VOTE_COUNT_METHOD_SINGLE 1 diff --git a/code/__HELPERS/dynamic_human_icon_gen.dm b/code/__HELPERS/dynamic_human_icon_gen.dm new file mode 100644 index 000000000000..c884e901d4fa --- /dev/null +++ b/code/__HELPERS/dynamic_human_icon_gen.dm @@ -0,0 +1,65 @@ +///Global list of all dynamically generated icons, for caching, so we don't have to generate multiple times. +GLOBAL_LIST_EMPTY(dynamic_human_appearances) + +/// Creates a human with the given parameters and returns an appearance of it +/proc/get_dynamic_human_appearance(outfit_path, species_path = /datum/species/human, mob_spawn_path, r_hand, l_hand, bloody_slots = NONE, animated = TRUE) + if(!species_path) + return FALSE + if(!ispath(species_path)) + stack_trace("Attempted to call get_dynamic_human_appearance() with an instantiated species_path. Pass the species datum typepath instead.") + return FALSE + var/arg_string = "[outfit_path]_[species_path]_[mob_spawn_path]_[l_hand]_[r_hand]_[bloody_slots]" + if(GLOB.dynamic_human_appearances[arg_string]) //if already exists in our cache, just return that + return GLOB.dynamic_human_appearances[arg_string] + var/mob/living/carbon/human/dummy/consistent/dummy = new() + dummy.set_species(species_path) + dummy.stat = CONSCIOUS //He needs to be alive or he has no eyes. Scary + dummy.underwear = "Nude" + dummy.undershirt = "Nude" + dummy.socks = "Nude" + if(outfit_path) + var/datum/outfit/outfit = new outfit_path() + if(r_hand != NO_REPLACE) //we can still override to be null, no replace means just use outfit's + outfit.r_hand = r_hand + if(l_hand != NO_REPLACE) + outfit.l_hand = l_hand + dummy.equipOutfit(outfit, visualsOnly = TRUE) + else if(mob_spawn_path) + var/obj/effect/mob_spawn/human/spawner = new mob_spawn_path(null) + if(r_hand != NO_REPLACE) + spawner.r_hand = r_hand + if(l_hand != NO_REPLACE) + spawner.l_hand = l_hand + spawner.special(dummy, dummy) + spawner.equip(dummy) + for(var/obj/item/carried_item in dummy) + if(dummy.is_holding(carried_item)) + var/datum/component/two_handed/twohanded = carried_item.GetComponent(/datum/component/two_handed) + if(twohanded) + twohanded.wield(dummy) + /* + var/datum/component/transforming/transforming = carried_item.GetComponent(/datum/component/transforming) + if(transforming) + transforming.set_active(carried_item) + */ + if(bloody_slots & carried_item.slot_flags) + carried_item.add_mob_blood(dummy) + //dummy.update_held_items() + dummy.regenerate_icons() + var/mutable_appearance/output = dummy.appearance + GLOB.dynamic_human_appearances[arg_string] = output + qdel(dummy) + return output + +///This exists to apply the icons async, as that cannot be done in Initialize because of possible sleeps. +/proc/apply_dynamic_human_appearance(atom/target, outfit_path, species_path = /datum/species/human, mob_spawn_path, r_hand, l_hand, bloody_slots = NONE) + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(set_dynamic_human_appearance), args) + +///This proc gets an argument of a target and runs +/proc/set_dynamic_human_appearance(list/arguments) + var/atom/target = arguments[1] //1st argument is the target + var/dynamic_appearance = get_dynamic_human_appearance(arglist(arguments.Copy(2))) //the rest of the arguments starting from 2 matter to the proc + target.icon = 'icons/mob/human.dmi' + target.icon_state = "" + target.appearance_flags |= KEEP_TOGETHER + target.copy_overlays(dynamic_appearance, TRUE) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 3e676f289458..9f229669c4a6 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1084,7 +1084,6 @@ GLOBAL_LIST_EMPTY(friendly_animal_types) var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing") - COMPILE_OVERLAYS(body) for(var/D in showDirs) body.setDir(D) var/icon/partial = getFlatIcon(body, defdir=D) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 9ff2cbe896ae..d5f43d594be8 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -87,10 +87,6 @@ #define SHIP_PLACEMENT_TEST #endif -// A reasonable number of maximum overlays an object needs -// If you think you need more, rethink it -#define MAX_ATOM_OVERLAYS 100 - #if defined(OPENDREAM) #if !defined(CIBUILDING) #warn You are building with OpenDream. Remember to build TGUI manually. diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 61fa476fe7b1..0af9eb6bca23 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -99,7 +99,6 @@ DEFINE_BITFIELD(flags_1, list( "NO_LAVA_GEN_1" = NO_LAVA_GEN_1, "NO_RUINS_1" = NO_RUINS_1, "ON_BORDER_1" = ON_BORDER_1, - "OVERLAY_QUEUED_1" = OVERLAY_QUEUED_1, "PREVENT_CLICK_UNDER_1" = PREVENT_CLICK_UNDER_1, "PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1, "RAD_NO_CONTAMINATE_1" = RAD_NO_CONTAMINATE_1, diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index d6c720380f46..cde6b820ad32 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -29,3 +29,5 @@ GLOBAL_LIST_EMPTY(poll_options) GLOBAL_PROTECT(poll_options) GLOBAL_VAR_INIT(internal_tick_usage, 0.2 * world.tick_lag) //This var is updated every tick by a DLL if present, used to reduce lag + +GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index 43cccd87abbd..41df60feee45 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -1,184 +1,106 @@ SUBSYSTEM_DEF(overlays) name = "Overlay" - flags = SS_TICKER - wait = 1 - priority = FIRE_PRIORITY_OVERLAYS - init_order = INIT_ORDER_OVERLAY - - var/list/queue + flags = SS_NO_FIRE|SS_NO_INIT var/list/stats - var/list/overlay_icon_state_caches - var/list/overlay_icon_cache /datum/controller/subsystem/overlays/PreInit() - overlay_icon_state_caches = list() - overlay_icon_cache = list() - queue = list() stats = list() -/datum/controller/subsystem/overlays/Initialize() - initialized = TRUE - fire(mc_check = FALSE) - return ..() - - -/datum/controller/subsystem/overlays/stat_entry(msg) - msg = "Ov:[length(queue)]" - return ..() - - /datum/controller/subsystem/overlays/Shutdown() text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log") - /datum/controller/subsystem/overlays/Recover() - overlay_icon_state_caches = SSoverlays.overlay_icon_state_caches - overlay_icon_cache = SSoverlays.overlay_icon_cache - queue = SSoverlays.queue - - -/datum/controller/subsystem/overlays/fire(resumed = FALSE, mc_check = TRUE) - var/list/queue = src.queue - var/static/count = 0 - if (count) - var/c = count - count = 0 //so if we runtime on the Cut, we don't try again. - queue.Cut(1,c+1) - - for (var/thing in queue) - count++ - if(thing) - STAT_START_STOPWATCH - var/atom/A = thing - COMPILE_OVERLAYS(A) - STAT_STOP_STOPWATCH - STAT_LOG_ENTRY(stats, A.type) - if(mc_check) - if(MC_TICK_CHECK) - break - else - CHECK_TICK - - if (count) - queue.Cut(1,count+1) - count = 0 + stats = SSoverlays.stats + +/// Converts an overlay list into text for debug printing +/// Of note: overlays aren't actually mutable appearances, they're just appearances +/// Don't have access to that type tho, so this is the best you're gonna get +/proc/overlays2text(list/overlays) + var/list/unique_overlays = list() + // As anything because we're basically doing type coercion, rather then actually filtering for mutable appearances + for(var/mutable_appearance/overlay as anything in overlays) + var/key = "[overlay.icon]-[overlay.icon_state]-[overlay.dir]" + unique_overlays[key] += 1 + var/list/output_text = list() + for(var/key in unique_overlays) + output_text += "([key]) = [unique_overlays[key]]" + return output_text.Join("\n") /proc/iconstate2appearance(icon, iconstate) var/static/image/stringbro = new() - var/list/icon_states_cache = SSoverlays.overlay_icon_state_caches - var/list/cached_icon = icon_states_cache[icon] - if (cached_icon) - var/cached_appearance = cached_icon["[iconstate]"] - if (cached_appearance) - return cached_appearance stringbro.icon = icon stringbro.icon_state = iconstate - if (!cached_icon) //not using the macro to save an associated lookup - cached_icon = list() - icon_states_cache[icon] = cached_icon - var/cached_appearance = stringbro.appearance - cached_icon["[iconstate]"] = cached_appearance - return cached_appearance + return stringbro.appearance /proc/icon2appearance(icon) var/static/image/iconbro = new() - var/list/icon_cache = SSoverlays.overlay_icon_cache - . = icon_cache[icon] - if (!.) - iconbro.icon = icon - . = iconbro.appearance - icon_cache[icon] = . - -/atom/proc/build_appearance_list(old_overlays) - var/static/image/appearance_bro = new() - var/list/new_overlays = list() - if (!islist(old_overlays)) - old_overlays = list(old_overlays) - for (var/overlay in old_overlays) + iconbro.icon = icon + return iconbro.appearance + +/atom/proc/build_appearance_list(list/build_overlays) + if (!islist(build_overlays)) + build_overlays = list(build_overlays) + for (var/overlay in build_overlays) if(!overlay) + build_overlays -= overlay continue if (istext(overlay)) - new_overlays += iconstate2appearance(icon, overlay) + var/index = build_overlays.Find(overlay) + build_overlays[index] = iconstate2appearance(icon, overlay) else if(isicon(overlay)) - new_overlays += icon2appearance(overlay) - else - if(isloc(overlay)) - var/atom/A = overlay - if (A.flags_1 & OVERLAY_QUEUED_1) - COMPILE_OVERLAYS(A) - appearance_bro.appearance = overlay //this works for images and atoms too! - if(!ispath(overlay)) - var/image/I = overlay - appearance_bro.dir = I.dir - new_overlays += appearance_bro.appearance - return new_overlays - -#define NOT_QUEUED_ALREADY (!(flags_1 & OVERLAY_QUEUED_1)) -#define QUEUE_FOR_COMPILE flags_1 |= OVERLAY_QUEUED_1; SSoverlays.queue += src; -/atom/proc/cut_overlays() - LAZYINITLIST(remove_overlays) - LAZYINITLIST(add_overlays) - remove_overlays = overlays.Copy() - add_overlays.Cut() - - //If not already queued for work and there are overlays to remove - if(NOT_QUEUED_ALREADY && remove_overlays.len) - QUEUE_FOR_COMPILE + var/index = build_overlays.Find(overlay) + build_overlays[index] = icon2appearance(overlay) + return build_overlays -/mob/living/carbon/cut_overlays() - icon_render_keys = list() - return ..() +/atom/proc/cut_overlays() + STAT_START_STOPWATCH + overlays = null + POST_OVERLAY_CHANGE(src) + STAT_STOP_STOPWATCH + STAT_LOG_ENTRY(SSoverlays.stats, type) -/atom/proc/cut_overlay(list/overlays) +/atom/proc/cut_overlay(list/remove_overlays) if(!overlays) return - overlays = build_appearance_list(overlays) - LAZYINITLIST(add_overlays) //always initialized after this point - LAZYINITLIST(remove_overlays) - var/a_len = add_overlays.len - var/r_len = remove_overlays.len - remove_overlays += overlays - add_overlays -= overlays - - var/fa_len = add_overlays.len - var/fr_len = remove_overlays.len - - //If not already queued and there is work to be done - if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len)) - QUEUE_FOR_COMPILE - -/atom/proc/add_overlay(list/overlays) + STAT_START_STOPWATCH + overlays -= build_appearance_list(remove_overlays) + POST_OVERLAY_CHANGE(src) + STAT_STOP_STOPWATCH + STAT_LOG_ENTRY(SSoverlays.stats, type) + +/atom/proc/add_overlay(list/add_overlays) if(!overlays) return - - overlays = build_appearance_list(overlays) - - LAZYINITLIST(add_overlays) //always initialized after this point - var/a_len = add_overlays.len - - add_overlays += overlays - var/fa_len = add_overlays.len - if(NOT_QUEUED_ALREADY && fa_len != a_len) - QUEUE_FOR_COMPILE - -/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom + STAT_START_STOPWATCH + overlays += build_appearance_list(add_overlays) + VALIDATE_OVERLAY_LIMIT(src) + POST_OVERLAY_CHANGE(src) + STAT_STOP_STOPWATCH + STAT_LOG_ENTRY(SSoverlays.stats, type) + +/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom if(!other) if(cut_old) cut_overlays() return + STAT_START_STOPWATCH var/list/cached_other = other.overlays.Copy() - if(cached_other) - if(cut_old || !LAZYLEN(overlays)) - remove_overlays = overlays - add_overlays = cached_other - if(NOT_QUEUED_ALREADY) - QUEUE_FOR_COMPILE - else if(cut_old) - cut_overlays() - -#undef NOT_QUEUED_ALREADY -#undef QUEUE_FOR_COMPILE + if(cut_old) + if(cached_other) + overlays = cached_other + else + overlays = null + VALIDATE_OVERLAY_LIMIT(src) + POST_OVERLAY_CHANGE(src) + STAT_STOP_STOPWATCH + STAT_LOG_ENTRY(SSoverlays.stats, type) + else if(cached_other) + overlays += cached_other + VALIDATE_OVERLAY_LIMIT(src) + POST_OVERLAY_CHANGE(src) + STAT_STOP_STOPWATCH + STAT_LOG_ENTRY(SSoverlays.stats, type) //TODO: Better solution for these? /image/proc/add_overlay(x) @@ -204,3 +126,100 @@ SUBSYSTEM_DEF(overlays) overlays |= cached_other else if(cut_old) cut_overlays() + +// Debug procs + +/atom + /// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances + /// Drawn from the overlays list + var/list/realized_overlays + /// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances + /// Drawn from the underlays list + var/list/realized_underlays + +/image + /// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances + /// Drawn from the overlays list + var/list/realized_overlays + /// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances + /// Drawn from the underlays list + var/list/realized_underlays + +/// Takes the atoms's existing overlays and underlays, and makes them mutable so they can be properly vv'd in the realized_overlays/underlays list +/atom/proc/realize_overlays() + realized_overlays = realize_appearance_queue(overlays) + realized_underlays = realize_appearance_queue(underlays) + +/// Takes the image's existing overlays, and makes them mutable so they can be properly vv'd in the realized_overlays list +/image/proc/realize_overlays() + realized_overlays = realize_appearance_queue(overlays) + realized_underlays = realize_appearance_queue(underlays) + +/// Takes a list of appearnces, makes them mutable so they can be properly vv'd and inspected +/proc/realize_appearance_queue(list/appearances) + var/list/real_appearances = list() + var/list/queue = appearances.Copy() + var/queue_index = 0 + while(queue_index < length(queue)) + queue_index++ + // If it's not a command, we assert that it's an appearance + var/mutable_appearance/appearance = queue[queue_index] + if(!appearance) // Who fucking adds nulls to their sublists god you people are the worst + continue + + var/mutable_appearance/new_appearance = new /mutable_appearance() + new_appearance.appearance = appearance + var/key = "[appearance.icon]-[appearance.icon_state]-[appearance.plane]-[appearance.layer]-[appearance.dir]-[appearance.color]" + var/tmp_key = key + var/appearance_indx = 1 + while(real_appearances[tmp_key]) + tmp_key = "[key]-[appearance_indx]" + appearance_indx++ + + real_appearances[tmp_key] = new_appearance + var/add_index = queue_index + // Now check its children + for(var/mutable_appearance/child_appearance as anything in appearance.overlays) + add_index++ + queue.Insert(add_index, child_appearance) + for(var/mutable_appearance/child_appearance as anything in appearance.underlays) + add_index++ + queue.Insert(add_index, child_appearance) + return real_appearances + +/// Takes two appearances as args, prints out, logs, and returns a text representation of their differences +/// Including suboverlays +/proc/diff_appearances(mutable_appearance/first, mutable_appearance/second, iter = 0) + var/list/diffs = list() + var/list/firstdeet = first.vars + var/list/seconddeet = second.vars + var/diff_found = FALSE + for(var/name in first.vars) + var/firstv = firstdeet[name] + var/secondv = seconddeet[name] + if(firstv ~= secondv) + continue + if((islist(firstv) || islist(secondv)) && length(firstv) == 0 && length(secondv) == 0) + continue + if(name == "vars") // Go away + continue + if(name == "_listen_lookup") // This is just gonna happen with marked datums, don't care + continue + if(name == "overlays") + first.realize_overlays() + second.realize_overlays() + var/overlays_differ = FALSE + for(var/i in 1 to length(first.realized_overlays)) + if(diff_appearances(first.realized_overlays[i], second.realized_overlays[i], iter + 1)) + overlays_differ = TRUE + + if(!overlays_differ) + continue + + diff_found = TRUE + diffs += "Diffs detected at [name]: First ([firstv]), Second ([secondv])" + + var/text = "Depth of: [iter]\n\t[diffs.Join("\n\t")]" + message_admins(text) + log_world(text) + return diff_found diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index d394d7707be8..c2b8a58a1f35 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -324,7 +324,6 @@ if(outfit_type) mannequin.equipOutfit(outfit_type,TRUE) mannequin.setDir(SOUTH) - COMPILE_OVERLAYS(mannequin) . = image(mannequin) unset_busy_human_dummy("HOLODISK_PRESET") diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 75a36e1aa677..fe01a7edd1c4 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -680,9 +680,13 @@ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) var/list/new_overlays = update_overlays() - if(managed_overlays) - cut_overlay(managed_overlays) - managed_overlays = null + if (managed_overlays) + if (length(overlays) == (islist(managed_overlays) ? length(managed_overlays) : 1)) + overlays = null + POST_OVERLAY_CHANGE(src) + else + cut_overlay(managed_overlays) + managed_overlays = null if(length(new_overlays)) managed_overlays = new_overlays add_overlay(new_overlays) diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index 505221b2b013..41652d539940 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -5,12 +5,15 @@ resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF var/turf_loc_check = TRUE -/obj/effect/decal/Initialize() +/obj/effect/decal/Initialize(mapload) . = ..() - if(turf_loc_check && (!isturf(loc) || NeverShouldHaveComeHere(loc))) + if(NeverShouldHaveComeHere(loc)) + if(mapload) + WARNING("[name] spawned in a bad turf ([loc]) at [AREACOORD(src)] in \the [get_area(src)]. \ + Please remove it or allow it to pass NeverShouldHaveComeHere if it's intended.") return INITIALIZE_HINT_QDEL var/static/list/loc_connections = list( - COMSIG_TURF_CHANGED = PROC_REF(handle_turf_change), + COMSIG_TURF_CHANGE = PROC_REF(on_decal_move), ) AddElement(/datum/element/connect_loc, loc_connections) @@ -24,7 +27,7 @@ if(!(resistance_flags & FIRE_PROOF)) //non fire proof decal or being burned by lava qdel(src) -/obj/effect/decal/proc/handle_turf_change(turf/source, path, list/new_baseturfs, flags, list/post_change_callbacks) +/obj/effect/decal/proc/on_decal_move(turf/source, path, list/new_baseturfs, flags, list/post_change_callbacks) SIGNAL_HANDLER post_change_callbacks += CALLBACK(src, PROC_REF(sanity_check_self)) @@ -61,5 +64,17 @@ /obj/effect/turf_decal/Destroy(force) SHOULD_CALL_PARENT(FALSE) + + // If we don't do this, turf decals will end up stacking up on a tile, and break the overlay limit + // I hate it too bestie + if(GLOB.running_create_and_destroy) + var/turf/T = loc + T.RemoveElement(/datum/element/decal, icon, icon_state, dir, FALSE, color, null, null, alpha, FALSE) + if(detail_overlay) + T.RemoveElement(/datum/element/decal, icon, detail_overlay, dir, FALSE, detail_color, null, null, alpha, appearance_flags) + + // Intentionally used over moveToNullspace(), which calls doMove(), which fires + // off an enormous amount of procs, signals, etc, that this temporary effect object + // never needs or affects. loc = null return QDEL_HINT_QUEUE diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 84f4ffa1f0a1..243112d84c68 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -856,7 +856,6 @@ . = ..() var/mutable_appearance/arm = mutable_appearance(icon = icon, icon_state = "borg_beaker_apparatus_arm") if(stored) - COMPILE_OVERLAYS(stored) stored.pixel_x = 0 stored.pixel_y = 0 var/mutable_appearance/stored_copy = new /mutable_appearance(stored) @@ -913,7 +912,6 @@ . = ..() var/mutable_appearance/arm = mutable_appearance(icon, "borg_hardware_apparatus_arm1") if(stored) - COMPILE_OVERLAYS(stored) stored.pixel_x = -3 stored.pixel_y = 0 if(!istype(stored, /obj/item/circuitboard)) diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 8f1b69bc831d..19b74e54b60d 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -307,7 +307,6 @@ GLOBAL_VAR_INIT(say_disabled, FALSE) qdel(I) randomize_human(D) JB.equip(D, TRUE, FALSE) - COMPILE_OVERLAYS(D) var/icon/I = icon(getFlatIcon(D), frame = 1) final.Insert(I, JB.name) qdel(D) diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index d4b9259a61e9..ddacd0c88dfa 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -219,7 +219,6 @@ equipAntagOnDummy(mannequin, ert) - COMPILE_OVERLAYS(mannequin) CHECK_TICK var/icon/preview_icon = icon('icons/effects/effects.dmi', "nothing") preview_icon.Scale(48+32, 16+32) diff --git a/code/modules/autowiki/pages/ships.dm b/code/modules/autowiki/pages/ships.dm index fe07ac402858..854117eb3c1e 100644 --- a/code/modules/autowiki/pages/ships.dm +++ b/code/modules/autowiki/pages/ships.dm @@ -79,7 +79,6 @@ wiki_dummy.wipe_state() to_equip.equip(wiki_dummy, TRUE, FALSE) - COMPILE_OVERLAYS(wiki_dummy) var/icon/wiki_icon = icon(getFlatIcon(wiki_dummy), frame = 1) //Make all icons 32x32 for wiki sizing consistency diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 4fb7ceac4527..51aa0dbfc41d 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -175,6 +175,8 @@ var/facial_hairstyle var/skin_tone + var/list/outfit_override + /obj/effect/mob_spawn/human/Initialize() if(ispath(outfit)) outfit = new outfit() diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index 71b66ac54fb4..86a2f792a839 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -64,6 +64,5 @@ if(selected_outfit && show_gear) selected_outfit.equip(mannequin, TRUE, preference_source = parent) - COMPILE_OVERLAYS(mannequin) parent.show_character_previews(new /mutable_appearance(mannequin)) unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES) diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index f189084e6dc1..e76be5bcf59a 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -123,6 +123,9 @@ hairstyle = "Bald" facial_hairstyle = "Shaved" +/obj/effect/mob_spawn/human/corpse/frontier/internals + outfit = /datum/outfit/frontier/internals + /datum/outfit/frontier name = "Frontiersman Corpse" uniform = /obj/item/clothing/under/frontiersmen @@ -131,19 +134,38 @@ gloves = /obj/item/clothing/gloves/color/black neck = /obj/item/clothing/neck/dogtag/frontier +/datum/outfit/frontier/internals + name = "Frontiersman Corpse Internals" + mask = /obj/item/clothing/mask/gas/sechailer + l_pocket = /obj/item/tank/internals/emergency_oxygen/engi + /obj/effect/mob_spawn/human/corpse/frontier/ranged outfit = /datum/outfit/frontier +/obj/effect/mob_spawn/human/corpse/frontier/ranged/internals + outfit = /datum/outfit/frontier/internals + /obj/effect/mob_spawn/human/corpse/frontier/surgeon outfit = /datum/outfit/job/frontiersmen/doctor/corpse +/obj/effect/mob_spawn/human/corpse/frontier/surgeon/internals + outfit = /datum/outfit/job/frontiersmen/doctor/corpse/internals + /datum/outfit/job/frontiersmen/doctor/corpse name = "Frontiersmen Surgeon Corpse" r_pocket = null +/datum/outfit/job/frontiersmen/doctor/corpse/internals + name = "Frontiersmen Surgeon Corpse Internals" + mask = /obj/item/clothing/mask/breath + l_pocket = /obj/item/tank/internals/emergency_oxygen/engi + /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper outfit = /datum/outfit/frontier/trooper +/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/internals + outfit = /datum/outfit/frontier/trooper/internals + /datum/outfit/frontier/trooper name = "Frontiersman Armored Corpse" suit = /obj/item/clothing/suit/armor/vest/frontier @@ -152,6 +174,10 @@ ears = /obj/item/radio/headset head = /obj/item/clothing/head/helmet/bulletproof/x11/frontier +/datum/outfit/frontier/trooper/internals + name = "Frontiersman Armored Corpse Internals" + mask = /obj/item/clothing/mask/gas/sechailer + l_pocket = /obj/item/tank/internals/emergency_oxygen/engi /obj/effect/mob_spawn/human/corpse/frontier/ranged/officer name = "Frontiersman Officer" @@ -165,6 +191,15 @@ ears = /obj/item/radio/headset head = /obj/item/clothing/head/frontier/peaked +/obj/effect/mob_spawn/human/corpse/frontier/ranged/officer/internals + name = "Frontiersman Officer Corpse Internals" + outfit = /datum/outfit/frontier/officer/internals + +/datum/outfit/frontier/officer/internals + name = "Frontiersman Officer Corpse Internals" + mask = /obj/item/clothing/mask/gas/sechailer + l_pocket = /obj/item/tank/internals/emergency_oxygen/engi + /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy outfit = /datum/outfit/frontier/trooper/heavy @@ -174,6 +209,14 @@ head = /obj/item/clothing/head/beret/sec/frontier/officer back = /obj/item/minigunpack +/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy/internals + outfit = /datum/outfit/frontier/trooper/heavy/internals + +/datum/outfit/frontier/trooper/heavy/internals + name = "Frontiersman Heavy Corpse Internals" + mask = /obj/item/clothing/mask/gas + l_pocket = /obj/item/tank/internals/emergency_oxygen/engi + /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy/gunless outfit = /datum/outfit/frontier/trooper/heavy/gunless diff --git a/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm b/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm index 3257e6440b9e..0cfb1c429a3b 100644 --- a/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm +++ b/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm @@ -3,66 +3,54 @@ desc = "A member of the brutal Frontiersman terrorist fleet! This one clutches a wicked-looking knife in nimble fingers, eager to relieve you of your innards." icon_state = "frontiersmanmelee" icon = 'icons/mob/simple_frontiersman.dmi' - icon_living = "frontiersmanmelee" - icon_dead = "frontiersmanmelee_dead" speak_chance = 0 melee_damage_lower = 15 melee_damage_upper = 15 - loot = list(/obj/effect/mob_spawn/human/corpse/frontier, - /obj/item/melee/knife/survival) + loot = list() + atmos_requirements = NORMAL_ATMOS_REQS faction = list(FACTION_ANTAG_FRONTIERSMEN) footstep_type = FOOTSTEP_MOB_SHOE + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier + r_hand = /obj/item/melee/knife/survival /mob/living/simple_animal/hostile/human/frontier/internals icon_state = "frontiersmanmelee_mask" - icon_living = "frontiersmanmelee_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 - + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/internals /mob/living/simple_animal/hostile/human/frontier/ranged name = "Frontiersman Quickdraw" desc = "A member of the brutal Frontiersman terrorist fleet! This one thumbs a slender revolver, stained chrome and a malicious smile glinting in the light." icon_state = "frontiersmanranged" - icon_living = "frontiersmanranged" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/gun/ballistic/revolver/shadow) ranged = 1 retreat_distance = 5 minimum_distance = 5 projectilesound = 'sound/weapons/gun/revolver/cattleman.ogg' casingtype = /obj/item/ammo_casing/a44roum + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged + r_hand = /obj/item/gun/ballistic/revolver/shadow /mob/living/simple_animal/hostile/human/frontier/ranged/internals icon_state = "frontiersmanranged_mask" - icon_living = "frontiersmanranged_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/gun/ballistic/revolver/shadow, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/internals /mob/living/simple_animal/hostile/human/frontier/ranged/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/surgeon name = "Frontiersman Sawbones" desc = "A member of the brutal Frontiersman terrorist fleet! They appear to be a \"doctor\" of some sort, nervously swinging about some kind of makeshift syringe launcher." icon_state = "frontiersmansurgeon" icon_living = "frontiersmansurgeon" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/surgeon, - /obj/item/melee/knife/survival, - /obj/item/gun/syringe) - + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/surgeon + l_hand = /obj/item/melee/knife/survival + r_hand = /obj/item/gun/syringe minimum_distance = 1 retreat_distance = null projectiletype = /obj/projectile/bullet/dart/tranq @@ -73,89 +61,68 @@ armor_base = /obj/item/clothing/suit/frontiersmen /mob/living/simple_animal/hostile/human/frontier/ranged/surgeon/neuter - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/surgeon) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/surgeon/internals icon_state = "frontiersmansurgeon_mask" icon_living = "frontiersmansurgeon_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/surgeon, - /obj/item/melee/knife/survival, - /obj/item/gun/syringe, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/surgeon/internals /mob/living/simple_animal/hostile/human/frontier/ranged/surgeon/internals/neuter - icon_state = "frontiersmansurgeon_mask" - icon_living = "frontiersmansurgeon_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/surgeon, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/mosin name = "Frontiersman Sharpshot" desc = "A member of the brutal Frontiersman terrorist fleet! This one confidently mills about with a long rifle slung over their shoulder." icon_state = "frontiersmanrangedrifle" - icon_living = "frontiersmanrangedrifle" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/gun/ballistic/rifle/illestren) casingtype = /obj/item/ammo_casing/a8_50r projectilesound = 'sound/weapons/gun/rifle/mosin.ogg' + r_hand = /obj/item/gun/ballistic/rifle/illestren /mob/living/simple_animal/hostile/human/frontier/ranged/mosin/internals icon_state = "frontiersmanrangedrifle_mask" - icon_living = "frontiersmanrangedrifle_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/gun/ballistic/rifle/illestren, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/internals /mob/living/simple_animal/hostile/human/frontier/ranged/mosin/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/clothing/mask/breath, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/mosin/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper name = "Frontiersman Doorkicker" desc = "A member of the brutal Frontiersman terrorist fleet! Bedecked in military-grade armor, they swagger their shotgun about with a boldness uncommon even among other Frontiersmen." icon_state = "frontiersmanrangedelite" - icon_living = "frontiersmanrangedelite" + maxHealth = 170 + health = 170 projectilesound = 'sound/weapons/gun/shotgun/shot.ogg' casingtype = /obj/item/ammo_casing/shotgun/buckshot - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/gun/ballistic/shotgun/brimstone) + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper + r_hand = /obj/item/gun/ballistic/shotgun/brimstone armor_base = /obj/item/clothing/suit/armor/vest/frontier /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/internals icon_state = "frontiersmanrangedelite_mask" - icon_living = "frontiersmanrangedelite_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/gun/ballistic/shotgun/brimstone, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/internals atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/flame name = "Frontiersman Scorcher" desc = "An ashen revenant wades through a sea of flames, mummified under twenty pounds of blackened asbestos fabric. Mirrored lenses glare inscrutably as they swing their instrument of destruction towards you. You should probably run." icon_state = "frontiersmanflametrooper" icon_living = "frontiersmanflametrooper" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/flame, - /obj/item/flamethrower) - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/flame + r_hand = /obj/item/flamethrower + atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 maxbodytemp = 1000 @@ -176,126 +143,95 @@ ranged_cooldown = world.time + ranged_cooldown_time /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/flame/neuter - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/flame) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/skm name = "Frontiersman Gunner" desc = "A member of the brutal Frontiersman terrorist fleet! This one could almost be mistaken for a real soldier by their assault rifle and armor, if it weren't for their swaggering demeanor." icon_state = "frontiersmanrangedak47" - icon_living = "frontiersmanrangedak47" projectilesound = 'sound/weapons/gun/rifle/skm.ogg' rapid = 4 rapid_fire_delay = 3 casingtype = /obj/item/ammo_casing/a762_40 - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/gun/ballistic/automatic/assault/skm) - armor_base = /obj/item/clothing/suit/armor/vest/frontier + r_hand = /obj/item/gun/ballistic/automatic/assault/skm /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/skm/internals icon_state = "frontiersmanrangedak47_mask" - icon_living = "frontiersmanrangedak47_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/gun/ballistic/automatic/assault/skm, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/internals + r_hand = /obj/item/gun/ballistic/automatic/assault/skm /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/skm/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/skm/neutured - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/rifle name = "Frontiersman Crackshot" desc = "A member of the brutal Frontiersman terrorist fleet! Compared to their allies, they stand a little straighter, laugh a little colder. Their long rifle has a regular series of scratches on the receiver." icon_state = "frontiersmanrangedmosin" - icon_living = "frontiersmanrangedmosin" - - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/gun/ballistic/rifle/illestren) casingtype = /obj/item/ammo_casing/a8_50r projectilesound = 'sound/weapons/gun/rifle/mosin.ogg' - armor_base = /obj/item/clothing/suit/armor/vest/frontier + r_hand = /obj/item/gun/ballistic/rifle/illestren /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/rifle/internals icon_state = "frontiersmanrangedmosin_mask" - icon_living = "frontiersmanrangedmosin_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/gun/ballistic/rifle/illestren, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/internals /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/rifle/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/rifle/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/heavy name = "Frontiersman Sweeper" desc = "Unseen behind the mirror-visor of this heavily plated hardsuit, flinty eyes dream of cruelty. A mass of chrome and photonics swings your way, the soft whine of its motor a harbinger of iridescent death." icon_state = "frontiersmanrangedminigun" - icon_living = "frontiersmanrangedminigun" projectilesound = 'sound/weapons/laser4.ogg' rapid = 6 rapid_fire_delay = 1.5 casingtype = null projectiletype = /obj/projectile/beam/weak/penetrator - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy) + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy armor_base = /obj/item/clothing/suit/space/hardsuit/security/independent/frontier /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/heavy/internals icon_state = "frontiersmanrangedminigun_mask" - icon_living = "frontiersmanrangedminigun_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 - + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy/internals /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/heavy/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy/gunless, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/trooper/heavy/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/trooper/heavy/gunless) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/officer name = "Frontiersman Boss" desc = "This Frontiersman moves with what could almost pass for discipline among the infamously ragtag terrorists. They leer at their underlings, one hand resting consciously over the machine pistol at their hip." icon_state = "frontiersmanofficer" - icon_living = "frontiersmanofficer" + maxHealth = 65 + health = 65 rapid = 4 projectilesound = 'sound/weapons/gun/pistol/mauler.ogg' casingtype = /obj/item/ammo_casing/c9mm - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/officer, - /obj/item/gun/ballistic/automatic/pistol/mauler) - armor_base = /obj/item/clothing/suit/armor/frontier + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/officer + r_hand = /obj/item/gun/ballistic/automatic/pistol/mauler /mob/living/simple_animal/hostile/human/frontier/ranged/officer/internals icon_state = "frontiersmanofficer_mask" - icon_living = "frontiersmanofficer_mask" - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/officer, - /obj/item/gun/ballistic/automatic/pistol/mauler, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) atmos_requirements = IMMUNE_ATMOS_REQS minbodytemp = 0 + mob_spawner = /obj/effect/mob_spawn/human/corpse/frontier/ranged/officer/internals /mob/living/simple_animal/hostile/human/frontier/ranged/officer/internals/neutered - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/officer, - /obj/item/clothing/mask/gas, - /obj/item/tank/internals/emergency_oxygen/engi) + neutered = TRUE /mob/living/simple_animal/hostile/human/frontier/ranged/officer/neutured - loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged/officer) + neutered = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/human/human.dm b/code/modules/mob/living/simple_animal/hostile/human/human.dm index 1efe7c02b261..e6a06df89c0b 100644 --- a/code/modules/mob/living/simple_animal/hostile/human/human.dm +++ b/code/modules/mob/living/simple_animal/hostile/human/human.dm @@ -33,23 +33,46 @@ unsuitable_atmos_damage = 15 minbodytemp = 180 status_flags = CANPUSH - del_on_death = TRUE footstep_type = FOOTSTEP_MOB_SHOE faction = list("hermit") + /// If we use stuff from dynamic human icon generation for loot + var/human_loot = TRUE + /// Path of the mob spawner we base the mob's visuals off of. + var/mob_spawner + /// Path of the right hand held item we give to the mob's visuals. + var/r_hand + /// Path of the left hand held item we give to the mob's visuals. + var/l_hand + // If we drop l and r hand loot + var/neutered = FALSE + ///Steals the armor datum from this type of armor var/obj/item/clothing/armor_base -/mob/living/simple_animal/hostile/human/Initialize() +/mob/living/simple_animal/hostile/human/Initialize(mapload) . = ..() + if(mob_spawner) + apply_dynamic_human_appearance(src, mob_spawn_path = mob_spawner, r_hand = r_hand, l_hand = l_hand) if(ispath(armor_base, /obj/item/clothing)) //sigh. if only we could get the initial() value of list vars var/obj/item/clothing/instance = new armor_base() armor = instance.armor qdel(instance) +/mob/living/simple_animal/hostile/human/drop_loot() + . = ..() + if(!human_loot) + return + if(mob_spawner) + new mob_spawner(loc) + if(r_hand && !neutered) + new r_hand(loc) + if(l_hand && !neutered) + new r_hand(loc) + /mob/living/simple_animal/hostile/human/vv_edit_var(var_name, var_value) switch(var_name) if (NAMEOF(src, armor_base)) diff --git a/code/modules/mob/living/simple_animal/hostile/human/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/human/syndicate.dm index 7e0f4a8b9c2c..05b9ea0a50ca 100644 --- a/code/modules/mob/living/simple_animal/hostile/human/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/human/syndicate.dm @@ -13,7 +13,7 @@ speak_chance = 0 stat_attack = HARD_CRIT loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/clothing/neck/dogtag/ramzi) - atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) + atmos_requirements = NORMAL_ATMOS_REQS maxbodytemp = 400 unsuitable_atmos_damage = 15 faction = list(FACTION_ANTAG_SYNDICATE) diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 0e1d850a696f..55d2a99bfdbc 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -110,6 +110,7 @@ var/original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs var/original_baseturf_count = length(original_baseturfs) + GLOB.running_create_and_destroy = TRUE for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please if(ispath(type_path, /turf)) spawn_at.ChangeTurf(type_path) @@ -212,6 +213,7 @@ if(fails & BAD_INIT_SLEPT) TEST_FAIL("[path] slept during Initialize()") + GLOB.running_create_and_destroy = FALSE SSticker.delay_end = FALSE //This shouldn't be needed, but let's be polite SSgarbage.collection_timeout[GC_QUEUE_CHECK] = GC_CHECK_QUEUE diff --git a/shiptest.dme b/shiptest.dme index 103c22c910c8..c9bd6c943a39 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -106,6 +106,7 @@ #include "code\__DEFINES\nanites.dm" #include "code\__DEFINES\networks.dm" #include "code\__DEFINES\obj_flags.dm" +#include "code\__DEFINES\overlays.dm" #include "code\__DEFINES\overmap.dm" #include "code\__DEFINES\paper.dm" #include "code\__DEFINES\particles.dm" @@ -195,6 +196,7 @@ #include "code\__HELPERS\dates.dm" #include "code\__HELPERS\datums.dm" #include "code\__HELPERS\dna.dm" +#include "code\__HELPERS\dynamic_human_icon_gen.dm" #include "code\__HELPERS\files.dm" #include "code\__HELPERS\filters.dm" #include "code\__HELPERS\game.dm"