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" = (