diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index bf8357d083..da8ee77372 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -53,6 +53,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G // Subsystem init_order, from highest priority to lowest priority // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. +#define INIT_ORDER_MISC_EARLY 60 #define INIT_ORDER_WEBHOOKS 50 #define INIT_ORDER_SQLITE 40 #define INIT_ORDER_INPUT 37 diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index d264c05f4e..61857daf5f 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -253,29 +253,50 @@ /proc/key_name_admin(var/whom, var/include_name = 1) return key_name(whom, 1, include_name) -// Helper procs for building detailed log lines -/datum/proc/log_info_line() - return "[src] ([type])" - -/atom/log_info_line() - var/turf/t = get_turf(src) - if(istype(t)) - return "([t]) ([t.x],[t.y],[t.z]) ([t.type])" - else if(loc) - return "([loc]) (0,0,0) ([loc.type])" - else - return "(NULL) (0,0,0) (NULL)" - -/mob/log_info_line() - return "[..()] ([ckey])" - -/proc/log_info_line(var/datum/d) - if(!istype(d)) - return - return d.log_info_line() /mob/proc/simple_info_line() return "[key_name(src)] ([x],[y],[z])" + /client/proc/simple_info_line() return "[key_name(src)] ([mob.x],[mob.y],[mob.z])" + + +/proc/log_info_line(datum/thing) + if (isnull(thing)) + return "*null*" + if (islist(thing)) + var/list/result = list() + var/list/thing_list = thing + for (var/key in thing_list) + var/value = isnum(key) ? null : thing[key] + result += "[log_info_line(key)][value ? " - [log_info_line(value)]" : ""]" + return "\[[jointext(result, ", ")]\]" + if (!istype(thing)) + return json_encode(thing) + return thing.get_log_info_line() + + +/datum/proc/get_log_info_line() + return "[src] ([type])" + + +/weakref/get_log_info_line() + return "[ref_name] ([ref_type]) ([ref]) (WEAKREF)" + + +/area/get_log_info_line() + return "[..()] ([isnum(z) ? "[x],[y],[z]" : "0,0,0"])" + + +/turf/get_log_info_line() + return "[..()] ([x],[y],[z]) ([loc ? loc.type : "NULL"])" + + +/atom/movable/get_log_info_line() + var/turf/turf = get_turf(src) + return "[..()] ([turf ? turf : "NULL"]) ([turf ? "[turf.x],[turf.y],[turf.z]" : "0,0,0"]) ([turf ? turf.type : "NULL"])" + + +/mob/get_log_info_line() + return ckey ? "[..()] ([ckey])" : ..() diff --git a/code/controllers/subsystems/antag.dm b/code/controllers/subsystems/antag.dm index 7431955136..a2d0a62353 100644 --- a/code/controllers/subsystems/antag.dm +++ b/code/controllers/subsystems/antag.dm @@ -27,11 +27,6 @@ SUBSYSTEM_DEF(antags) var/datum/antagonist/A = new antag_type antag_datums[A.id] = A -/datum/controller/subsystem/antags/Shutdown() - for(var/thing in antag_datums) - qdel(thing) - . = ..() - /datum/controller/subsystem/antags/proc/get_antag_id_from_name(var/role_text) for(var/datum/antagonist/A as anything in antag_datums) diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm index 7a4baaf5ed..3516470a23 100644 --- a/code/controllers/subsystems/atoms.dm +++ b/code/controllers/subsystems/atoms.dm @@ -1,159 +1,119 @@ -#define BAD_INIT_QDEL_BEFORE 1 -#define BAD_INIT_DIDNT_INIT 2 -#define BAD_INIT_SLEPT 4 -#define BAD_INIT_NO_HINT 8 - SUBSYSTEM_DEF(atoms) name = "Atoms" init_order = INIT_ORDER_ATOMS flags = SS_NO_FIRE - // override and GetArguments() exists for mod-override/downstream hook functionality. - // Useful for total-overhaul type modifications. - var/adjust_init_arguments = FALSE + // Bad initialization types. + var/const/QDEL_BEFORE_INITIALIZE = 1 + var/const/DID_NOT_SET_INITIALIZED = 2 + var/const/SLEPT_IN_INITIALIZE = 4 + var/const/DID_NOT_RETURN_HINT = 8 - var/atom_init_stage = INITIALIZATION_INSSATOMS - var/old_init_stage + var/static/atom_init_stage = INITIALIZATION_INSSATOMS + var/static/old_init_stage + var/static/list/late_loaders = list() + var/static/list/created_atoms = list() + var/static/list/bad_init_calls = list() - var/list/late_loaders - var/list/BadInitializeCalls = list() -/datum/controller/subsystem/atoms/Initialize(timeofday) - if(!plant_controller) // Initialize seed repo for /obj/item/seed and /obj/item/grown - plant_controller = new - setupgenetics() //to set the mutations' place in structural enzymes, so initializers know where to put mutations. +/datum/controller/subsystem/atoms/Initialize(start_uptime) atom_init_stage = INITIALIZATION_INNEW_MAPLOAD InitializeAtoms() -/datum/controller/subsystem/atoms/proc/InitializeAtoms(var/list/supplied_atoms) - - if(atom_init_stage <= INITIALIZATION_INSSATOMS_LATE) - return - - atom_init_stage = INITIALIZATION_INNEW_MAPLOAD - - LAZYINITLIST(late_loaders) - - var/list/mapload_arg = list(TRUE) - var/count = LAZYLEN(supplied_atoms) - if(count) - while(supplied_atoms.len) - var/atom/A = supplied_atoms[supplied_atoms.len] - supplied_atoms.len-- - if(!A.initialized) - InitAtom(A, GetArguments(A, mapload_arg)) - CHECK_TICK - else if(!subsystem_initialized) - // If wondering why not just store all atoms in a list and use the block above: that turns out unbearably expensive. - // Instead, atoms without extra arguments in New created on server start are fished out of world directly. - // We do this exactly once. - - for(var/atom/A in world) - if(!A.initialized) - InitAtom(A, GetArguments(A, mapload_arg, FALSE)) - ++count - CHECK_TICK - - report_progress("Initialized [count] atom\s") - - atom_init_stage = INITIALIZATION_INNEW_REGULAR - - if(late_loaders.len) - for(var/I in late_loaders) - var/atom/A = I - A.LateInitialize(arglist(late_loaders[A])) - report_progress("Late initialized [late_loaders.len] atom\s") - late_loaders.Cut() - -/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments) - LAZYREMOVE(global.pre_init_created_atoms, A) - var/the_type = A.type - if(QDELING(A)) - BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE - return TRUE - - var/start_tick = world.time - - var/result = A.Initialize(arglist(arguments)) - - if(start_tick != world.time) - BadInitializeCalls[the_type] |= BAD_INIT_SLEPT - - var/qdeleted = FALSE - - if(result != INITIALIZE_HINT_NORMAL) - switch(result) - if(INITIALIZE_HINT_LATELOAD) - if(arguments[1]) //mapload - late_loaders[A] = arguments - else - A.LateInitialize(arglist(arguments)) - if(INITIALIZE_HINT_QDEL) - qdel(A) - qdeleted = TRUE - else - BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT - - if(!A) //possible harddel - qdeleted = TRUE - else if(!A.initialized) - BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT - - return qdeleted || QDELING(A) - -// override and GetArguments() exists for mod-override/downstream hook functionality. -// Useful for total-overhaul type modifications. -/atom/proc/AdjustInitializeArguments(list/arguments) - // Lists are passed by reference so can simply modify the arguments list without returning it - -/datum/controller/subsystem/atoms/proc/GetArguments(atom/A, list/mapload_arg, created=TRUE) - if(!created && !adjust_init_arguments) - return mapload_arg // Performance optimization. Nothing to do. - var/list/arguments = mapload_arg.Copy() - var/extra_args = LAZYACCESS(global.pre_init_created_atoms, A) - if(created && extra_args) - arguments += extra_args - if(adjust_init_arguments) - A.AdjustInitializeArguments(arguments) - return arguments - -/datum/controller/subsystem/atoms/stat_entry(msg) - ..("Bad Initialize Calls:[BadInitializeCalls.len]") - -/datum/controller/subsystem/atoms/proc/map_loader_begin() - old_init_stage = atom_init_stage - atom_init_stage = INITIALIZATION_INSSATOMS_LATE - -/datum/controller/subsystem/atoms/proc/map_loader_stop() - atom_init_stage = old_init_stage /datum/controller/subsystem/atoms/Recover() - atom_init_stage = SSatoms.atom_init_stage - if(atom_init_stage == INITIALIZATION_INNEW_MAPLOAD) + created_atoms.Cut() + late_loaders.Cut() + if (atom_init_stage == INITIALIZATION_INNEW_MAPLOAD) InitializeAtoms() - old_init_stage = SSatoms.old_init_stage - BadInitializeCalls = SSatoms.BadInitializeCalls -/datum/controller/subsystem/atoms/proc/InitLog() - . = "" - for(var/path in BadInitializeCalls) - . += "Path : [path] \n" - var/fails = BadInitializeCalls[path] - if(fails & BAD_INIT_DIDNT_INIT) - . += "- Didn't call atom/Initialize()\n" - if(fails & BAD_INIT_NO_HINT) - . += "- Didn't return an Initialize hint\n" - if(fails & BAD_INIT_QDEL_BEFORE) - . += "- Qdel'd in New()\n" - if(fails & BAD_INIT_SLEPT) - . += "- Slept during Initialize()\n" /datum/controller/subsystem/atoms/Shutdown() var/initlog = InitLog() - if(initlog) - text2file(initlog, "[log_path]-initialize.log") + if (!initlog) + return + text2file(initlog, "[log_path]/initialize.log") -#undef BAD_INIT_QDEL_BEFORE -#undef BAD_INIT_DIDNT_INIT -#undef BAD_INIT_SLEPT -#undef BAD_INIT_NO_HINT + +/datum/controller/subsystem/atoms/proc/InitializeAtoms() + if (atom_init_stage <= INITIALIZATION_INSSATOMS_LATE) + return + atom_init_stage = INITIALIZATION_INNEW_MAPLOAD + var/list/mapload_arg = list(TRUE) + var/count = 0 + var/atom/created + var/list/arguments + for (var/i = 1 to length(created_atoms)) + created = created_atoms[i] + if (!created.initialized) + arguments = created_atoms[created] ? mapload_arg + created_atoms[created] : mapload_arg + InitAtom(created, arguments) + CHECK_TICK + created_atoms.Cut() + if (!subsystem_initialized) + for (var/atom/atom in world) + if (!atom.initialized) + InitAtom(atom, mapload_arg) + ++count + CHECK_TICK + report_progress("Initialized [count] atom\s") + atom_init_stage = INITIALIZATION_INNEW_REGULAR + if (!length(late_loaders)) + return + for (var/atom/atom as anything in late_loaders) + atom.LateInitialize(arglist(late_loaders[atom])) + report_progress("Late initialized [length(late_loaders)] atom\s") + late_loaders.Cut() + + +/datum/controller/subsystem/atoms/proc/InitAtom(atom/atom, list/arguments) + var/atom_type = atom?.type + if (QDELING(atom)) + bad_init_calls[atom_type] |= QDEL_BEFORE_INITIALIZE + return TRUE + var/start_tick = world.time + var/result = atom.Initialize(arglist(arguments)) + if (start_tick != world.time) + bad_init_calls[atom_type] |= SLEPT_IN_INITIALIZE + var/qdeleted = FALSE + if (result != INITIALIZE_HINT_NORMAL) + switch (result) + if (INITIALIZE_HINT_LATELOAD) + if (arguments[1]) //mapload + late_loaders[atom] = arguments + else + atom.LateInitialize(arglist(arguments)) + if (INITIALIZE_HINT_QDEL) + qdel(atom) + qdeleted = TRUE + else + bad_init_calls[atom_type] |= DID_NOT_RETURN_HINT + if (!atom) + qdeleted = TRUE + else if (!atom.initialized) + bad_init_calls[atom_type] |= DID_NOT_SET_INITIALIZED + return qdeleted || QDELING(atom) + + +/datum/controller/subsystem/atoms/proc/BeginMapLoad() + old_init_stage = atom_init_stage + atom_init_stage = INITIALIZATION_INSSATOMS_LATE + + +/datum/controller/subsystem/atoms/proc/FinishMapLoad() + atom_init_stage = old_init_stage + + +/datum/controller/subsystem/atoms/proc/InitLog() + . = "" + for (var/path in bad_init_calls) + . += "Path : [path] \n" + var/fails = bad_init_calls[path] + if (fails & DID_NOT_SET_INITIALIZED) + . += "- Didn't call atom/Initialize()\n" + if (fails & DID_NOT_RETURN_HINT) + . += "- Didn't return an Initialize hint\n" + if (fails & QDEL_BEFORE_INITIALIZE) + . += "- Qdel'd in New()\n" + if (fails & SLEPT_IN_INITIALIZE) + . += "- Slept during Initialize()\n" diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 273b1c99a3..86cd9caef1 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -273,71 +273,73 @@ SUBSYSTEM_DEF(garbage) // Should be treated as a replacement for the 'del' keyword. // Datums passed to this will be given a chance to clean up references to allow the GC to collect them. -/proc/qdel(datum/D, force=FALSE) - if(!istype(D)) - del(D) +/proc/qdel(datum/thing, force) + if (!thing) return - var/datum/qdel_item/I = SSgarbage.items[D.type] - if (!I) - I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type) - I.qdels++ - - - if(isnull(D.gc_destroyed)) - if(SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted + if (!istype(thing)) + crash_with("qdel() can only handle /datum (sub)types, was passed: [log_info_line(thing)]") + del(thing) + return + var/datum/qdel_item/qdel_item = SSgarbage.items[thing.type] + if (!qdel_item) + qdel_item = new (thing.type) + SSgarbage.items[thing.type] = qdel_item + qdel_item.qdels++ + if (isnull(thing.gc_destroyed)) + if (SEND_SIGNAL(thing, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted return - D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED + thing.gc_destroyed = GC_CURRENTLY_BEING_QDELETED var/start_time = world.time var/start_tick = world.tick_usage - SEND_SIGNAL(D, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy - var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up. - if(world.time != start_time) - I.slept_destroy++ + SEND_SIGNAL(thing, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy + var/hint = thing.Destroy(force) // Let our friend know they're about to get fucked up. + if (world.time != start_time) + qdel_item.slept_destroy++ else - I.destroy_time += TICK_USAGE_TO_MS(start_tick) - if(!D) + qdel_item.destroy_time += TICK_USAGE_TO_MS(start_tick) + if (!thing) return - switch(hint) + switch (hint) if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. - SSgarbage.PreQueue(D) + SSgarbage.PreQueue(thing) if (QDEL_HINT_IWILLGC) - D.gc_destroyed = world.time + thing.gc_destroyed = world.time return if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory. if(!force) - D.gc_destroyed = null //clear the gc variable (important!) + thing.gc_destroyed = null //clear the gc variable (important!) return // Returning LETMELIVE after being told to force destroy // indicates the objects Destroy() does not respect force - #ifdef TESTING - if(!I.no_respect_force) - crash_with("[D.type] has been force deleted, but is \ +#ifdef TESTING + if(!qdel_item.no_respect_force) + crash_with("[thing.type] has been force deleted, but is \ returning an immortal QDEL_HINT, indicating it does \ not respect the force flag for qdel(). It has been \ placed in the queue, further instances of this type \ will also be queued.") - #endif - I.no_respect_force++ - - SSgarbage.PreQueue(D) +#endif + qdel_item.no_respect_force++ + SSgarbage.PreQueue(thing) if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() - SSgarbage.HardQueue(D) + SSgarbage.HardQueue(thing) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. - SSgarbage.HardDelete(D) + SSgarbage.HardDelete(thing) if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. - SSgarbage.PreQueue(D) + SSgarbage.PreQueue(thing) #ifdef TESTING - D.find_references() + thing.find_references() #endif else #ifdef TESTING - if(!I.no_hint) - crash_with("[D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") + if (!qdel_item.no_hint) + crash_with("[thing.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") #endif - I.no_hint++ - SSgarbage.PreQueue(D) - else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") + qdel_item.no_hint++ + SSgarbage.PreQueue(thing) + else if (thing.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + CRASH("[thing.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") + #ifdef TESTING diff --git a/code/controllers/subsystems/init_misc_early.dm b/code/controllers/subsystems/init_misc_early.dm new file mode 100644 index 0000000000..54633fe1a8 --- /dev/null +++ b/code/controllers/subsystems/init_misc_early.dm @@ -0,0 +1,9 @@ +SUBSYSTEM_DEF(init_misc_early) + name = "Early Misc Initialization" + init_order = INIT_ORDER_MISC_EARLY + flags = SS_NO_FIRE + + +/datum/controller/subsystem/init_misc_early/Initialize(timeofday) + plant_controller = new + setupgenetics() diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm index 6c17c18bca..3c7903c21d 100644 --- a/code/datums/weakref.dm +++ b/code/datums/weakref.dm @@ -1,26 +1,30 @@ -//obtain a weak reference to a datum -/proc/weakref(datum/D) - if(!istype(D)) - return - if(QDELETED(D)) - return - if(!D.weakref) - D.weakref = new/weakref(D) - return D.weakref - /weakref var/ref + var/ref_name + var/ref_type -/weakref/New(datum/D) - ref = "\ref[D]" /weakref/Destroy() - // A weakref datum should not be manually destroyed as it is a shared resource, - // rather it should be automatically collected by the BYOND GC when all references are gone. - return QDEL_HINT_LETMELIVE + SHOULD_CALL_PARENT(FALSE) + return QDEL_HINT_IWILLGC + + +/weakref/New(datum/thing) + ref = "\ref[thing]" + ref_name = "[thing]" + ref_type = thing.type + /weakref/proc/resolve() - var/datum/D = locate(ref) - if(D && D.weakref == src) - return D - return null \ No newline at end of file + var/datum/thing = locate(ref) + if (thing && thing.weakref == src) + return thing + return null + + +/proc/weakref(datum/thing) + if (!istype(thing) || QDELING(thing)) + return + if (!thing.weakref) + thing.weakref = new /weakref (thing) + return thing.weakref diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index 2cf2b4f1ed..233fc9cce4 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -133,15 +133,19 @@ var/stasis_level = 3 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) var/obj/item/reagent_containers/syringe/syringe -/obj/structure/closet/body_bag/cryobag/Initialize() - tank = new tank_type(null) //It's in nullspace to prevent ejection when the bag is opened. - ..() /obj/structure/closet/body_bag/cryobag/Destroy() QDEL_NULL(syringe) QDEL_NULL(tank) return ..() + +/obj/structure/closet/body_bag/cryobag/Initialize() + . = ..() + if (ispath(tank_type, /obj/item/tank)) + tank = new tank_type // no loc to prevent tank being dropped when opened + + /obj/structure/closet/body_bag/cryobag/attack_hand(mob/living/user) if(used) var/confirm = alert(user, "Are you sure you want to open \the [src]? \ diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index fe3b4ff9ba..066f2844cf 100644 --- a/code/game/objects/items/contraband.dm +++ b/code/game/objects/items/contraband.dm @@ -1,24 +1,22 @@ -//Let's get some REAL contraband stuff in here. Because come on, getting brigged for LIPSTICK is no fun. -// -// Includes drug powder. -// -//Illicit drugs~ /obj/item/storage/pill_bottle/happy name = "bottle of Happy pills" desc = "A recreational drug. When you want to see the rainbow. Probably not work-approved..." wrapper_color = COLOR_PINK starts_with = list(/obj/item/reagent_containers/pill/happy = 7) + /obj/item/storage/pill_bottle/zoom name = "bottle of Zoom pills" desc = "Probably illegal. Trade brain for speed." wrapper_color = COLOR_BLUE starts_with = list(/obj/item/reagent_containers/pill/zoom = 7) + /obj/item/reagent_containers/glass/beaker/vial/random flags = 0 var/list/random_reagent_list = list(list("water" = 15) = 1, list("cleaner" = 15) = 1) + /obj/item/reagent_containers/glass/beaker/vial/random/toxin random_reagent_list = list( list("mindbreaker" = 10, "bliss" = 20) = 3, @@ -26,26 +24,21 @@ list("impedrezene" = 15) = 2, list("zombiepowder" = 10) = 1) + /obj/item/reagent_containers/glass/beaker/vial/random/Initialize() . = ..() if(is_open_container()) flags ^= OPENCONTAINER - var/list/picked_reagents = pickweight(random_reagent_list) for(var/reagent in picked_reagents) reagents.add_reagent(reagent, picked_reagents[reagent]) - var/list/names = new for(var/datum/reagent/R in reagents.reagent_list) names += R.name - desc = "Contains [english_list(names)]." update_icon() -/*///////////////////////////////////// -// DRUG POWDER // -////////////////////////////////////// -*/ + /obj/item/reagent_containers/powder name = "powder" desc = "A powdered form of... something." @@ -57,46 +50,47 @@ w_class = ITEMSIZE_TINY volume = 50 + /// The name of the reagent with the most volume in this powder. + var/main_reagent_name + + +/obj/item/reagent_containers/powder/Initialize(mapload, datum/reagents/initial_reagents) + . = ..() + if (istype(initial_reagents)) + initial_reagents.trans_to_holder(reagents, volume) + else if (islist(initial_reagents)) + var/list/initial_reagents_list = initial_reagents + for (var/reagent_type in initial_reagents_list) + reagents.add_reagent(reagent_type, initial_reagents[reagent_type]) + if (!reagents.total_volume) + return INITIALIZE_HINT_QDEL + var/datum/reagent/main_reagent = reagents.get_master_reagent() + main_reagent_name = lowertext(main_reagent.name) + color = reagents.get_color() + + /obj/item/reagent_containers/powder/examine(mob/user) - if(reagents) - var/datum/reagent/R = reagents.get_master_reagent() - desc = "A powdered form of what appears to be [R.name]. There's about [reagents.total_volume] units here." - return ..() + . = ..() + if (isliving(user) && get_dist(user, src) > 2) + return + . += "It seems to be about [reagents.total_volume] units of [main_reagent_name]." -/obj/item/reagent_containers/powder/Initialize() - ..() - get_appearance() -/obj/item/reagent_containers/powder/proc/get_appearance() - /// Names and colors based on dominant reagent. - if (reagents.reagent_list.len > 0) - color = reagents.get_color() - var/datum/reagent/R = reagents.get_master_reagent() - var/new_name = lowertext(R) - name = "powdered [new_name]" - -/// Snorting. - -/obj/item/reagent_containers/powder/attackby(var/obj/item/W, var/mob/living/user) - - if(!ishuman(user)) /// You gotta be fleshy to snort the naughty drugs. +/obj/item/reagent_containers/powder/attackby(obj/item/item, mob/living/user) + if (!ishuman(user)) return ..() - - if(!istype(W, /obj/item/glass_extra/straw) && !istype(W, /obj/item/reagent_containers/rollingpaper)) + if (!istype(item, /obj/item/glass_extra/straw) && !istype(item, /obj/item/reagent_containers/rollingpaper)) return ..() - - user.visible_message("[user] snorts [src] with [W]!") + reagents.trans_to_mob(user, amount_per_transfer_from_this, CHEM_BLOOD) + var/used_up = !reagents.total_volume + user.visible_message( + SPAN_ITALIC("\The [user] snorts some [name] with \a [item]."), + SPAN_ITALIC("You snort [used_up ? "the last" : "some"] of the [main_reagent_name] with \the [item].") + ) playsound(loc, 'sound/effects/snort.ogg', 50, 1) - - if(reagents) - reagents.trans_to_mob(user, amount_per_transfer_from_this, CHEM_BLOOD) - - if(!reagents.total_volume) /// Did we use all of it? + if (used_up) qdel(src) -////// End powder. /////////// -////////////////////////////// -///// Drugs for loadout/////// /obj/item/storage/pill_bottle/bliss name = "unlabeled pill bottle" @@ -126,4 +120,4 @@ /obj/item/storage/pill_bottle/schnappi name = "unlabeled pill bottle" desc = "A pill bottle with its label suspiciously scratched out." - starts_with = list(/obj/item/reagent_containers/pill/unidentified/schnappi = 7) \ No newline at end of file + starts_with = list(/obj/item/reagent_containers/pill/unidentified/schnappi = 7) diff --git a/code/game/objects/random/_random.dm b/code/game/objects/random/_random.dm index 14ef5acd43..d6e073b620 100644 --- a/code/game/objects/random/_random.dm +++ b/code/game/objects/random/_random.dm @@ -7,12 +7,14 @@ var/drop_get_turf = TRUE var/start_anomalous = FALSE -// creates a new object and deletes itself + /obj/random/Initialize() + . = INITIALIZE_HINT_QDEL ..() - if(!prob(spawn_nothing_percentage)) - try_spawn_item() - return INITIALIZE_HINT_QDEL + if (prob(spawn_nothing_percentage)) + return + try_spawn_item() + /obj/random/proc/try_spawn_item() var/atom/result = spawn_item() diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index f04ab4bc7e..6f9e32ffd0 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -404,13 +404,13 @@ // As this is only done at runtime, we have to create all the vending machines in existence and force them // to register their products when this asset initializes. /datum/asset/spritesheet/vending/proc/populate_vending_products() - SSatoms.map_loader_begin() + SSatoms.BeginMapLoad() for(var/path in subtypesof(/obj/machinery/vending)) var/obj/machinery/vending/x = new path(null) - // force an inventory build; with map_loader_begin active, init isn't called + // force an inventory build; with BeginMapLoad active, init isn't called x.build_inventory() qdel(x) - SSatoms.map_loader_stop() + SSatoms.FinishMapLoad() // /datum/asset/simple/genetics // assets = list( diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 06d802dcac..f09664765e 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -730,6 +730,24 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.h_style = new_h_style return TOPIC_REFRESH_UPDATE_PREVIEW + else if (href_list["hair_style_left"]) + var/list/valid_hairstyles = pref.get_valid_hairstyles() + var/index = valid_hairstyles.Find(href_list["hair_style_left"]) + if (!index || index == 1) + pref.h_style = valid_hairstyles[length(valid_hairstyles)] + else + pref.h_style = valid_hairstyles[index - 1] + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if (href_list["hair_style_right"]) + var/list/valid_hairstyles = pref.get_valid_hairstyles() + var/index = valid_hairstyles.Find(href_list["hair_style_right"]) + if (!index || index == length(valid_hairstyles)) + pref.h_style = valid_hairstyles[1] + else + pref.h_style = valid_hairstyles[index + 1] + return TOPIC_REFRESH_UPDATE_PREVIEW + else if(href_list["grad_style"]) var/list/valid_gradients = GLOB.hair_gradients @@ -738,26 +756,22 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.grad_style = new_grad_style return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["hair_style_left"]) - var/H = href_list["hair_style_left"] - var/list/valid_hairstyles = pref.get_valid_hairstyles() - var/start = valid_hairstyles.Find(H) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.h_style = valid_hairstyles[start-1] - else //But if we ARE, become the final element. - pref.h_style = valid_hairstyles[valid_hairstyles.len] + else if (href_list["grad_style_left"]) + var/list/valid_hair_gradients = GLOB.hair_gradients + var/index = valid_hair_gradients.Find(href_list["grad_style_left"]) + if (!index || index == 1) + pref.grad_style = valid_hair_gradients[length(valid_hair_gradients)] + else + pref.grad_style = valid_hair_gradients[index - 1] return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["hair_style_right"]) - var/H = href_list["hair_style_right"] - var/list/valid_hairstyles = pref.get_valid_hairstyles() - var/start = valid_hairstyles.Find(H) - - if(start != valid_hairstyles.len) //If we're not the end of the list, become the next element. - pref.h_style = valid_hairstyles[start+1] - else //But if we ARE, become the first element. - pref.h_style = valid_hairstyles[1] + else if (href_list["grad_style_right"]) + var/list/valid_hair_gradients = GLOB.hair_gradients + var/index = valid_hair_gradients.Find(href_list["grad_style_right"]) + if (!index || index == length(valid_hair_gradients)) + pref.grad_style = valid_hair_gradients[1] + else + pref.grad_style = valid_hair_gradients[index + 1] return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["facial_color"]) @@ -806,26 +820,22 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.f_style = new_f_style return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["facial_style_left"]) - var/F = href_list["facial_style_left"] + else if (href_list["facial_style_left"]) var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - var/start = valid_facialhairstyles.Find(F) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.f_style = valid_facialhairstyles[start-1] - else //But if we ARE, become the final element. - pref.f_style = valid_facialhairstyles[valid_facialhairstyles.len] + var/index = valid_facialhairstyles.Find(href_list["facial_style_left"]) + if (!index || index == 1) + pref.f_style = valid_facialhairstyles[length(valid_facialhairstyles)] + else + pref.f_style = valid_facialhairstyles[index - 1] return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["facial_style_right"]) - var/F = href_list["facial_style_right"] + else if (href_list["facial_style_right"]) var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - var/start = valid_facialhairstyles.Find(F) - - if(start != valid_facialhairstyles.len) //If we're not the end of the list, become the next element. - pref.f_style = valid_facialhairstyles[start+1] - else //But if we ARE, become the first element. + var/index = valid_facialhairstyles.Find(href_list["facial_style_right"]) + if (!index || index == length(valid_facialhairstyles)) pref.f_style = valid_facialhairstyles[1] + else + pref.f_style = valid_facialhairstyles[index + 1] return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["marking_style"]) diff --git a/code/modules/clothing/head/hood.dm b/code/modules/clothing/head/hood.dm index 7f847d3b47..f863142fe0 100644 --- a/code/modules/clothing/head/hood.dm +++ b/code/modules/clothing/head/hood.dm @@ -153,6 +153,36 @@ desc = "A starry winter hood." icon_state = "winterhood_cosmic" +/obj/item/clothing/head/hood/winter/parka_red + name = "red parka hood" + desc = "A red fur-lined hood." + icon_state = "redpark_hood" + +/obj/item/clothing/head/hood/winter/parka_green + name = "green parka hood" + desc = "A green fur-lined hood." + icon_state = "greenpark_hood" + +/obj/item/clothing/head/hood/winter/parka_blue + name = "blue parka hood" + desc = "A blue fur-lined hood." + icon_state = "bluepark_hood" + +/obj/item/clothing/head/hood/winter/parka_yellow + name = "yellow parka hood" + desc = "A yellow fur-lined hood." + icon_state = "yellowpark_hood" + +/obj/item/clothing/head/hood/winter/parka_purple + name = "purple parka hood" + desc = "A purple fur-lined hood." + icon_state = "purplepark_hood" + +/obj/item/clothing/head/hood/winter/parka_vintage + name = "vintage parka hood" + desc = "An old-fashioned fur-lined hood." + icon_state = "vintagepark_hood" + // Explorer gear /obj/item/clothing/head/hood/explorer name = "explorer hood" diff --git a/code/modules/clothing/suits/hooded.dm b/code/modules/clothing/suits/hooded.dm index 3605c08469..e71a4cf0c1 100644 --- a/code/modules/clothing/suits/hooded.dm +++ b/code/modules/clothing/suits/hooded.dm @@ -335,6 +335,45 @@ light_power = 1.8 light_range = 1.2 +/obj/item/clothing/suit/storage/hooded/wintercoat/parka + name = "red parka" + desc = "A heavy fur-lined jacket designed to keep you extra warm in sub-zero conditions." + icon_state = "redpark" + item_state_slots = list(slot_r_hand_str = "coatwinter", slot_l_hand_str = "coatwinter") + hoodtype = /obj/item/clothing/head/hood/winter/parka_red + +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/green + name = "green parka" + icon_state = "greenpark" + hoodtype = /obj/item/clothing/head/hood/winter/parka_green + +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/blue + name = "blue parka" + icon_state = "bluepark" + hoodtype = /obj/item/clothing/head/hood/winter/parka_blue + +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/yellow + name = "yellow parka" + icon_state = "yellowpark" + hoodtype = /obj/item/clothing/head/hood/winter/parka_yellow + +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/purple + name = "purple parka" + icon_state = "purplepark" + hoodtype = /obj/item/clothing/head/hood/winter/parka_purple + +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen + name = "corporate blue parka" + desc = "A NanoTrasen branded fur-lined jacket made to keep you nice and toasty on cold winter days. Or at least alive" + icon_state = "corppark" + hoodtype = /obj/item/clothing/head/hood/winter/parka_blue //No point having a unique hood when it's just blue anyway! + +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/vintage + name = "vintage parka" + desc = "An old-fashioned fur-lined jacket made to keep you nice and toasty on cold winter days. Or at least alive." + icon_state = "vintagepark" + hoodtype = /obj/item/clothing/head/hood/winter/parka_vintage + // winter coats end here /obj/item/clothing/suit/storage/hooded/explorer diff --git a/code/modules/maps/tg/reader.dm b/code/modules/maps/tg/reader.dm index cceff9c61d..3177de00f4 100644 --- a/code/modules/maps/tg/reader.dm +++ b/code/modules/maps/tg/reader.dm @@ -366,7 +366,7 @@ var/global/use_preloader = FALSE first_turf_index++ //turn off base new Initialization until the whole thing is loaded - SSatoms.map_loader_begin() + SSatoms.BeginMapLoad() //instanciate the first /turf var/turf/T if(members[first_turf_index] != /turf/template_noop) @@ -385,7 +385,7 @@ var/global/use_preloader = FALSE for(index in 1 to first_turf_index-1) instance_atom(members[index],members_attributes[index],crds,no_changeturf,orientation) //Restore initialization to the previous value - SSatoms.map_loader_stop() + SSatoms.FinishMapLoad() //////////////// //Helpers procs @@ -406,9 +406,9 @@ var/global/use_preloader = FALSE //custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize if(TICK_CHECK) - SSatoms.map_loader_stop() + SSatoms.FinishMapLoad() stoplag() - SSatoms.map_loader_begin() + SSatoms.BeginMapLoad() // Rotate the atom now that it exists, rather than changing its orientation beforehand through the fields["dir"] if(orientation != 0) // 0 means no rotation diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index aae07314d4..6f4cd20429 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -7,8 +7,7 @@ if (istype(loc, /turf/space)) return ..() - 1 - if(species.slowdown) - . += species.slowdown + . += species.get_slowdown(src) if(force_max_speed) return ..() + HUMAN_LOWEST_SLOWDOWN @@ -263,4 +262,4 @@ /mob/living/carbon/human/set_dir(var/new_dir) . = ..() if(. && species.tail) - update_tail_showing() \ No newline at end of file + update_tail_showing() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index e415895d3e..ca173606fb 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -82,7 +82,7 @@ handle_shock() handle_pain() - + handle_allergens() handle_medical_side_effects() @@ -167,11 +167,12 @@ var/pressure_difference // First get the absolute pressure difference. - if(pressure < species.safe_pressure) // We are in an underpressure. - pressure_difference = species.safe_pressure - pressure + var/species_safe_pressure = species.get_safe_pressure(src) + if(pressure < species_safe_pressure) // We are in an underpressure. + pressure_difference = species_safe_pressure - pressure else //We are in an overpressure or standard atmosphere. - pressure_difference = pressure - species.safe_pressure + pressure_difference = pressure - species_safe_pressure if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. pressure_difference = 0 @@ -184,10 +185,10 @@ // The difference is always positive to avoid extra calculations. // Apply the relative difference on a standard atmosphere to get the final result. // The return value will be the adjusted_pressure of the human that is the basis of pressure warnings and damage. - if(pressure < species.safe_pressure) - return species.safe_pressure - pressure_difference + if(pressure < species_safe_pressure) + return species_safe_pressure - pressure_difference else - return species.safe_pressure + pressure_difference + return species_safe_pressure + pressure_difference /mob/living/carbon/human/handle_disabilities() ..() @@ -565,7 +566,7 @@ else failed_last_breath = 0 adjustOxyLoss(-5) - + if(!does_not_breathe && client) // If we breathe, and have an active client, check if we have synthetic lungs. var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] var/turf = get_turf(src) @@ -588,7 +589,7 @@ to_chat(src, "You feel your face burning and a searing heat in your lungs!") if(breath.temperature >= species.heat_discomfort_level) - + if(breath.temperature >= species.breath_heat_level_3) apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Heat") throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) @@ -641,25 +642,25 @@ breath.update_values() return 1 - + /mob/living/carbon/human/proc/play_inhale(var/mob/living/M, var/exhale) var/suit_inhale_sound if(species.suit_inhale_sound) suit_inhale_sound = species.suit_inhale_sound else // Failsafe suit_inhale_sound = 'sound/effects/mob_effects/suit_breathe_in.ogg' - + playsound_local(get_turf(src), suit_inhale_sound, 100, pressure_affected = FALSE, volume_channel = VOLUME_CHANNEL_AMBIENCE) if(!exhale) // Did we fail exhale? If no, play it after inhale finishes. addtimer(CALLBACK(src, .proc/play_exhale, M), 5 SECONDS) - + /mob/living/carbon/human/proc/play_exhale(var/mob/living/M) var/suit_exhale_sound if(species.suit_exhale_sound) suit_exhale_sound = species.suit_exhale_sound else // Failsafe suit_exhale_sound = 'sound/effects/mob_effects/suit_breathe_out.ogg' - + playsound_local(get_turf(src), suit_exhale_sound, 100, pressure_affected = FALSE, volume_channel = VOLUME_CHANNEL_AMBIENCE) /mob/living/carbon/human/proc/handle_allergens() @@ -729,7 +730,7 @@ else loc_temp = environment.temperature - if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1) + if(adjusted_pressure < species.get_warning_high_pressure(src) && adjusted_pressure > species.get_warning_low_pressure(src) && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1) clear_alert("pressure") return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp @@ -798,15 +799,16 @@ if(status_flags & GODMODE) return 1 //godmode - if(adjusted_pressure >= species.hazard_high_pressure) - var/pressure_damage = min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) + var/species_hazard_high_pressure = species.get_hazard_high_pressure(src) + if(adjusted_pressure >= species_hazard_high_pressure) + var/pressure_damage = min( ( (adjusted_pressure / species_hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) take_overall_damage(brute=pressure_damage, used_weapon = "High Pressure") throw_alert("pressure", /obj/screen/alert/highpressure, 2) - else if(adjusted_pressure >= species.warning_high_pressure) + else if(adjusted_pressure >= species.get_warning_high_pressure(src)) throw_alert("pressure", /obj/screen/alert/highpressure, 1) - else if(adjusted_pressure >= species.warning_low_pressure) + else if(adjusted_pressure >= species.get_warning_low_pressure(src)) clear_alert("pressure") - else if(adjusted_pressure >= species.hazard_low_pressure) + else if(adjusted_pressure >= species.get_hazard_low_pressure(src)) throw_alert("pressure", /obj/screen/alert/lowpressure, 1) else if( !(COLD_RESISTANCE in mutations)) @@ -814,7 +816,7 @@ if(getOxyLoss() < 55) // 12 OxyLoss per 4 ticks when wearing internals; unconsciousness in 16 ticks, roughly half a minute var/pressure_dam = 3 // 16 OxyLoss per 4 ticks when no internals present; unconsciousness in 13 ticks, roughly twenty seconds // (Extra 1 oxyloss from failed breath) - // Being in higher pressure decreases the damage taken, down to a minimum of (species.hazard_low_pressure / ONE_ATMOSPHERE) at species.hazard_low_pressure + // Being in higher pressure decreases the damage taken, down to a minimum of (species.get_hazard_low_pressure(src) / ONE_ATMOSPHERE) at species.get_hazard_low_pressure(src) pressure_dam *= (ONE_ATMOSPHERE - adjusted_pressure) / ONE_ATMOSPHERE if(wear_suit && wear_suit.min_pressure_protection && head && head.min_pressure_protection) diff --git a/code/modules/mob/living/carbon/human/species/outsider/vox.dm b/code/modules/mob/living/carbon/human/species/outsider/vox.dm index 9166e3cd6a..c7e3f3ec53 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/vox.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/vox.dm @@ -14,6 +14,7 @@ /datum/unarmed_attack/claws/strong, /datum/unarmed_attack/bite/strong ) + rarity_value = 5 blurb = "The Vox are the broken remnants of a once-proud race, now reduced to little more than \ scavenging vermin who prey on isolated stations, ships or planets to keep their own ancient arkships \ @@ -40,9 +41,6 @@ male_sneeze_sound = 'sound/voice/shrieksneeze.ogg' female_sneeze_sound = 'sound/voice/shrieksneeze.ogg' - warning_low_pressure = 50 - hazard_low_pressure = 0 - cold_level_1 = 210 //Default 260 cold_level_2 = 150 //Default 200 cold_level_3 = 90 //Default 120 @@ -98,6 +96,8 @@ default_emotes = list( /decl/emote/audible/vox_shriek ) + inherent_verbs = list(/mob/living/carbon/human/proc/toggle_vox_pressure_seal) + var/list/current_pressure_toggle = list() /datum/species/vox/get_random_name(var/gender) var/datum/language/species_language = GLOB.all_languages[default_language] @@ -106,3 +106,57 @@ /datum/species/vox/equip_survival_gear(var/mob/living/carbon/human/H, var/extendedtank = 0,var/comprehensive = 0) . = ..() H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/vox(H), slot_wear_mask) + +/datum/species/vox/get_slowdown(var/mob/living/carbon/human/H) + if(current_pressure_toggle["\ref[H]"]) + return 1.5 + return ..() + +/datum/species/vox/get_warning_low_pressure(var/mob/living/carbon/human/H) + if(current_pressure_toggle["\ref[H]"]) + return 50 + return ..() + +/datum/species/vox/get_hazard_low_pressure(var/mob/living/carbon/human/H) + if(current_pressure_toggle["\ref[H]"]) + return 0 + return ..() + +/mob/living/carbon/human/proc/toggle_vox_pressure_seal() + set name = "Toggle Vox Pressure Seal" + set category = "Abilities" + set src = usr + + if(!istype(species, /datum/species/vox)) + verbs -= /mob/living/carbon/human/proc/toggle_vox_pressure_seal + return + + if(incapacitated(INCAPACITATION_KNOCKOUT)) + to_chat(src, SPAN_WARNING("You are in no state to do that.")) + return + + var/datum/gender/G = gender_datums[get_visible_gender()] + visible_message(SPAN_NOTICE("\The [src] begins flexing and realigning [G.his] scaling...")) + if(!do_after(src, 2 SECONDS, src, FALSE)) + visible_message( + SPAN_NOTICE("\The [src] ceases adjusting [G.his] scaling."), + self_message = SPAN_WARNING("You must remain still to seal or unseal your scaling.")) + return + + if(incapacitated(INCAPACITATION_KNOCKOUT)) + to_chat(src, SPAN_WARNING("You are in no state to do that.")) + return + + // TODO: maybe add cold and heat thresholds to this. + var/my_ref = "\ref[src]" + var/datum/species/vox/kikiki = species + if((kikiki.current_pressure_toggle[my_ref] = !kikiki.current_pressure_toggle[my_ref])) + visible_message( + SPAN_NOTICE("\The [src]'s scaling flattens and smooths out."), + self_message = SPAN_NOTICE("You flatten your scaling and inflate internal bladders, protecting yourself against low pressure at the cost of dexterity.") + ) + else + visible_message( + SPAN_NOTICE("\The [src]'s scaling bristles roughly."), + self_message = SPAN_NOTICE("You bristle your scaling and deflate your internal bladders, restoring mobility but leaving yourself vulnerable to low pressure.") + ) diff --git a/code/modules/mob/living/carbon/human/species/species_getters.dm b/code/modules/mob/living/carbon/human/species/species_getters.dm index 9053e66e0b..b288005f42 100644 --- a/code/modules/mob/living/carbon/human/species/species_getters.dm +++ b/code/modules/mob/living/carbon/human/species/species_getters.dm @@ -113,3 +113,21 @@ /datum/species/proc/get_vision_flags(var/mob/living/carbon/human/H) return vision_flags + +/datum/species/proc/get_hazard_high_pressure(var/mob/living/carbon/human/H) + return hazard_high_pressure + +/datum/species/proc/get_warning_high_pressure(var/mob/living/carbon/human/H) + return warning_high_pressure + +/datum/species/proc/get_warning_low_pressure(var/mob/living/carbon/human/H) + return warning_low_pressure + +/datum/species/proc/get_hazard_low_pressure(var/mob/living/carbon/human/H) + return hazard_low_pressure + +/datum/species/proc/get_safe_pressure(var/mob/living/carbon/human/H) + return safe_pressure + +/datum/species/proc/get_slowdown(var/mob/living/carbon/human/H) + return slowdown diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm index 71657c63fd..a8d85a21b6 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm @@ -225,7 +225,7 @@ var/global/datum/species/shapeshifter/promethean/prometheans var/datum/gas_mixture/environment = T.return_air() var/pressure = environment.return_pressure() var/affecting_pressure = H.calculate_affecting_pressure(pressure) - if(affecting_pressure <= hazard_low_pressure) // Dangerous low pressure stops the regeneration of physical wounds. Body is focusing on keeping them intact rather than sealing. + if(affecting_pressure <= get_hazard_low_pressure(H)) // Dangerous low pressure stops the regeneration of physical wounds. Body is focusing on keeping them intact rather than sealing. regen_brute = FALSE regen_burn = FALSE diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 0d7ef3ff39..668cd4e46f 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -1721,22 +1721,49 @@ shaved //Skrell 'hairstyles' /datum/sprite_accessory/hair/skr - name = "Skrell Average Tentacles" - icon_state = "skrell_hair_average" + name = "Tentacles, Average" + icon_state = "skrell_short" species_allowed = list(SPECIES_SKRELL, SPECIES_EVENT1, SPECIES_EVENT2, SPECIES_EVENT3) -/datum/sprite_accessory/hair/skr/tentacle_veryshort - name = "Skrell Short Tentacles" - icon_state = "skrell_hair_short" - gender = MALE +/datum/sprite_accessory/hair/skr/pullback + name = "Tentacles, Average, Pullback" + icon_state = "skrell_short_pullback" -/datum/sprite_accessory/hair/skr/tentacle_average - name = "Skrell Long Tentacles" - icon_state = "skrell_hair_long" +/datum/sprite_accessory/hair/skr/very_short + name = "Tentacles, Short" + icon_state = "skrell_very_short" -/datum/sprite_accessory/hair/skr/tentacle_verylong - name = "Skrell Very Long Tentacles" - icon_state = "skrell_hair_verylong" +/datum/sprite_accessory/hair/skr/long + name = "Tentacles, Long" + icon_state = "skrell_long" + +/datum/sprite_accessory/hair/skr/long/pullback + name = "Tentacles, Long, Pullback" + icon_state = "skrell_long_pullback" + +/datum/sprite_accessory/hair/skr/long/scarf + name = "Tentacles, Long, Scarf" + icon_state = "skrell_long_scarf" + +/datum/sprite_accessory/hair/skr/long/wavy + name = "Tentacles, Long, Wavy" + icon_state = "skrell_long_wavy" + +/datum/sprite_accessory/hair/skr/very_long + name = "Tentacles, Very Long" + icon_state = "skrell_very_long" + +/datum/sprite_accessory/hair/skr/very_long/pullback + name = "Tentacles, Very Long, Pullback" + icon_state = "skrell_very_long_pullback" + +/datum/sprite_accessory/hair/skr/very_long/scarf + name = "Tentacles, Very Long, Scarf" + icon_state = "skrell_very_long_scarf" + +/datum/sprite_accessory/hair/skr/very_long/wavy + name = "Tentacles, Very Long, Wavy" + icon_state = "skrell_very_long_wavy" //Tajaran hairstyles /datum/sprite_accessory/hair/taj diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index e0cef49359..3578e0854d 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -91,27 +91,16 @@ return -/obj/item/reagent_containers/pill/attackby(obj/item/W as obj, mob/user as mob) - if(is_sharp(W)) - var/obj/item/reagent_containers/powder/J = new /obj/item/reagent_containers/powder(src.loc) - user.visible_message("[user] gently cuts up [src] with [W]!") - playsound(src.loc, 'sound/effects/chop.ogg', 50, 1) - - if(reagents) - reagents.trans_to_obj(J, reagents.total_volume) - J.get_appearance() +/obj/item/reagent_containers/pill/attackby(obj/item/item, mob/living/user) + if (is_sharp(item) || istype(item, /obj/item/card)) + user.visible_message( + SPAN_ITALIC("\The [user] cuts up \a [src] with \a [item]."), + SPAN_ITALIC("You cut up \the [src] with \the [item].") + ) + playsound(loc, 'sound/effects/chop.ogg', 50, 1) + new /obj/item/reagent_containers/powder (loc, reagents) qdel(src) - - if(istype(W, /obj/item/card/id)) - var/obj/item/reagent_containers/powder/J = new /obj/item/reagent_containers/powder(src.loc) - user.visible_message("[user] clumsily chops up [src] with [W]!") - playsound(src.loc, 'sound/effects/chop.ogg', 50, 1) - - if(reagents) - reagents.trans_to_obj(J, reagents.total_volume) - J.get_appearance() - qdel(src) - + return TRUE return ..() //////////////////////////////////////////////////////////////////////////////// diff --git a/code/unit_tests/subsystem_tests.dm b/code/unit_tests/subsystem_tests.dm index 2e1351f968..eab1f91445 100644 --- a/code/unit_tests/subsystem_tests.dm +++ b/code/unit_tests/subsystem_tests.dm @@ -20,7 +20,7 @@ name = "SUBSYSTEM - ATOMS: Shall have no bad init calls" /datum/unit_test/subsystem_atom_shall_have_no_bad_init_calls/start_test() - if(SSatoms.BadInitializeCalls.len) + if(SSatoms.bad_init_calls.len) log_bad(jointext(SSatoms.InitLog(), null)) fail("[SSatoms] had bad initialization calls.") else @@ -35,7 +35,7 @@ var/fail = FALSE for(var/atom/atom in world) if(!atom.initialized && !QDELETED(atom)) // Not ideal to skip over qdeleted atoms, but a lot of current code uses pre-init qdels - log_bad("Uninitialized atom: [atom.type] - [atom.log_info_line()]") + log_bad("Uninitialized atom: [atom.type] - [atom.get_log_info_line()]") fail = TRUE if(fail) fail("There were uninitialized atoms.") diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index d3f8e33bac..0cae37c8c5 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index 00a893e96f..eeab4db3af 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/icons/mob/human_face_alt.dmi b/icons/mob/human_face_alt.dmi index 6d02bc14bf..6811bb8fd6 100644 Binary files a/icons/mob/human_face_alt.dmi and b/icons/mob/human_face_alt.dmi differ diff --git a/icons/mob/human_face_m.dmi b/icons/mob/human_face_m.dmi index f83ec2d74a..767aed67f2 100644 Binary files a/icons/mob/human_face_m.dmi and b/icons/mob/human_face_m.dmi differ diff --git a/icons/mob/human_races/r_def_skrell.dmi b/icons/mob/human_races/r_def_skrell.dmi index 7b24ba3b4d..5224f7dd59 100644 Binary files a/icons/mob/human_races/r_def_skrell.dmi and b/icons/mob/human_races/r_def_skrell.dmi differ diff --git a/icons/mob/human_races/r_skrell.dmi b/icons/mob/human_races/r_skrell.dmi index 05e51ca0da..cf99d43e60 100644 Binary files a/icons/mob/human_races/r_skrell.dmi and b/icons/mob/human_races/r_skrell.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index a72af16f2b..0ee1a7c4a8 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 327c59d686..f2b2154cb3 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 40157ba26d..9fbeeec331 100644 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/maps/cynosure/cynosure-2.dmm b/maps/cynosure/cynosure-2.dmm index 103457257a..29db1bb9e6 100644 --- a/maps/cynosure/cynosure-2.dmm +++ b/maps/cynosure/cynosure-2.dmm @@ -1147,8 +1147,6 @@ /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, /obj/effect/floor_decal/steeldecal/steel_decals_central7{ dir = 8 }, @@ -1168,6 +1166,8 @@ }, /obj/item/melee/umbrella/random, /obj/item/melee/umbrella/random, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, /turf/simulated/floor/tiled, /area/surface/station/arrivals/cynosure) "aGP" = ( @@ -2610,12 +2610,12 @@ /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, /obj/item/melee/umbrella/random{ pixel_y = -4 }, /obj/item/melee/umbrella/random, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, /turf/simulated/floor/tiled, /area/surface/station/arrivals/cynosure) "bkP" = ( @@ -27785,6 +27785,12 @@ name = "Biohazard Shutter" }, /obj/structure/closet/emcloset, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, +/obj/item/clothing/shoes/boots/winter, +/obj/item/clothing/shoes/boots/winter, +/obj/item/melee/umbrella/random, +/obj/item/melee/umbrella/random, /turf/simulated/floor/tiled/techfloor, /area/surface/station/rnd/hallway/gnd) "mHN" = ( @@ -35441,12 +35447,12 @@ /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, /obj/item/melee/umbrella/random{ pixel_y = -4 }, /obj/item/melee/umbrella/random, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, /turf/simulated/floor/tiled, /area/surface/station/arrivals/cynosure) "pVb" = ( @@ -42243,9 +42249,8 @@ /obj/item/melee/umbrella/random{ pixel_y = -4 }, -/obj/effect/floor_decal/arrows, -/obj/item/clothing/suit/storage/hooded/wintercoat, /obj/item/clothing/suit/storage/hooded/wintercoat, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, /turf/simulated/floor/plating, /area/surface/station/hallway/primary/groundfloor/south) "sWe" = ( @@ -50374,14 +50379,14 @@ /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/shoes/boots/winter, /obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, -/obj/item/clothing/suit/storage/hooded/wintercoat, /obj/item/melee/umbrella/random, /obj/item/melee/umbrella/random, /obj/machinery/camera/network/ground_floor{ c_tag = "Ground Floor - Arrivals North"; dir = 8 }, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, +/obj/item/clothing/suit/storage/hooded/wintercoat/parka/nanotrasen, /turf/simulated/floor/tiled, /area/surface/station/arrivals/cynosure) "wNJ" = (