diff --git a/code/__DEFINES/assert.dm b/code/__DEFINES/assert.dm
new file mode 100644
index 0000000000..cff7810771
--- /dev/null
+++ b/code/__DEFINES/assert.dm
@@ -0,0 +1,13 @@
+#undef ASSERT
+
+/// Override BYOND's native ASSERT to optionally specify a message
+#define ASSERT(condition, message...) \
+ if (!(condition)) { \
+ CRASH(assertion_message(__FILE__, __LINE__, #condition, ##message)) \
+ }
+
+/proc/assertion_message(file, line, condition, message)
+ if (!isnull(message))
+ message = " - [message]"
+
+ return "[file]:[line]:Assertion failed: [condition][message]"
diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm
index 0820c709cd..d69f0891ff 100644
--- a/code/__DEFINES/conflict.dm
+++ b/code/__DEFINES/conflict.dm
@@ -109,6 +109,7 @@
#define SHOES_SLOWDOWN -1
#define SLOWDOWN_ARMOR_NONE 0
+#define SLOWDOWN_ARMOR_SUPER_LIGHT 0.10
#define SLOWDOWN_ARMOR_VERY_LIGHT 0.20
#define SLOWDOWN_ARMOR_LIGHT 0.35
#define SLOWDOWN_ARMOR_MEDIUM 0.55
diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
index b9e89bf441..529b2e9314 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
@@ -1,3 +1,5 @@
+///from base of mob/set_stat(): (new_stat, old_stat)
+#define COMSIG_MOB_STATCHANGE "mob_statchange"
/// From /obj/structure/machinery/door/airlock/proc/take_damage
#define COMSIG_MOB_DESTROY_AIRLOCK "mob_destroy_airlock"
@@ -132,3 +134,6 @@
/// From /mob/proc/say_dead(message)
#define COMSIG_DEAD_SPEAK "comsig_dead_speak"
#define COMPONENT_OVERRIDE_DEAD_SPEAK (1<<0)
+
+/// Cancels all running cloaking effects on target
+#define COMSIG_MOB_EFFECT_CLOAK_CANCEL "mob_effect_cloak_cancel"
diff --git a/code/__DEFINES/dcs/signals/atom/signals_atom.dm b/code/__DEFINES/dcs/signals/atom/signals_atom.dm
index 17915824a0..394e63bd6e 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_atom.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_atom.dm
@@ -71,3 +71,6 @@
/// Called when an atom is mouse dropped on another atom, from /client/MouseDrop: (atom/dropped_onto)
#define COMSIG_ATOM_DROP_ON "atom_drop_on"
+
+/// Called when an atom has emp_act called on it, from /atom/emp_act: (severity)
+#define COMSIG_ATOM_EMP_ACT "atom_emp_act"
diff --git a/code/__DEFINES/dcs/signals/atom/signals_cell.dm b/code/__DEFINES/dcs/signals/atom/signals_cell.dm
new file mode 100644
index 0000000000..75e13d8bfd
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/atom/signals_cell.dm
@@ -0,0 +1,26 @@
+/// (charge_amount)
+#define COMSIG_CELL_USE_CHARGE "cell_use_charge"
+ #define COMPONENT_CELL_NO_USE_CHARGE (1<<0)
+
+/// (charge_amount)
+#define COMSIG_CELL_ADD_CHARGE "cell_add_charge"
+
+#define COMSIG_CELL_START_TICK_DRAIN "cell_start_tick_drain"
+
+#define COMSIG_CELL_STOP_TICK_DRAIN "cell_stop_tick_drain"
+
+/// (mob/living/user)
+#define COMSIG_CELL_TRY_RECHARGING "cell_try_recharging"
+ #define COMPONENT_CELL_NO_RECHARGE (1<<0)
+
+#define COMSIG_CELL_OUT_OF_CHARGE "cell_out_of_charge"
+
+/// (charge_amount)
+#define COMSIG_CELL_CHECK_CHARGE "cell_check_charge"
+ #define COMPONENT_CELL_CHARGE_INSUFFICIENT (1<<0)
+
+#define COMSIG_CELL_TRY_INSERT_CELL "cell_try_insert_cell"
+ #define COMPONENT_CANCEL_CELL_INSERT (1<<0)
+
+/// (mob/living/user)
+#define COMSIG_CELL_REMOVE_CELL "cell_remove_cell"
diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
index 7e4436b3cf..90aa4bd984 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
@@ -26,3 +26,5 @@
/// from /obj/proc/afterbuckle()
#define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle"
+
+#define COMSIG_STRUCTURE_CRATE_SQUAD_LAUNCHED "structure_crate_squad_launched"
diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm
index 949f9385e8..e51c6f08ad 100644
--- a/code/__DEFINES/job.dm
+++ b/code/__DEFINES/job.dm
@@ -11,6 +11,7 @@
#define SQUAD_MARINE_CRYO "Foxtrot"
#define SQUAD_MARINE_INTEL "Intel"
#define SQUAD_SOF "SOF"
+#define SQUAD_CBRN "CBRN"
// Job name defines
#define JOB_SQUAD_MARINE "Rifleman"
diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm
index 5a0809d83a..764282d597 100644
--- a/code/__DEFINES/keybinding.dm
+++ b/code/__DEFINES/keybinding.dm
@@ -14,6 +14,7 @@
#define COMSIG_KB_ADMIN_INVISIMINTOGGLE_DOWN "keybinding_admin_invisimintoggle_down"
#define COMSIG_KB_ADMIN_DEADMIN_DOWN "keybinding_admin_deadmin_down"
#define COMSIG_KB_ADMIN_READMIN_DOWN "keybinding_admin_readmin_down"
+#define COMSIG_KB_ADMIN_MENTORSAY_DOWN "keybinding_admin_mentorsay_down"
//Carbon
#define COMSIG_KB_CARBON_HOLDRUNMOVEINTENT_DOWN "keybinding_carbon_holdrunmoveintent_down"
diff --git a/code/__DEFINES/speech_channels.dm b/code/__DEFINES/speech_channels.dm
index 3f6e4720bd..5a9a74af8a 100644
--- a/code/__DEFINES/speech_channels.dm
+++ b/code/__DEFINES/speech_channels.dm
@@ -1,6 +1,7 @@
// Used to direct channels to speak into.
#define SAY_CHANNEL "Say"
#define COMMS_CHANNEL "Comms"
+#define WHISPER_CHANNEL "Whisper"
#define ME_CHANNEL "Me"
#define OOC_CHANNEL "OOC"
#define LOOC_CHANNEL "LOOC"
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ef500b6af9..b2704fc668 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -1,63 +1,76 @@
-//shamelessly ripped from TG
#define SIGNAL_ADDTRAIT(trait_ref) "addtrait [trait_ref]"
#define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]"
// trait accessor defines
-//here be dragons
#define ADD_TRAIT(target, trait, source) \
do { \
var/list/_L; \
- if (!target.status_traits) { \
- target.status_traits = list(); \
- _L = target.status_traits; \
+ if (!target._status_traits) { \
+ target._status_traits = list(); \
+ _L = target._status_traits; \
_L[trait] = list(source); \
SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
- if(trait in GLOB.traits_with_elements){ \
- target.AddElement(GLOB.traits_with_elements[trait]); \
- } \
} else { \
- _L = target.status_traits; \
+ _L = target._status_traits; \
if (_L[trait]) { \
_L[trait] |= list(source); \
} else { \
_L[trait] = list(source); \
SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
- if(trait in GLOB.traits_with_elements){ \
- target.AddElement(GLOB.traits_with_elements[trait]); \
- } \
} \
} \
} while (0)
#define REMOVE_TRAIT(target, trait, sources) \
do { \
- var/list/_L = target.status_traits; \
+ var/list/_L = target._status_traits; \
var/list/_S; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
- if (_L && _L[trait]) { \
+ if (_L?[trait]) { \
for (var/_T in _L[trait]) { \
- if ((!_S && (_T != TRAIT_SOURCE_QUIRK)) || (_T in _S)) { \
+ if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \
_L[trait] -= _T \
} \
};\
if (!length(_L[trait])) { \
_L -= trait; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
- if(trait in GLOB.traits_with_elements) { \
- target.RemoveElement(GLOB.traits_with_elements[trait]); \
- } \
}; \
if (!length(_L)) { \
- target.status_traits = null \
+ target._status_traits = null \
+ }; \
+ } \
+ } while (0)
+#define REMOVE_TRAIT_NOT_FROM(target, trait, sources) \
+ do { \
+ var/list/_traits_list = target._status_traits; \
+ var/list/_sources_list; \
+ if (sources && !islist(sources)) { \
+ _sources_list = list(sources); \
+ } else { \
+ _sources_list = sources\
+ }; \
+ if (_traits_list?[trait]) { \
+ for (var/_trait_source in _traits_list[trait]) { \
+ if (!(_trait_source in _sources_list)) { \
+ _traits_list[trait] -= _trait_source \
+ } \
+ };\
+ if (!length(_traits_list[trait])) { \
+ _traits_list -= trait; \
+ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
+ }; \
+ if (!length(_traits_list)) { \
+ target._status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAITS_NOT_IN(target, sources) \
do { \
- var/list/_L = target.status_traits; \
+ var/list/_L = target._status_traits; \
var/list/_S = sources; \
if (_L) { \
for (var/_T in _L) { \
@@ -65,20 +78,17 @@
if (!length(_L[_T])) { \
_L -= _T; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T), _T); \
- if(_T in GLOB.traits_with_elements) { \
- target.RemoveElement(GLOB.traits_with_elements[_T]); \
}; \
};\
- };\
if (!length(_L)) { \
- target.status_traits = null\
+ target._status_traits = null\
};\
}\
} while (0)
#define REMOVE_TRAITS_IN(target, sources) \
do { \
- var/list/_L = target.status_traits; \
+ var/list/_L = target._status_traits; \
var/list/_S = sources; \
if (sources && !islist(sources)) { \
_S = list(sources); \
@@ -91,46 +101,36 @@
if (!length(_L[_T])) { \
_L -= _T; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T)); \
- if(_T in GLOB.traits_with_elements) { \
- target.RemoveElement(GLOB.traits_with_elements[_T]); \
}; \
};\
- };\
if (!length(_L)) { \
- target.status_traits = null\
+ target._status_traits = null\
};\
}\
} while (0)
-/// Will 100% nuke a trait regardless of source. Preferably use this as little as possible
-#define REMOVE_TRAIT_ALLSOURCES(target, trait) \
- do { \
- var/list/_L = target.status_traits; \
- if (_L?[trait]) { \
- if (length(_L)) { \
- _L -= trait; \
- SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
- }; \
- else { \
- target.status_traits = null \
- }; \
- } \
- } while (0)
-
-#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
-#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
-#define HAS_TRAIT_FROM_ONLY(target, trait, source) (\
- target.status_traits ?\
- (target.status_traits[trait] ?\
- ((source in target.status_traits[trait]) && (length(target.status_traits) == 1))\
- : FALSE)\
- : FALSE)
-#define HAS_TRAIT_NOT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (length(target.status_traits[trait] - source) > 0) : FALSE) : FALSE)
-
+#define HAS_TRAIT(target, trait) (target._status_traits?[trait] ? TRUE : FALSE)
+#define HAS_TRAIT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]))
+#define HAS_TRAIT_FROM_ONLY(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]) && (length(target._status_traits[trait]) == 1))
+#define HAS_TRAIT_NOT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (length(target._status_traits[trait] - source) > 0))
+/// Returns a list of trait sources for this trait. Only useful for wacko cases and internal futzing
+/// You should not be using this
+#define GET_TRAIT_SOURCES(target, trait) (target._status_traits?[trait] || list())
+/// Returns the amount of sources for a trait. useful if you don't want to have a "thing counter" stuck around all the time
+#define COUNT_TRAIT_SOURCES(target, trait) length(GET_TRAIT_SOURCES(target, trait))
+/// A simple helper for checking traits in a mob's mind
+#define HAS_MIND_TRAIT(target, trait) (HAS_TRAIT(target, trait) || (target.mind ? HAS_TRAIT(target.mind, trait) : FALSE))
/// Example trait
// #define TRAIT_X "t_x"
+
+/// cannot be removed without admin intervention
+#define ROUNDSTART_TRAIT "roundstart"
+
//-- mob traits --
+/// Apply this to make a mob not dense, and remove it when you want it to no longer make them undense, other sorces of undesity will still apply. Always define a unique source when adding a new instance of this!
+#define TRAIT_UNDENSE "undense"
+
// SPECIES TRAITS
/// Knowledge of Yautja technology
#define TRAIT_YAUTJA_TECH "t_yautja_tech"
@@ -279,6 +279,7 @@ GLOBAL_LIST_INIT(mob_traits, list(
*/
GLOBAL_LIST_INIT(traits_by_type, list(
/mob = list(
+ "TRAIT_UNDENSE" = TRAIT_UNDENSE,
"TRAIT_YAUTJA_TECH" = TRAIT_YAUTJA_TECH,
"TRAIT_SUPER_STRONG" = TRAIT_SUPER_STRONG,
"TRAIT_FOREIGN_BIO" = TRAIT_FOREIGN_BIO,
@@ -355,6 +356,8 @@ GLOBAL_LIST(trait_name_map)
// #define TRAIT_SOURCE_Y "t_s_y"
#define TRAIT_SOURCE_INHERENT "t_s_inherent"
//-- mob traits --
+///Status trait coming from lying down through update_canmove()
+#define LYING_TRAIT "lying"
///Status trait coming from species. .human/species_gain()
#define TRAIT_SOURCE_SPECIES "t_s_species"
///Status trait coming from the hive.
@@ -379,6 +382,8 @@ GLOBAL_LIST(trait_name_map)
#define TRAIT_SOURCE_ABILITY(ability) "t_s_ability_[ability]"
///Status trait forced by the xeno action charge
#define TRAIT_SOURCE_XENO_ACTION_CHARGE "t_s_xeno_action_charge"
+///Status trait coming from a xeno nest
+#define XENO_NEST_TRAIT "xeno_nest"
//-- structure traits --
///Status trait coming from being flipped or unflipped.
#define TRAIT_SOURCE_FLIP_TABLE "t_s_flip_table"
@@ -390,3 +395,12 @@ GLOBAL_LIST(trait_name_map)
//Status trait coming from clothing.
#define TRAIT_SOURCE_CLOTHING "t_s_clothing"
+
+/// trait effect related to active specialist gear
+#define SPECIALIST_GEAR_TRAIT "specialist_gear"
+/// traits associated with usage of snowflake dropship double seats
+#define DOUBLE_SEATS_TRAIT "double_seats"
+/// traits associated with xeno on-ground weeds
+#define XENO_WEED_TRAIT "xeno_weed"
+/// traits from chloroform usage
+#define CHLOROFORM_TRAIT "chloroform"
diff --git a/code/__HELPERS/job.dm b/code/__HELPERS/job.dm
index 43902b07cf..89fe687764 100644
--- a/code/__HELPERS/job.dm
+++ b/code/__HELPERS/job.dm
@@ -26,13 +26,6 @@
if(I.assignment in GLOB.joblist) return I.assignment
return "Unknown"
-/proc/FindNameFromID(mob/living/carbon/human/H)
- ASSERT(istype(H))
- var/obj/item/card/id/I = H.wear_id
- if(istype(I)) return I.registered_name
- I = H.get_active_hand()
- if(istype(I)) return I.registered_name
-
/proc/get_all_job_icons() return GLOB.joblist + list("Prisoner")//For all existing HUD icons
/obj/proc/GetJobName() //Used in secHUD icon generation
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
new file mode 100644
index 0000000000..ba99b2e1e7
--- /dev/null
+++ b/code/__HELPERS/traits.dm
@@ -0,0 +1,43 @@
+#define TRAIT_CALLBACK_ADD(target, trait, source) CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(___TraitAdd), ##target, ##trait, ##source)
+#define TRAIT_CALLBACK_REMOVE(target, trait, source) CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(___TraitRemove), ##target, ##trait, ##source)
+
+///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback.
+/proc/___TraitAdd(target,trait,source)
+ if(!target || !trait || !source)
+ return
+ if(islist(target))
+ for(var/i in target)
+ if(!isatom(i))
+ continue
+ var/atom/the_atom = i
+ ADD_TRAIT(the_atom,trait,source)
+ else if(isatom(target))
+ var/atom/the_atom2 = target
+ ADD_TRAIT(the_atom2,trait,source)
+
+///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback.
+/proc/___TraitRemove(target,trait,source)
+ if(!target || !trait || !source)
+ return
+ if(islist(target))
+ for(var/i in target)
+ if(!isatom(i))
+ continue
+ var/atom/the_atom = i
+ REMOVE_TRAIT(the_atom,trait,source)
+ else if(isatom(target))
+ var/atom/the_atom2 = target
+ REMOVE_TRAIT(the_atom2,trait,source)
+
+
+/// Proc that handles adding multiple traits to a target via a list. Must have a common source and target.
+/datum/proc/add_traits(list/list_of_traits, source)
+ ASSERT(islist(list_of_traits), "Invalid arguments passed to add_traits! Invoked on [src] with [list_of_traits], source being [source].")
+ for(var/trait in list_of_traits)
+ ADD_TRAIT(src, trait, source)
+
+/// Proc that handles removing multiple traits from a target via a list. Must have a common source and target.
+/datum/proc/remove_traits(list/list_of_traits, source)
+ ASSERT(islist(list_of_traits), "Invalid arguments passed to remove_traits! Invoked on [src] with [list_of_traits], source being [source].")
+ for(var/trait in list_of_traits)
+ REMOVE_TRAIT(src, trait, source)
diff --git a/code/controllers/subsystem/communications.dm b/code/controllers/subsystem/communications.dm
index d4144c9f0e..e214b29836 100644
--- a/code/controllers/subsystem/communications.dm
+++ b/code/controllers/subsystem/communications.dm
@@ -107,6 +107,7 @@ var/const/MAX_FREQ = 1468 // ---------------------------------------------------
var/const/HC_FREQ = 1471
var/const/SOF_FREQ = 1472
var/const/PVST_FREQ = 1473
+var/const/CBRN_FREQ = 1474
//Ship department channels
var/const/SENTRY_FREQ = 1480
@@ -162,6 +163,7 @@ var/list/radiochannels = list(
SQUAD_MARINE_5 = ECHO_FREQ,
SQUAD_MARINE_CRYO = CRYO_FREQ,
SQUAD_SOF = SOF_FREQ,
+ SQUAD_CBRN = CBRN_FREQ,
RADIO_CHANNEL_ALAMO = DS1_FREQ,
RADIO_CHANNEL_NORMANDY = DS2_FREQ,
@@ -262,6 +264,7 @@ SUBSYSTEM_DEF(radio)
"[DELTA_FREQ]" = "deltaradio",
"[ECHO_FREQ]" = "echoradio",
"[CRYO_FREQ]" = "cryoradio",
+ "[CBRN_FREQ]" = "hcradio",
"[SOF_FREQ]" = "hcradio",
"[HC_FREQ]" = "hcradio",
"[PVST_FREQ]" = "pvstradio",
diff --git a/code/datums/agents/tools/chloroform.dm b/code/datums/agents/tools/chloroform.dm
index 464533309b..cc804c1d27 100644
--- a/code/datums/agents/tools/chloroform.dm
+++ b/code/datums/agents/tools/chloroform.dm
@@ -48,7 +48,7 @@
/obj/item/weapon/chloroform/proc/grab_stun(mob/living/M, mob/living/user)
M.anchored = TRUE
M.frozen = TRUE
- M.density = FALSE
+ ADD_TRAIT(M, TRAIT_UNDENSE, CHLOROFORM_TRAIT)
M.able_to_speak = FALSE
M.update_canmove()
@@ -83,6 +83,7 @@
M.able_to_speak = TRUE
M.layer = MOB_LAYER
M.unfreeze()
+ REMOVE_TRAIT(M, TRAIT_UNDENSE, CHLOROFORM_TRAIT)
QDEL_NULL(mask_item)
diff --git a/code/datums/components/cell.dm b/code/datums/components/cell.dm
new file mode 100644
index 0000000000..81ef3733e2
--- /dev/null
+++ b/code/datums/components/cell.dm
@@ -0,0 +1,202 @@
+#define UNLIMITED_CHARGE -1
+#define UNLIMITED_DISTANCE -1
+
+/datum/component/cell
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ /// Maximum charge of the power cell, set to -1 for infinite charge
+ var/max_charge = 10000
+ /// Initial max charge of the power cell
+ var/initial_max_charge
+ /// Current charge of power cell
+ var/charge = 10000
+ /// If the component can be recharged by hitting its parent with a cell
+ var/hit_charge = FALSE
+ /// The maximum amount that can be recharged per tick when using a cell to recharge this component
+ var/max_recharge_tick = 400
+ /// If draining charge on process(), how much to drain per process call
+ var/charge_drain = 10
+ /// If the parent should show cell charge on examine
+ var/display_charge = TRUE
+ /// From how many tiles at the highest someone can examine the parent to see the charge
+ var/charge_examine_range = 1
+ /// If the component requires a cell to be inserted to work instead of having an integrated one
+ var/cell_insert = FALSE
+ /// Ref to an inserted cell. Should only be null if cell_insert is false
+ var/obj/item/cell/inserted_cell
+
+
+/datum/component/cell/Initialize(
+ max_charge = 10000,
+ hit_charge = FALSE,
+ max_recharge_tick = 400,
+ charge_drain = 10,
+ display_charge = TRUE,
+ charge_examine_range = 1,
+ cell_insert = FALSE,
+ )
+
+ . = ..()
+ if(!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.max_charge = max_charge
+ charge = max_charge
+ src.hit_charge = hit_charge
+ src.max_recharge_tick = max_recharge_tick
+ src.charge_drain = charge_drain
+ src.display_charge = display_charge
+ src.charge_examine_range = charge_examine_range
+ src.cell_insert = cell_insert
+
+/datum/component/cell/Destroy(force, silent)
+ QDEL_NULL(inserted_cell)
+ return ..()
+
+
+/datum/component/cell/RegisterWithParent()
+ ..()
+ RegisterSignal(parent, list(COMSIG_PARENT_ATTACKBY, COMSIG_ITEM_ATTACKED), PROC_REF(on_object_hit))
+ RegisterSignal(parent, COMSIG_CELL_ADD_CHARGE, PROC_REF(add_charge))
+ RegisterSignal(parent, COMSIG_CELL_USE_CHARGE, PROC_REF(use_charge))
+ RegisterSignal(parent, COMSIG_CELL_CHECK_CHARGE, PROC_REF(has_charge))
+ RegisterSignal(parent, COMSIG_CELL_START_TICK_DRAIN, PROC_REF(start_drain))
+ RegisterSignal(parent, COMSIG_CELL_STOP_TICK_DRAIN, PROC_REF(stop_drain))
+ RegisterSignal(parent, COMSIG_CELL_REMOVE_CELL, PROC_REF(remove_cell))
+ RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
+
+/datum/component/cell/process()
+ use_charge(null, charge_drain)
+
+/datum/component/cell/proc/on_emp(datum/source, severity)
+ SIGNAL_HANDLER
+
+ use_charge(null, round(max_charge / severity))
+
+/datum/component/cell/proc/start_drain(datum/source)
+ SIGNAL_HANDLER
+
+ START_PROCESSING(SSobj, src)
+
+/datum/component/cell/proc/stop_drain(datum/source)
+ SIGNAL_HANDLER
+
+ STOP_PROCESSING(SSobj, src)
+
+/datum/component/cell/proc/on_examine(datum/source, mob/examiner, list/examine_text)
+ SIGNAL_HANDLER
+
+ if(!display_charge)
+ return
+
+ if((charge_examine_range != UNLIMITED_DISTANCE) && get_dist(examiner, parent) > charge_examine_range)
+ return
+
+ examine_text += "A small gauge in the corner reads \"Power: [round(100 * charge / max_charge)]%\"."
+
+/datum/component/cell/proc/on_object_hit(datum/source, obj/item/cell/attack_obj, mob/living/attacker, params)
+ SIGNAL_HANDLER
+
+ if(!hit_charge || !istype(attack_obj))
+ return
+
+ if(!cell_insert)
+ INVOKE_ASYNC(src, PROC_REF(charge_from_cell), attack_obj, attacker)
+
+ else
+ insert_cell(attack_obj, attacker)
+
+ return COMPONENT_NO_AFTERATTACK|COMPONENT_CANCEL_ITEM_ATTACK
+
+/datum/component/cell/proc/insert_cell(obj/item/cell/power_cell, mob/living/user)
+ if(inserted_cell)
+ to_chat(user, SPAN_WARNING("There's already a power cell in [parent]!"))
+ return
+
+ if(SEND_SIGNAL(parent, COMSIG_CELL_TRY_INSERT_CELL) & COMPONENT_CANCEL_CELL_INSERT)
+ return
+
+ power_cell.drop_to_floor(user)
+ power_cell.forceMove(parent)
+ inserted_cell = power_cell
+ charge = power_cell.charge
+ max_charge = power_cell.maxcharge
+
+/datum/component/cell/proc/remove_cell(mob/living/user)
+ SIGNAL_HANDLER
+
+ user.put_in_hands(inserted_cell, TRUE)
+ to_chat(user, SPAN_NOTICE("You remove [inserted_cell] from [parent]."))
+ inserted_cell = null
+ max_charge = initial_max_charge
+ charge = 0
+
+/datum/component/cell/proc/charge_from_cell(obj/item/cell/power_cell, mob/living/user)
+ if(max_charge == UNLIMITED_CHARGE)
+ to_chat(user, SPAN_WARNING("[parent] doesn't need more power."))
+ return
+
+ while(charge < max_charge)
+ if(SEND_SIGNAL(parent, COMSIG_CELL_TRY_RECHARGING, user) & COMPONENT_CELL_NO_RECHARGE)
+ return
+
+ if(power_cell.charge <= 0)
+ to_chat(user, SPAN_WARNING("[power_cell] is completely dry."))
+ return
+
+ if(!do_after(user, 1 SECONDS, (INTERRUPT_ALL & (~INTERRUPT_MOVED)), BUSY_ICON_BUILD, power_cell, INTERRUPT_DIFF_LOC))
+ to_chat(user, SPAN_WARNING("You were interrupted."))
+ return
+
+ if(power_cell.charge <= 0)
+ return
+
+ var/to_transfer = min(max_recharge_tick, power_cell.charge, (max_charge - charge))
+ if(power_cell.use(to_transfer))
+ add_charge(null, to_transfer)
+ to_chat(user, "You transfer some power between [power_cell] and [parent]. The gauge now reads: [round(100 * charge / max_charge)]%.")
+
+/datum/component/cell/proc/add_charge(datum/source, charge_add = 0)
+ SIGNAL_HANDLER
+
+ if(max_charge == UNLIMITED_CHARGE)
+ return
+
+ if(!charge_add)
+ return
+
+ charge = clamp(charge + charge_add, 0, max_charge)
+
+/datum/component/cell/proc/use_charge(datum/source, charge_use = 0)
+ SIGNAL_HANDLER
+
+ if(max_charge == UNLIMITED_CHARGE)
+ return
+
+ if(!charge_use)
+ return
+
+ if(!charge)
+ return COMPONENT_CELL_NO_USE_CHARGE
+
+ charge = clamp(charge - charge_use, 0, max_charge)
+
+ if(!charge)
+ on_charge_empty()
+ return
+
+/datum/component/cell/proc/has_charge(datum/source, charge_amount = 0)
+ SIGNAL_HANDLER
+
+ if(!charge)
+ return COMPONENT_CELL_CHARGE_INSUFFICIENT
+
+ if(charge < charge_amount)
+ return COMPONENT_CELL_CHARGE_INSUFFICIENT
+
+/datum/component/cell/proc/on_charge_empty()
+ stop_drain()
+ SEND_SIGNAL(parent, COMSIG_CELL_OUT_OF_CHARGE)
+
+#undef UNLIMITED_CHARGE
+#undef UNLIMITED_DISTANCE
diff --git a/code/datums/components/crate_tag.dm b/code/datums/components/crate_tag.dm
new file mode 100644
index 0000000000..379df82a20
--- /dev/null
+++ b/code/datums/components/crate_tag.dm
@@ -0,0 +1,36 @@
+/datum/component/crate_tag
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ /// The crate tag used for notifications and as label
+ var/name
+
+/datum/component/crate_tag/Initialize(name, obj/structure/closet/crate/masquarade_type)
+ var/obj/structure/closet/crate/crate = parent
+ if(!istype(crate))
+ return COMPONENT_INCOMPATIBLE
+ setup(name, masquarade_type)
+ RegisterSignal(parent, COMSIG_STRUCTURE_CRATE_SQUAD_LAUNCHED, PROC_REF(notify_squad))
+
+/datum/component/crate_tag/InheritComponent(datum/component/C, i_am_original, name, obj/structure/closet/crate/masquarade_type)
+ . = ..()
+ setup(name, masquarade_type)
+
+/datum/component/crate_tag/proc/setup(name, obj/structure/closet/crate/masquarade_type)
+ var/obj/structure/closet/crate/crate = parent
+ if(masquarade_type)
+ crate.name = initial(masquarade_type.name)
+ crate.desc = initial(masquarade_type.desc)
+ crate.icon_opened = initial(masquarade_type.icon_opened)
+ crate.icon_closed = initial(masquarade_type.icon_closed)
+ if(crate.opened)
+ crate.icon_state = crate.icon_opened
+ else
+ crate.icon_state = crate.icon_closed
+ if(name)
+ parent.AddComponent(/datum/component/label, name)
+ src.name = name // Keep it around additionally for notifications
+
+/// Handler to notify an overwatched squad that this crate has been dropped for them
+/datum/component/crate_tag/proc/notify_squad(datum/source, datum/squad/squad)
+ SIGNAL_HANDLER
+ squad.send_message("'[name]' supply drop incoming. Heads up!")
+ squad.send_maptext(name, "Incoming Supply Drop:")
diff --git a/code/datums/components/weed_food.dm b/code/datums/components/weed_food.dm
index ce6c17e0af..16be8665f5 100644
--- a/code/datums/components/weed_food.dm
+++ b/code/datums/components/weed_food.dm
@@ -259,7 +259,7 @@
active = FALSE
merged = TRUE
- parent_mob.density = FALSE
+ ADD_TRAIT(parent_mob, TRAIT_UNDENSE, XENO_WEED_TRAIT)
parent_mob.anchored = TRUE
parent_mob.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
parent_mob.plane = FLOOR_PLANE
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 31e909b068..a2b060572b 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -145,6 +145,9 @@ GLOBAL_DATUM_INIT(data_core, /datum/datacore, new)
continue
dept_flags |= FLAG_SHOW_MARINES
squad_sublists[squad_name] = TRUE
+ ///If it is a real squad in the USCM squad list to prevent the crew manifest from breaking
+ if(!(squad_name in ROLES_SQUAD_ALL))
+ continue
LAZYSET(marines_by_squad[squad_name][real_rank], name, rank)
//here we fill manifest
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index b26c6afe4d..7d497785a7 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -19,8 +19,8 @@
/// Active timers with this datum as the target
var/list/active_timers
- /// Status traits attached.
- var/list/status_traits
+ /// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...))
+ var/list/_status_traits
/**
* Components attached to this datum
diff --git a/code/datums/diseases/black_goo.dm b/code/datums/diseases/black_goo.dm
index 38a26f3648..d4d9b6f509 100644
--- a/code/datums/diseases/black_goo.dm
+++ b/code/datums/diseases/black_goo.dm
@@ -211,6 +211,15 @@
. = ..()
reagents.add_reagent("antiZed", 30)
+/obj/item/reagent_container/glass/bottle/labeled_black_goo_cure
+ name = "\"Pathogen\" cure bottle"
+ desc = "The bottle has a biohazard symbol on the front, and has a label, designating its use against Agent A0-3959X.91–15, colloquially known as the \"Black Goo\"."
+ icon_state = "bottle20"
+
+/obj/item/reagent_container/glass/bottle/labeled_black_goo_cure/Initialize()
+ . = ..()
+ reagents.add_reagent("antiZed", 60)
+
/datum/language/zombie
name = "Zombie"
desc = "A growling, guttural method of communication, only Zombies seem to be capable of producing these sounds."
diff --git a/code/datums/emergency_calls/cbrn.dm b/code/datums/emergency_calls/cbrn.dm
new file mode 100644
index 0000000000..3a6b1c6406
--- /dev/null
+++ b/code/datums/emergency_calls/cbrn.dm
@@ -0,0 +1,80 @@
+/datum/emergency_call/cbrn
+ name = "CBRN (Squad)"
+ arrival_message = "A CBRN squad has been dispatched to your ship. Stand by."
+ objectives = "Handle the chemical, biological, radiological, or nuclear threat. Further orders may be provided."
+ mob_min = 3
+ mob_max = 5
+ max_heavies = 0
+ max_smartgunners = 0
+
+/datum/emergency_call/cbrn/create_member(datum/mind/new_mind, turf/override_spawn_loc)
+ var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point()
+
+ if(!istype(spawn_loc))
+ return //Didn't find a useable spawn point.
+
+ var/mob/living/carbon/human/mob = new(spawn_loc)
+ new_mind.transfer_to(mob, TRUE)
+
+ if(!leader && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job))
+ leader = mob
+ arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/leader, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Fireteam Leader!"))
+
+ else if(medics < max_medics && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(mob.client, JOB_SQUAD_MEDIC, time_required_for_job))
+ medics++
+ arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/medic, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Squad Medic!"))
+
+ else if(engineers < max_engineers && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_ENGINEER) && check_timelock(mob.client, JOB_SQUAD_ENGI, time_required_for_job))
+ engineers++
+ arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/engineer, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Squad Engineer!"))
+
+ else
+ arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/standard, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are a CBRN Squad Rifleman!"))
+
+ to_chat(mob, SPAN_ROLE_BODY("You are a member of the USCM's CBRN. The CBRN is a force that specializes in handling chemical, biological, radiological, and nuclear threats."))
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS)
+
+/datum/emergency_call/cbrn/ert
+ name = "CBRN (Distress)"
+ arrival_message = "Your distress signal has been received and we are dispatching the nearest CBRN squad to board with you now. Stand by."
+ probability = 10
+
+/datum/emergency_call/cbrn/ert/New()
+ ..()
+ objectives = "Investigate the distress signal aboard the [MAIN_SHIP_NAME]."
+
+/datum/emergency_call/cbrn/specialists
+ name = "CBRN (Specialists)"
+ mob_min = 2
+ mob_max = 5
+ max_engineers = 0
+ max_medics = 0
+
+/datum/emergency_call/cbrn/specialists/New()
+ var/cbrn_ship_name = "Unit [pick(nato_phonetic_alphabet)]-[rand(1, 99)]"
+ arrival_message = "[MAIN_SHIP_NAME], CBRN [cbrn_ship_name] has been dispatched. Follow all orders provided by [cbrn_ship_name]."
+ objectives = "You are a specialist team in [cbrn_ship_name] dispatched to quell a threat to [MAIN_SHIP_NAME]. Further orders may be provided."
+
+/datum/emergency_call/cbrn/specialists/create_member(datum/mind/new_mind, turf/override_spawn_loc)
+ var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point()
+
+ if(!istype(spawn_loc))
+ return //Didn't find a useable spawn point.
+
+ var/mob/living/carbon/human/mob = new(spawn_loc)
+ new_mind.transfer_to(mob, TRUE)
+
+ if(!leader && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job))
+ leader = mob
+ arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/specialist/lead, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Specialist Squad Leader!"))
+ else
+ arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/specialist, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are a CBRN Specialist!"))
+
+ to_chat(mob, SPAN_ROLE_BODY("You are a member of the USCM's CBRN. The CBRN is a force that specializes in handling chemical, biological, radiological, and nuclear threats."))
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS)
diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm
index 4c3dfbbfac..9db46955a5 100644
--- a/code/datums/emergency_calls/emergency_call.dm
+++ b/code/datums/emergency_calls/emergency_call.dm
@@ -305,7 +305,7 @@
candidates = list()
if(arrival_message && announce_incoming)
- marine_announcement(arrival_message, "Intercepted Tranmission:")
+ marine_announcement(arrival_message, "Intercepted Transmission:")
/datum/emergency_call/proc/add_candidate(mob/M)
if(!M.client || (M.mind && (M.mind in candidates)) || istype(M, /mob/living/carbon/xenomorph))
diff --git a/code/datums/factions/uscm.dm b/code/datums/factions/uscm.dm
index 0a989ab206..2197d674c4 100644
--- a/code/datums/factions/uscm.dm
+++ b/code/datums/factions/uscm.dm
@@ -190,6 +190,19 @@
if(JOB_CMB_OBS)
marine_rk = "obs"
icon_prefix = "cmb_"
+ // Check squad marines here too, for the unique ones
+ if(JOB_SQUAD_ENGI)
+ marine_rk = "engi"
+ if(JOB_SQUAD_MEDIC)
+ marine_rk = "med"
+ if(JOB_SQUAD_SPECIALIST)
+ marine_rk = "spec"
+ if(JOB_SQUAD_SMARTGUN)
+ marine_rk = "gun"
+ if(JOB_SQUAD_TEAM_LEADER)
+ marine_rk = "tl"
+ if(JOB_SQUAD_LEADER)
+ marine_rk = "leader"
if(marine_rk)
var/image/I = image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad")
diff --git a/code/datums/keybinding/communication.dm b/code/datums/keybinding/communication.dm
index 4164198d48..e1ba0ab5a3 100644
--- a/code/datums/keybinding/communication.dm
+++ b/code/datums/keybinding/communication.dm
@@ -32,7 +32,7 @@
/datum/keybinding/client/communication/whisper
hotkey_keys = list("Unbound")
classic_keys = list("Unbound")
- name = "Whisper"
+ name = WHISPER_CHANNEL
full_name = "IC Whisper"
keybind_signal = COMSIG_KB_CLIENT_WHISPER_DOWN
@@ -56,4 +56,4 @@
name = MENTOR_CHANNEL
full_name = "Mentor Say"
description = "Talk with other mentors."
- keybind_signal = COMSIG_KB_ADMIN_ASAY_DOWN
+ keybind_signal = COMSIG_KB_ADMIN_MENTORSAY_DOWN
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index d1115eb533..4fe098f9fb 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -35,8 +35,11 @@
var/list/filter_data //For handling persistent filters
- // Base transform matrix
- var/matrix/base_transform = null
+ /// Base transform matrix, edited by admin tooling and such
+ var/matrix/base_transform
+ /// Last transform used before being compound with base_transform
+ /// This allows us to re-create transform if only base_transform changes
+ var/matrix/raw_transform
///Chemistry.
var/datum/reagents/reagents = null
@@ -118,7 +121,14 @@ directive is properly returned.
//===========================================================================
-
+// TODO make all atoms use set_density, do not rely on it at present
+///Setter for the `density` variable to append behavior related to its changing.
+/atom/proc/set_density(new_value)
+ SHOULD_CALL_PARENT(TRUE)
+ if(density == new_value)
+ return
+ . = density
+ density = new_value
//atmos procs
@@ -143,15 +153,27 @@ directive is properly returned.
if(loc)
return loc.return_gas()
-// Updates the atom's transform
-/atom/proc/apply_transform(matrix/M)
- if(!base_transform)
- transform = M
- return
+/// Updates the atom's transform compounding it with [/atom/var/base_transform]
+/atom/proc/apply_transform(matrix/new_transform, time = 0, easing = (EASE_IN|EASE_OUT))
+ var/matrix/base_copy
+ if(base_transform)
+ base_copy = matrix(base_transform)
+ else
+ base_copy = matrix()
+ raw_transform = matrix(new_transform) // Keep a copy to replay if needed
- var/matrix/base_copy = matrix(base_transform)
// Compose the base and applied transform in that order
- transform = base_copy.Multiply(M)
+ var/matrix/complete = base_copy.Multiply(raw_transform)
+
+ if(!time)
+ transform = complete
+ return
+ animate(src, transform = complete, time = time, easing = easing)
+
+/// Upates the base_transform which will be compounded with other transforms
+/atom/proc/update_base_transform(matrix/new_transform, time = 0)
+ base_transform = matrix(new_transform)
+ apply_transform(raw_transform, time)
/atom/proc/on_reagent_change()
return
@@ -185,7 +207,9 @@ directive is properly returned.
return
/atom/proc/emp_act(severity)
- return
+ SHOULD_CALL_PARENT(TRUE)
+
+ SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity)
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
@@ -225,8 +249,8 @@ directive is properly returned.
if(!examine_strings)
log_debug("Attempted to create an examine block with no strings! Atom : [src], user : [user]")
return
- to_chat(user, examine_block(examine_strings.Join("\n")))
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, examine_strings)
+ to_chat(user, examine_block(examine_strings.Join("\n")))
/atom/proc/get_examine_text(mob/user)
. = list()
@@ -703,7 +727,6 @@ Parameters are passed from New.
var/result = tgui_input_list(usr, "Choose the transformation to apply","Transform Mod", list("Scale","Translate","Rotate"))
if(!result)
return
- var/matrix/M = transform
if(!result)
return
switch(result)
@@ -712,18 +735,21 @@ Parameters are passed from New.
var/y = tgui_input_real_number(usr, "Choose y mod","Transform Mod")
if(isnull(x) || isnull(y))
return
- transform = M.Scale(x,y)
+ var/matrix/base_matrix = matrix(base_transform)
+ update_base_transform(base_matrix.Scale(x,y))
if("Translate")
var/x = tgui_input_real_number(usr, "Choose x mod (negative = left, positive = right)","Transform Mod")
var/y = tgui_input_real_number(usr, "Choose y mod (negative = down, positive = up)","Transform Mod")
if(isnull(x) || isnull(y))
return
- transform = M.Translate(x,y)
+ var/matrix/base_matrix = matrix(base_transform)
+ update_base_transform(base_matrix.Translate(x,y))
if("Rotate")
var/angle = tgui_input_real_number(usr, "Choose angle to rotate","Transform Mod")
if(isnull(angle))
return
- transform = M.Turn(angle)
+ var/matrix/base_matrix = matrix(base_transform)
+ update_base_transform(base_matrix.Turn(angle))
SEND_SIGNAL(src, COMSIG_ATOM_VV_MODIFY_TRANSFORM)
diff --git a/code/game/jobs/job/marine/squads.dm b/code/game/jobs/job/marine/squads.dm
index 55be638e69..b50d5a2ae0 100644
--- a/code/game/jobs/job/marine/squads.dm
+++ b/code/game/jobs/job/marine/squads.dm
@@ -210,6 +210,17 @@
roundstart = FALSE
locked = TRUE
+/datum/squad/marine/cbrn
+ name = SQUAD_CBRN
+ equipment_color = "#3B2A7B" //Chemical Corps Purple
+ chat_color = "#553EB2"
+ radio_freq = CBRN_FREQ
+ minimap_color = "#3B2A7B"
+
+ active = FALSE
+ roundstart = FALSE
+ locked = TRUE
+
//############################### UPP Squads
/datum/squad/upp
name = "Root"
diff --git a/code/game/jobs/role_authority.dm b/code/game/jobs/role_authority.dm
index 5aab13f68a..1e2e2ac6ed 100644
--- a/code/game/jobs/role_authority.dm
+++ b/code/game/jobs/role_authority.dm
@@ -524,9 +524,7 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou
new_human.sec_hud_set_ID()
new_human.hud_set_squad()
- for(var/obj/current_item in new_human.get_contents())
- SEND_SIGNAL(current_item, COMSIG_POST_SPAWN_UPDATE, new_human)
-
+ SEND_SIGNAL(new_human, COMSIG_POST_SPAWN_UPDATE)
SSround_recording.recorder.track_player(new_human)
//Find which squad has the least population. If all 4 squads are equal it should just use a random one
diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm
index 0af38cacd7..1c240fb05d 100644
--- a/code/game/machinery/atmoalter/scrubber.dm
+++ b/code/game/machinery/atmoalter/scrubber.dm
@@ -18,16 +18,14 @@
PF.flags_can_pass_all = PASS_OVER|PASS_AROUND|PASS_UNDER
/obj/structure/machinery/portable_atmospherics/powered/scrubber/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
if(prob(50/severity))
on = !on
update_icon()
- ..(severity)
-
/obj/structure/machinery/portable_atmospherics/powered/scrubber/update_icon()
src.overlays = 0
diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm
index 116753093f..912a607032 100644
--- a/code/game/machinery/bots/bots.dm
+++ b/code/game/machinery/bots/bots.dm
@@ -114,6 +114,7 @@
/obj/structure/machinery/bot/emp_act(severity)
+ . = ..()
var/was_on = on
stat |= EMPED
new /obj/effect/overlay/temp/emp_sparks (loc)
diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm
index d82591994e..b88f824956 100644
--- a/code/game/machinery/bots/mulebot.dm
+++ b/code/game/machinery/bots/mulebot.dm
@@ -916,11 +916,11 @@
post_signal_multiple(control_freq, kv)
/obj/structure/machinery/bot/mulebot/emp_act(severity)
+ . = ..()
if (cell)
cell.emp_act(severity)
if(load)
load.emp_act(severity)
- ..()
/obj/structure/machinery/bot/mulebot/explode()
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index d416c561fc..3b2a91eea3 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -35,6 +35,9 @@
var/colony_camera_mapload = TRUE
+ /// If this camera should have innate EMP-proofing
+ var/emp_proof = FALSE
+
/obj/structure/machinery/camera/Initialize(mapload, ...)
. = ..()
WireColorToFlag = randomCameraWires()
@@ -72,6 +75,7 @@
if(WEST) pixel_x = 27
/obj/structure/machinery/camera/emp_act(severity)
+ . = ..()
if(!isEmpProof())
if(prob(100/severity))
icon_state = "[initial(icon_state)]emp"
@@ -89,7 +93,6 @@
if(can_use())
cameranet.addCamera(src)
kick_viewers()
- ..()
/obj/structure/machinery/camera/ex_act(severity)
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index b075808652..fc23282581 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -30,6 +30,7 @@
network = list(CAMERA_NET_LASER_TARGETS)
unslashable = TRUE
unacidable = TRUE
+ emp_proof = TRUE
/obj/structure/machinery/camera/laser_cam/Initialize(mapload, laser_name)
. = ..()
@@ -37,8 +38,6 @@
var/area/A = get_area(src)
c_tag = "[laser_name] ([A.name])"
-/obj/structure/machinery/camera/laser_cam/emp_act(severity)
- return //immune to EMPs, just in case
/obj/structure/machinery/camera/laser_cam/ex_act()
return
@@ -132,9 +131,7 @@
invisibility = 101 //fuck you init()
colony_camera_mapload = FALSE
-
-/obj/structure/machinery/camera/autoname/lz_camera/emp_act(severity)
- return //immune to EMPs, just in case
+ emp_proof = TRUE
/obj/structure/machinery/camera/autoname/lz_camera/ex_act()
return
@@ -144,7 +141,7 @@
/obj/structure/machinery/camera/proc/isEmpProof()
var/O = locate(/obj/item/stack/sheet/mineral/osmium) in assembly.upgrades
- return O
+ return O || emp_proof
/obj/structure/machinery/camera/proc/isXRay()
var/obj/item/stock_parts/scanning_module/O = locate(/obj/item/stock_parts/scanning_module) in assembly.upgrades
diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm
index fcd8c65e93..eb7a501fa0 100644
--- a/code/game/machinery/cell_charger.dm
+++ b/code/game/machinery/cell_charger.dm
@@ -80,11 +80,11 @@
return
/obj/structure/machinery/cell_charger/emp_act(severity)
+ . = ..()
if(inoperable())
return
if(charging)
charging.emp_act(severity)
- ..(severity)
/obj/structure/machinery/cell_charger/process()
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index 8d35dd1b6a..1ac5a06738 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -165,8 +165,8 @@
return
/obj/structure/machinery/computer/arcade/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
var/empprize = null
var/num_of_prizes = 0
@@ -178,5 +178,3 @@
for(num_of_prizes; num_of_prizes > 0; num_of_prizes--)
empprize = pickweight(prizes)
new empprize(src.loc)
-
- ..(severity)
diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm
index b5cfc856b0..b78f306162 100644
--- a/code/game/machinery/computer/camera_console.dm
+++ b/code/game/machinery/computer/camera_console.dm
@@ -354,8 +354,8 @@
exproof = TRUE
colony_camera_mapload = FALSE
-/obj/structure/machinery/computer/cameras/mortar/emp_act(severity)
- return FALSE
+/obj/structure/machinery/computer/cameras/mortar/set_broken()
+ return
/obj/structure/machinery/computer/cameras/dropship
name = "abstract dropship camera computer"
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index 304b24a14f..c335177962 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -31,8 +31,9 @@
return 1
/obj/structure/machinery/computer/emp_act(severity)
- if(prob(20/severity)) set_broken()
- ..()
+ . = ..()
+ if(prob(20/severity))
+ set_broken()
/obj/structure/machinery/computer/ex_act(severity)
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index fe85599018..53efad3da8 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -483,8 +483,8 @@
return
/obj/structure/machinery/computer/med_data/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
for(var/datum/data/record/R as anything in GLOB.data_core.medical)
@@ -509,8 +509,6 @@
qdel(R)
continue
- ..(severity)
-
/obj/structure/machinery/computer/med_data/laptop
name = "Medical Laptop"
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 8571189379..2d9a4a1dbe 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -524,8 +524,8 @@ What a mess.*/
return selection.fields["img"]
/obj/structure/machinery/computer/secure_data/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
for(var/datum/data/record/R in GLOB.data_core.security)
@@ -550,8 +550,6 @@ What a mess.*/
qdel(R)
continue
- ..(severity)
-
/obj/structure/machinery/computer/secure_data/detective_computer
icon = 'icons/obj/structures/machinery/computer.dmi'
icon_state = "messyfiles"
diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm
index a20d344b53..60b5aa2329 100644
--- a/code/game/machinery/computer/skills.dm
+++ b/code/game/machinery/computer/skills.dm
@@ -347,8 +347,8 @@ What a mess.*/
return
/obj/structure/machinery/computer/skills/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
for(var/datum/data/record/R in GLOB.data_core.security)
@@ -373,4 +373,3 @@ What a mess.*/
qdel(R)
continue
- ..(severity)
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 99996bea89..687882d9d7 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -65,6 +65,7 @@
return
/obj/structure/machinery/deployable/barrier/emp_act(severity)
+ . = ..()
if(inoperable())
return
if(prob(50/severity))
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index b7257ff4e7..c6d9ddf3ef 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -866,3 +866,8 @@ GLOBAL_LIST_INIT(airlock_wire_descriptions, list(
var/damage = xeno.melee_damage_upper * TAILSTAB_AIRLOCK_DAMAGE_MULTIPLIER
take_damage(damage, xeno)
return TAILSTAB_COOLDOWN_NORMAL
+
+/obj/structure/machinery/door/airlock/autoclose()
+ if(locked)
+ return
+ ..()
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index b13138cd7c..7796ad74df 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -138,7 +138,6 @@
opacity = 0
assembly_type = /obj/structure/airlock_assembly/airlock_assembly_research
glass = 1
- heat_proof = 1
req_one_access = list(ACCESS_CIVILIAN_RESEARCH, ACCESS_CIVILIAN_COMMAND, ACCESS_WY_COLONIAL)
/obj/structure/machinery/door/airlock/glass_mining/colony
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index a7d86d629b..99353a6688 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -1,4 +1,3 @@
-
/obj/structure/machinery/door
name = "\improper Door"
desc = "It opens and closes."
@@ -7,50 +6,43 @@
anchored = TRUE
opacity = TRUE
density = TRUE
- throwpass = 0
+ throwpass = FALSE
layer = DOOR_OPEN_LAYER
minimap_color = MINIMAP_DOOR
var/open_layer = DOOR_OPEN_LAYER
var/closed_layer = DOOR_CLOSED_LAYER
var/id = ""
+ var/width = 1
var/secondsElectrified = 0
- var/visible = 1
+ var/visible = TRUE
var/panel_open = FALSE
- var/operating = 0
- var/autoclose = 0
- var/glass = 0
- var/normalspeed = 1
- var/openspeed = 10 //How many seconds does it take to open it? Default 1 second. Use only if you have long door opening animations
- var/heat_proof = 0 // For glass airlocks/opacity firedoors
- var/air_properties_vary_with_direction = 0
- var/turf/filler //Fixes double door opacity issue
+ var/operating = FALSE
+ var/autoclose = FALSE
+ var/glass = FALSE
+ /// If FALSE it speeds up the autoclosing timing.
+ var/normalspeed = TRUE
+ /// Time to open/close airlock, default is 1 second.
+ var/openspeed = 1 SECONDS
+ /// Fixes multi_tile doors opacity issues.
+ var/list/filler_turfs = list() //Previously this was just var, because no one had forseen someone creating doors more than 2 tiles wide
/// Stops it being forced open through normal means (Hunters/Zombies/Aliens).
var/heavy = FALSE
/// Resistance to masterkey
var/masterkey_resist = FALSE
var/masterkey_mod = 0.1
-
-
- //Multi-tile doors
- dir = EAST
- var/width = 1
+ dir = EAST //So multitile doors are directioned properly
/obj/structure/machinery/door/Initialize(mapload, ...)
. = ..()
- if(density)
- layer = closed_layer
- update_flags_heat_protection(get_turf(src))
- else
- layer = open_layer
-
+ layer = density ? closed_layer : open_layer
handle_multidoor()
/obj/structure/machinery/door/Destroy()
. = ..()
- if(filler && width > 1)
- filler.set_opacity(0)// Ehh... let's hope there are no walls there. Must fix this
- filler = null
+ if(length(filler_turfs) && width > 1)
+ change_filler_opacity(0) // It still doesn't check for walls, might want to add checking that in the future
+ filler_turfs = null
density = FALSE
/obj/structure/machinery/door/initialize_pass_flags(datum/pass_flags_container/PF)
@@ -58,21 +50,41 @@
if (PF)
PF.flags_can_pass_all = NONE
+/// Also refreshes filler_turfs list.
+/obj/structure/machinery/door/proc/change_filler_opacity(new_opacity)
+ // I have no idea why do we null opacity first before... changing it
+ for(var/turf/filler_turf as anything in filler_turfs)
+ filler_turf.set_opacity(null)
+
+ filler_turfs = list()
+ for(var/turf/filler as anything in locate_filler_turfs())
+ filler.set_opacity(new_opacity)
+ filler_turfs += filler
+
+/// Updates collision box and opacity of multi_tile airlocks.
/obj/structure/machinery/door/proc/handle_multidoor()
if(width > 1)
if(dir in list(EAST, WEST))
bound_width = width * world.icon_size
bound_height = world.icon_size
- filler = get_step(src,EAST)
- filler.set_opacity(opacity)
else
bound_width = world.icon_size
bound_height = width * world.icon_size
- filler = get_step(src,NORTH)
- filler.set_opacity(opacity)
+ change_filler_opacity(opacity)
+
+/// Finds turfs which should be filler ones.
+/obj/structure/machinery/door/proc/locate_filler_turfs()
+ var/turf/filler_temp
+ var/list/located_turfs = list()
-//process()
- //return
+ for(var/i in 1 to width - 1)
+ if (dir in list(EAST, WEST))
+ filler_temp = locate(x + i, y, z)
+ else
+ filler_temp = locate(x, y + i, z)
+ if (filler_temp)
+ located_turfs += filler_temp
+ return located_turfs
/obj/structure/machinery/door/proc/borders_space()
for(var/turf/target in range(1, src))
@@ -81,7 +93,8 @@
return FALSE
/obj/structure/machinery/door/Collided(atom/movable/AM)
- if(panel_open || operating) return
+ if(panel_open || operating)
+ return
if(ismob(AM))
var/mob/M = AM
if(world.time - M.last_bumped <= openspeed) return //Can bump-open one airlock per second. This is to prevent shock spam.
@@ -89,12 +102,10 @@
if(!M.is_mob_restrained() && M.mob_size > MOB_SIZE_SMALL)
bumpopen(M)
return
-
if(istype(AM, /obj))
var/obj/O = AM
if(O.buckled_mob)
Collided(O.buckled_mob)
-
if(istype(AM, /obj/structure/machinery/bot))
var/obj/structure/machinery/bot/bot = AM
if(src.check_access(bot.botcard))
@@ -102,16 +113,17 @@
open()
return
-
/obj/structure/machinery/door/proc/bumpopen(mob/user as mob)
- if(operating) return
- src.add_fingerprint(user)
- if(!src.requiresID())
+ if(operating)
+ return
+ add_fingerprint(user)
+ if(!requiresID())
user = null
-
if(density)
- if(allowed(user)) open()
- else flick("door_deny", src)
+ if(allowed(user))
+ open()
+ else
+ flick("door_deny", src)
return
/obj/structure/machinery/door/attack_remote(mob/user)
@@ -124,9 +136,7 @@
add_fingerprint(user)
if(operating)
return
- if(!Adjacent(user))
- user = null //so allowed(user) always succeeds
- if(!requiresID())
+ if(!Adjacent(user) || !requiresID())
user = null //so allowed(user) always succeeds
if(allowed(user))
if(density)
@@ -137,64 +147,56 @@
if(density)
flick("door_deny", src)
-
/obj/structure/machinery/door/attackby(obj/item/I, mob/user)
if(!(I.flags_item & NOBLUDGEON))
try_to_activate_door(user)
- return 1
+ return TRUE
/obj/structure/machinery/door/emp_act(severity)
- if(prob(20/severity) && (istype(src,/obj/structure/machinery/door/airlock) || istype(src,/obj/structure/machinery/door/window)) )
+ . = ..()
+ if(prob(20/severity) && use_power)
open()
if(prob(40/severity))
if(secondsElectrified == 0)
secondsElectrified = -1
spawn(30 SECONDS)
secondsElectrified = 0
- ..()
-
/obj/structure/machinery/door/ex_act(severity)
- if(unacidable) return
+ if(unacidable)
+ return
if(density)
switch(severity)
if(0 to EXPLOSION_THRESHOLD_LOW)
if(prob(80))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(2, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread
+ spark.set_up(2, 1, src)
+ spark.start()
if(EXPLOSION_THRESHOLD_LOW to INFINITY)
qdel(src)
else
switch(severity)
if(0 to EXPLOSION_THRESHOLD_MEDIUM)
if(prob(80))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(2, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread
+ spark.set_up(2, 1, src)
+ spark.start()
else
qdel(src)
return
-
/obj/structure/machinery/door/get_explosion_resistance()
if(density)
if(unacidable)
- return 1000000
+ return 1000000 //Used for negation of explosions, should probably be made into define in the future
else
return EXPLOSION_THRESHOLD_LOW //this should exactly match the amount of damage needed to destroy the door
else
return 0
-
/obj/structure/machinery/door/update_icon()
- if(density)
- icon_state = "door1"
- else
- icon_state = "door0"
- return
-
+ icon_state = density ? "door1" : "door0"
/obj/structure/machinery/door/proc/do_animate(animation)
switch(animation)
@@ -212,7 +214,6 @@
flick("door_deny", src)
return
-
/obj/structure/machinery/door/proc/open(forced=0)
if(!density)
return TRUE
@@ -223,8 +224,8 @@
do_animate("opening")
icon_state = "door0"
set_opacity(FALSE)
- if(filler)
- filler.set_opacity(opacity)
+ if(length(filler_turfs))
+ change_filler_opacity(opacity)
addtimer(CALLBACK(src, PROC_REF(finish_open)), openspeed)
return TRUE
@@ -235,11 +236,9 @@
if(operating)
operating = FALSE
-
if(autoclose)
addtimer(CALLBACK(src, PROC_REF(autoclose)), normalspeed ? 150 + openspeed : 5)
-
/obj/structure/machinery/door/proc/close()
if(density)
return TRUE
@@ -256,22 +255,19 @@
update_icon()
if(visible && !glass)
set_opacity(TRUE)
- if(filler)
- filler.set_opacity(opacity)
+ if(length(filler_turfs))
+ change_filler_opacity(opacity)
operating = FALSE
/obj/structure/machinery/door/proc/requiresID()
return TRUE
-
-/obj/structure/machinery/door/proc/update_flags_heat_protection(turf/source)
-
-
+/// Used for overriding in airlocks
/obj/structure/machinery/door/proc/autoclose()
- var/obj/structure/machinery/door/airlock/A = src
- if(!A.density && !A.operating && !A.locked && !A.welded && A.autoclose)
+ if(!autoclose)
+ return
+ if(!density && !operating)
close()
- return
/obj/structure/machinery/door/Move(new_loc, new_dir)
. = ..()
@@ -279,15 +275,10 @@
if(dir in list(EAST, WEST))
bound_width = width * world.icon_size
bound_height = world.icon_size
- filler.set_opacity(0)
- filler = (get_step(src,EAST)) //Find new turf
- filler.set_opacity(opacity)
else
bound_width = world.icon_size
bound_height = width * world.icon_size
- filler.set_opacity(0)
- filler = (get_step(src,NORTH)) //Find new turf
- filler.set_opacity(opacity)
+ change_filler_opacity(opacity)
/obj/structure/machinery/door/attack_animal(mob/living/user)
. = ..()
@@ -318,6 +309,10 @@
return TRUE
+/obj/structure/machinery/door/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation)
+ . = ..()
+ // Yes, for a split second after departure you can see through rear dropship airlocks, but it's the simplest solution I could've think of
+ handle_multidoor()
/obj/structure/machinery/door/morgue
icon = 'icons/obj/structures/doors/doormorgue.dmi'
diff --git a/code/game/machinery/doors/multi_tile.dm b/code/game/machinery/doors/multi_tile.dm
index 65ecd67a43..ca21812816 100644
--- a/code/game/machinery/doors/multi_tile.dm
+++ b/code/game/machinery/doors/multi_tile.dm
@@ -13,7 +13,6 @@
/obj/structure/machinery/door/airlock/multi_tile/Initialize()
. = ..()
- handle_multidoor()
update_icon()
/obj/structure/machinery/door/airlock/multi_tile/glass
@@ -137,7 +136,6 @@
/obj/structure/window/framed/almayer,
/obj/structure/machinery/door/airlock,
)
- var/multi_filler = list()
/obj/structure/machinery/door/airlock/multi_tile/almayer/Initialize()
. = ..()
@@ -233,42 +231,6 @@
req_access = null
req_one_access = list(ACCESS_CIVILIAN_BRIG, ACCESS_CIVILIAN_COMMAND, ACCESS_WY_COLONIAL)
-/obj/structure/machinery/door/airlock/multi_tile/almayer/handle_multidoor()
- . = ..()
- if(!(width > 1)) return //Bubblewrap
-
- update_filler_turfs()
-
-//We have to find these again since these doors are used on shuttles a lot so the turfs changes
-/obj/structure/machinery/door/airlock/multi_tile/almayer/proc/update_filler_turfs()
- for(var/turf/T in multi_filler)
- T.set_opacity(null)
-
- multi_filler = list()
- for(var/turf/T in get_filler_turfs())
- T.set_opacity(opacity)
- multi_filler += list(T)
-
-/obj/structure/machinery/door/airlock/multi_tile/proc/get_filler_turfs()
- . = list()
- for(var/i = 1, i < width, i++)
- if(dir in list(NORTH, SOUTH))
- var/turf/T = locate(x, y + i, z)
- if(T)
- . += list(T)
- else if(dir in list(EAST, WEST))
- var/turf/T = locate(x + i, y, z)
- if(T)
- . += list(T)
-
-/obj/structure/machinery/door/airlock/multi_tile/almayer/open()
- . = ..()
- update_filler_turfs()
-
-/obj/structure/machinery/door/airlock/multi_tile/almayer/close()
- . = ..()
- update_filler_turfs()
-
//------Dropship Cargo Doors -----//
/obj/structure/machinery/door/airlock/multi_tile/almayer/dropshiprear
diff --git a/code/game/machinery/doors/runed_sandstone.dm b/code/game/machinery/doors/runed_sandstone.dm
index 4bf66dfdc8..a6de7348dd 100644
--- a/code/game/machinery/doors/runed_sandstone.dm
+++ b/code/game/machinery/doors/runed_sandstone.dm
@@ -110,8 +110,8 @@
density = FALSE
update_icon()
set_opacity(0)
- if(filler)
- filler.set_opacity(opacity)
+ if(length(filler_turfs))
+ change_filler_opacity(opacity)
if(operating)
operating = FALSE
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 2b57fbd0a4..bd544c5c3f 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -11,7 +11,6 @@
flags_atom = ON_BORDER
opacity = FALSE
var/obj/item/circuitboard/airlock/electronics = null
- air_properties_vary_with_direction = 1
/obj/structure/machinery/door/window/Initialize()
. = ..()
diff --git a/code/game/machinery/fire_alarm.dm b/code/game/machinery/fire_alarm.dm
index fe1f80646c..dd7e0ee701 100644
--- a/code/game/machinery/fire_alarm.dm
+++ b/code/game/machinery/fire_alarm.dm
@@ -63,8 +63,9 @@ FIRE ALARM
return src.alarm()
/obj/structure/machinery/firealarm/emp_act(severity)
- if(prob(50/severity)) alarm()
- ..()
+ . = ..()
+ if(prob(50/severity))
+ alarm()
/obj/structure/machinery/firealarm/attackby(obj/item/held_object as obj, mob/user as mob)
src.add_fingerprint(user)
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index 75d0de56de..cd59862a2e 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -84,12 +84,11 @@
/obj/structure/machinery/flasher/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
if(prob(75/severity))
flash()
- ..(severity)
/obj/structure/machinery/flasher/portable/HasProximity(atom/movable/AM as mob|obj)
if ((src.disable) || (src.last_flash && world.time < src.last_flash + 150))
diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm
index 33f75c50e3..d5a0505fca 100644
--- a/code/game/machinery/igniter.dm
+++ b/code/game/machinery/igniter.dm
@@ -105,11 +105,10 @@
return 1
/obj/structure/machinery/sparker/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
ignite()
- ..(severity)
/obj/structure/machinery/ignition_switch/attack_remote(mob/user as mob)
return attack_hand(user)
diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm
index 66eb038671..de61830c25 100644
--- a/code/game/machinery/lightswitch.dm
+++ b/code/game/machinery/lightswitch.dm
@@ -62,8 +62,7 @@
updateicon()
/obj/structure/machinery/light_switch/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
power_change()
- ..(severity)
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 4ce295aa99..66bf08afba 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -175,10 +175,10 @@ Class Procs:
. += SPAN_WARNING("[msg]")
/obj/structure/machinery/emp_act(severity)
+ . = ..()
if(use_power && stat == 0)
use_power(7500/severity)
new /obj/effect/overlay/temp/emp_sparks (loc)
- ..()
/obj/structure/machinery/ex_act(severity)
diff --git a/code/game/machinery/medical_pod/sleeper.dm b/code/game/machinery/medical_pod/sleeper.dm
index 35d9a44863..bf2abe246c 100644
--- a/code/game/machinery/medical_pod/sleeper.dm
+++ b/code/game/machinery/medical_pod/sleeper.dm
@@ -332,14 +332,13 @@
/obj/structure/machinery/medical_pod/sleeper/emp_act(severity)
+ . = ..()
if(filtering)
toggle_filter()
if(inoperable())
- ..(severity)
return
if(occupant)
go_out()
- ..()
/obj/structure/machinery/medical_pod/sleeper/proc/toggle_filter()
if(!occupant)
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index c75360e075..72e311c6d8 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -218,20 +218,14 @@
update_icon()
/obj/structure/machinery/recharger/emp_act(severity)
+ . = ..()
if(inoperable() || !anchored)
- ..(severity)
return
-/*
- if(istype(charging, /obj/item/weapon/gun/energy))
- var/obj/item/weapon/gun/energy/E = charging
- if(E.power_supply)
- E.power_supply.emp_act(severity)
-*/
+
if(istype(charging, /obj/item/weapon/baton))
var/obj/item/weapon/baton/B = charging
if(B.bcell)
B.bcell.charge = 0
- ..(severity)
/obj/structure/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
src.overlays = 0
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 6444021288..56b782cd77 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -109,13 +109,12 @@
return
/obj/structure/machinery/recharge_station/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
if(occupant)
occupant.emp_act(severity)
go_out()
- ..(severity)
/obj/structure/machinery/recharge_station/update_icon()
..()
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index aa51201ae1..dd45ad5978 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -40,12 +40,11 @@
/obj/structure/machinery/space_heater/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
if(cell)
cell.emp_act(severity)
- ..(severity)
/obj/structure/machinery/space_heater/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/cell))
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index c56f8da361..79ead63215 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -188,11 +188,10 @@
var/emotion = "Neutral"
/obj/structure/machinery/ai_status_display/emp_act(severity)
+ . = ..()
if(inoperable())
- ..(severity)
return
set_picture("ai_bsod")
- ..(severity)
/obj/structure/machinery/ai_status_display/proc/update()
if(mode==0) //Blank
diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm
index d93304c663..510d488bb3 100644
--- a/code/game/machinery/telecomms/presets.dm
+++ b/code/game/machinery/telecomms/presets.dm
@@ -449,8 +449,7 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
id = "CentComm Receiver"
network = "tcommsat"
autolinkers = list("receiverCent")
- freq_listening = list(WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ)
-
+ freq_listening = list(WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ, CBRN_FREQ)
//Buses
@@ -469,7 +468,7 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
/obj/structure/machinery/telecomms/bus/preset_three
id = "Bus 3"
network = "tcommsat"
- freq_listening = list(SEC_FREQ, COMM_FREQ, WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, JTAC_FREQ, INTEL_FREQ, WY_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ)
+ freq_listening = list(SEC_FREQ, COMM_FREQ, WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, JTAC_FREQ, INTEL_FREQ, WY_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ, CBRN_FREQ)
autolinkers = list("processor3", "security", "command", "JTAC")
/obj/structure/machinery/telecomms/bus/preset_four
@@ -485,7 +484,7 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
/obj/structure/machinery/telecomms/bus/preset_cent
id = "CentComm Bus"
network = "tcommsat"
- freq_listening = list(WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ)
+ freq_listening = list(WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ, CBRN_FREQ)
autolinkers = list("processorCent", "centcomm")
//Processors
@@ -550,7 +549,7 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
/obj/structure/machinery/telecomms/server/presets/command
id = "Command Server"
- freq_listening = list(COMM_FREQ, WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, JTAC_FREQ, INTEL_FREQ, WY_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ)
+ freq_listening = list(COMM_FREQ, WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, JTAC_FREQ, INTEL_FREQ, WY_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ, CBRN_FREQ)
autolinkers = list("command")
/obj/structure/machinery/telecomms/server/presets/engineering
@@ -565,10 +564,9 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
/obj/structure/machinery/telecomms/server/presets/centcomm
id = "CentComm Server"
- freq_listening = list(WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ)
+ freq_listening = list(WY_WO_FREQ, PMC_FREQ, DUT_FREQ, YAUT_FREQ, HC_FREQ, PVST_FREQ, SOF_FREQ, CBRN_FREQ)
autolinkers = list("centcomm")
-
//Broadcasters
//--PRESET LEFT--//
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index 255d70f458..8b8b12dfd1 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -93,13 +93,13 @@ GLOBAL_LIST_EMPTY_TYPED(telecomms_list, /obj/structure/machinery/telecomms)
update_state()
/obj/structure/machinery/telecomms/emp_act(severity)
+ . = ..()
if(prob(100/severity))
if(!(stat & EMPED))
stat |= EMPED
var/duration = (300 * 10)/severity
spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot.
stat &= ~EMPED
- ..()
/*
The receiver idles and receives messages from subspace-compatible radio equipment;
diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm
index 56a4429146..a7013a0249 100644
--- a/code/game/machinery/vending/cm_vending.dm
+++ b/code/game/machinery/vending/cm_vending.dm
@@ -140,9 +140,9 @@ GLOBAL_LIST_EMPTY(vending_products)
if(vend_dir_whitelist)
var/user_dir = get_dir(loc, user)
if(!(user_dir in vend_dir_whitelist))
- return turf
+ return get_turf(user)
var/turf/relative_turf = get_step(user, vend_dir)
- if(relative_turf.Adjacent(src))
+ if(relative_turf)
return relative_turf
return turf
diff --git a/code/game/machinery/vending/vendor_types/crew/synthetic.dm b/code/game/machinery/vending/vendor_types/crew/synthetic.dm
index bf96246e92..c912ae8ce3 100644
--- a/code/game/machinery/vending/vendor_types/crew/synthetic.dm
+++ b/code/game/machinery/vending/vendor_types/crew/synthetic.dm
@@ -102,7 +102,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_synth, list(
list("Surgical Drop Pouch", 0, /obj/item/clothing/accessory/storage/surg_vest/drop_green, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Surgical Drop Pouch (Blue)", 0, /obj/item/clothing/accessory/storage/surg_vest/drop_blue, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Surgical Drop Pouch (Black)", 0, /obj/item/clothing/accessory/storage/surg_vest/drop_black, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
- list("Tool Webbing", 0, /obj/item/clothing/accessory/storage/black_vest/tool_webbing, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
+ list("Tool Webbing", 0, /obj/item/clothing/accessory/storage/tool_webbing/equipped, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Drop Pouch", 0, /obj/item/clothing/accessory/storage/droppouch, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("SHOES (CHOOSE 1)", 0, null, null, null),
diff --git a/code/game/objects/items/circuitboards/robot_modules.dm b/code/game/objects/items/circuitboards/robot_modules.dm
index 2e51853538..04fcff10fa 100644
--- a/code/game/objects/items/circuitboards/robot_modules.dm
+++ b/code/game/objects/items/circuitboards/robot_modules.dm
@@ -9,13 +9,12 @@
var/list/stacktypes
/obj/item/circuitboard/robot_module/emp_act(severity)
+ . = ..()
if(modules)
for(var/obj/O in modules)
O.emp_act(severity)
if(emag)
emag.emp_act(severity)
- ..()
- return
/obj/item/circuitboard/robot_module/Initialize()
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 0a7709aa61..33a93ed18d 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -155,6 +155,7 @@
do_flash(user = user, aoe = TRUE)
/obj/item/device/flash/emp_act(severity)
+ . = ..()
if(broken) return
switch(flashes_stored)
if(0 to 5)
@@ -168,7 +169,6 @@
if(M.flash_eyes())
M.apply_effect(10, WEAKEN)
M.visible_message(SPAN_DISARM("[M] is blinded by \the [src]!"))
- ..()
/obj/item/device/flash/synthetic
name = "synthetic flash"
diff --git a/code/game/objects/items/devices/portable_vendor.dm b/code/game/objects/items/devices/portable_vendor.dm
index 65e2128a02..29e1d06018 100644
--- a/code/game/objects/items/devices/portable_vendor.dm
+++ b/code/game/objects/items/devices/portable_vendor.dm
@@ -210,6 +210,7 @@
s.start()
/obj/item/device/portable_vendor/emp_act(severity)
+ . = ..()
if (broken)
return
if (prob(40*severity))
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index d34444487f..c3320e031e 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -863,6 +863,14 @@
"Corporate Liaison" = TRACKER_CL
)
+/obj/item/device/radio/headset/distress/cbrn
+ name = "\improper CBRN headset"
+ desc = "A headset given to CBRN marines. Channels are as follows: :g - public, :v - marine command, :a - alpha squad, :b - bravo squad, :c - charlie squad, :d - delta squad, :n - engineering, :m - medbay, :u - requisitions, :j - JTAC, :t - intel"
+ frequency = CBRN_FREQ
+ initial_keys = list(/obj/item/device/encryptionkey/public, /obj/item/device/encryptionkey/mcom)
+ ignore_z = TRUE
+ has_hud = TRUE
+
/obj/item/device/radio/headset/distress/pmc/hvh
desc = "A special headset used by corporate personnel. Channels are as follows: :o - colony."
initial_keys = list(/obj/item/device/encryptionkey/colony, /obj/item/device/encryptionkey/WY)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 2092ffa108..c503edc8f9 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -425,11 +425,11 @@
else return
/obj/item/device/radio/emp_act(severity)
+ . = ..()
broadcasting = FALSE
listening = FALSE
for (var/ch_name in channels)
channels[ch_name] = 0
- ..()
///////////////////////////////
//////////Borg Radios//////////
diff --git a/code/game/objects/items/explosives/mine.dm b/code/game/objects/items/explosives/mine.dm
index 742a5f314c..57dd23bf4e 100644
--- a/code/game/objects/items/explosives/mine.dm
+++ b/code/game/objects/items/explosives/mine.dm
@@ -42,6 +42,7 @@
prime() //We don't care about how strong the explosion was.
/obj/item/explosive/mine/emp_act()
+ . = ..()
prime() //Same here. Don't care about the effect strength.
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index 71fb02cf3f..2137b41d86 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -29,20 +29,6 @@
if(!C.handcuffed)
place_handcuffs(C, user)
-/obj/item/handcuffs/obj/structure/MouseDrop(mob/living/carbon/human/H)
- var/mob/living/carbon/human/user = usr
- if (!istype(user))
- return
- if (user.stat || get_dist(user, src) > 1 || get_dist(user, H) > 1 || H.lying)
- return
- if (!istype(H))
- return
-
- if(!do_after(user, cuff_delay, INTERRUPT_ALL, BUSY_ICON_HOSTILE, H, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
- return
-
- // TODO: apply handcuffs
-
/obj/item/handcuffs/get_mob_overlay(mob/user_mob, slot)
var/image/ret = ..()
diff --git a/code/game/objects/items/implants/implant.dm b/code/game/objects/items/implants/implant.dm
index d39b7e6754..e7ebe0391f 100644
--- a/code/game/objects/items/implants/implant.dm
+++ b/code/game/objects/items/implants/implant.dm
@@ -82,6 +82,7 @@ Implant Specifics:
"}
return dat
/obj/item/implant/tracking/emp_act(severity)
+ . = ..()
if (malfunction) //no, dawg, you can't malfunction while you are malfunctioning
return
malfunction = MALFUNCTION_TEMPORARY
@@ -216,6 +217,7 @@ Implant Specifics:
"}
return 1
/obj/item/implant/explosive/emp_act(severity)
+ . = ..()
if (malfunction)
return
malfunction = MALFUNCTION_TEMPORARY
@@ -307,6 +309,7 @@ the implant may become unstable and either pre-maturely inject the subject or si
return
/obj/item/implant/chem/emp_act(severity)
+ . = ..()
if (malfunction)
return
malfunction = MALFUNCTION_TEMPORARY
@@ -432,6 +435,7 @@ the implant may become unstable and either pre-maturely inject the subject or si
STOP_PROCESSING(SSobj, src)
/obj/item/implant/death_alarm/emp_act(severity) //for some reason alarms stop going off in case they are emp'd, even without this
+ . = ..()
if (malfunction) //so I'm just going to add a meltdown chance here
return
malfunction = MALFUNCTION_TEMPORARY
diff --git a/code/game/objects/items/implants/implantneurostim.dm b/code/game/objects/items/implants/implantneurostim.dm
index a46cc31a2c..544cf70147 100644
--- a/code/game/objects/items/implants/implantneurostim.dm
+++ b/code/game/objects/items/implants/implantneurostim.dm
@@ -105,6 +105,7 @@
/obj/item/implant/neurostim/emp_act(severity)
+ . = ..()
if (malfunction)
return
if (prob(80))
diff --git a/code/game/objects/items/props/helmetgarb.dm b/code/game/objects/items/props/helmetgarb.dm
index 72c49a0ea1..b20c567150 100644
--- a/code/game/objects/items/props/helmetgarb.dm
+++ b/code/game/objects/items/props/helmetgarb.dm
@@ -99,10 +99,8 @@
var/nvg_maxhealth = 125
var/nvg_health = 125
- var/nvg_maxcharge = 2500
- var/nvg_charge = 2500
- var/nvg_drain = 8 // has a 5 minute duration but byond may give it a couple of irl time due to lag
- var/infinite_charge = FALSE
+ /// How much charge the cell should have at most. -1 is infinite
+ var/cell_max_charge = 2500
var/activated = FALSE
var/nightvision = FALSE
@@ -117,6 +115,13 @@
var/mob/living/attached_mob
var/lighting_alpha = 100
+/obj/item/prop/helmetgarb/helmet_nvg/Initialize(mapload, ...)
+ . = ..()
+ if(shape != NVG_SHAPE_COSMETIC)
+ AddComponent(/datum/component/cell, cell_max_charge, TRUE, charge_drain = 8)
+ RegisterSignal(src, COMSIG_CELL_TRY_RECHARGING, PROC_REF(cell_try_recharge))
+ RegisterSignal(src, COMSIG_CELL_OUT_OF_CHARGE, PROC_REF(on_power_out))
+
/obj/item/prop/helmetgarb/helmet_nvg/on_enter_storage(obj/item/storage/internal/S)
..()
@@ -139,42 +144,30 @@
/obj/item/prop/helmetgarb/helmet_nvg/attackby(obj/item/A as obj, mob/user as mob)
- if(istype(A,/obj/item/cell))
- recharge(A, user)
-
if(HAS_TRAIT(A, TRAIT_TOOL_SCREWDRIVER))
repair(user)
else
..()
-/obj/item/prop/helmetgarb/helmet_nvg/proc/recharge(obj/item/cell/C, mob/user as mob)
+/obj/item/prop/helmetgarb/helmet_nvg/proc/cell_try_recharge(datum/source, mob/living/user)
+ SIGNAL_HANDLER
+
if(user.action_busy)
- return
+ return COMPONENT_CELL_NO_RECHARGE
+
if(src != user.get_inactive_hand())
- to_chat(user, SPAN_WARNING("You need to hold \the [src] in hand in order to recharge them."))
- return
+ to_chat(user, SPAN_WARNING("You need to hold [src] in hand in order to recharge them."))
+ return COMPONENT_CELL_NO_RECHARGE
+
if(shape == NVG_SHAPE_COSMETIC)
- to_chat(user, SPAN_WARNING("There is no connector for the power cell inside \the [src]."))
- return
+ to_chat(user, SPAN_WARNING("There is no connector for the power cell inside [src]."))
+ return COMPONENT_CELL_NO_RECHARGE
+
if(shape == NVG_SHAPE_BROKEN)
- to_chat(user, SPAN_WARNING("You need to repair \the [src] first."))
- return
- if(nvg_charge == nvg_maxcharge)
- to_chat(user, SPAN_WARNING("\The [src] are already fully charged."))
- return
+ to_chat(user, SPAN_WARNING("You need to repair [src] first."))
+ return COMPONENT_CELL_NO_RECHARGE
- while(nvg_charge < nvg_maxcharge)
- if(C.charge <= 0)
- to_chat(user, SPAN_WARNING("\The [C] is completely dry."))
- break
- if(!do_after(user, 1 SECONDS, (INTERRUPT_ALL & (~INTERRUPT_MOVED)), BUSY_ICON_BUILD, C, INTERRUPT_DIFF_LOC))
- to_chat(user, SPAN_WARNING("You were interrupted."))
- break
- var/to_transfer = min(400, C.charge, (nvg_maxcharge - nvg_charge))
- if(C.use(to_transfer))
- nvg_charge += to_transfer
- to_chat(user, "You transfer some power between \the [C] and \the [src]. The gauge now reads: [round(100.0*nvg_charge/nvg_maxcharge) ]%.")
/obj/item/prop/helmetgarb/helmet_nvg/proc/repair(mob/user as mob)
if(user.action_busy)
@@ -202,7 +195,6 @@
to_chat(user, "You successfully patch \the [src].")
nvg_maxhealth = 65
nvg_health = 65
- nvg_drain = initial(nvg_drain) * 2
return
else if(nvg_health == nvg_maxhealth)
@@ -244,9 +236,6 @@
else if(nvg_health_procent >= 0)
. += "They are falling apart."
- if (get_dist(user, src) <= 1 && (shape == NVG_SHAPE_FINE || shape == NVG_SHAPE_PATCHED))
- . += "A small gauge in the corner reads: Power: [round(100.0*nvg_charge/nvg_maxcharge) ]%."
-
/obj/item/prop/helmetgarb/helmet_nvg/on_exit_storage(obj/item/storage/S)
remove_attached_item()
return ..()
@@ -296,7 +285,7 @@
if(attached_mob != user && slot == WEAR_HEAD)
set_attached_mob(user)
- if(slot == WEAR_HEAD && !nightvision && activated && nvg_charge > 0 && shape > NVG_SHAPE_BROKEN)
+ if(slot == WEAR_HEAD && !nightvision && activated && !SEND_SIGNAL(src, COMSIG_CELL_CHECK_CHARGE) && shape > NVG_SHAPE_BROKEN)
enable_nvg(user)
else
remove_nvg()
@@ -319,7 +308,7 @@
attached_item.update_icon()
activation.update_button_icon()
- START_PROCESSING(SSobj, src)
+ SEND_SIGNAL(src, COMSIG_CELL_START_TICK_DRAIN)
/obj/item/prop/helmetgarb/helmet_nvg/proc/update_sight(mob/M)
@@ -353,20 +342,15 @@
attached_mob.update_sight()
- STOP_PROCESSING(SSobj, src)
+ SEND_SIGNAL(src, COMSIG_CELL_STOP_TICK_DRAIN)
/obj/item/prop/helmetgarb/helmet_nvg/process(delta_time)
- if(nvg_charge > 0 && !infinite_charge)
- nvg_charge = max(0, nvg_charge - nvg_drain * delta_time)
-
if(!attached_mob)
return PROCESS_KILL
- if(!activated || !attached_item || nvg_charge <= 0 || attached_mob.is_dead())
- if(activated && !attached_mob.is_dead())
- to_chat(attached_mob, SPAN_WARNING("\The [src] emit a low power warning and immediately shut down!"))
- remove_nvg()
+ if(!activated || !attached_item || attached_mob.is_dead())
+ on_power_out()
return
if(!attached_item.has_garb_overlay())
@@ -375,6 +359,13 @@
return
+/obj/item/prop/helmetgarb/helmet_nvg/proc/on_power_out(datum/source)
+ SIGNAL_HANDLER
+
+ if(activated && !attached_mob.is_dead())
+ to_chat(attached_mob, SPAN_WARNING("[src] emit a low power warning and immediately shut down!"))
+ remove_nvg()
+
/obj/item/prop/helmetgarb/helmet_nvg/ui_action_click(mob/owner, obj/item/holder)
toggle_nods(owner)
@@ -410,7 +401,7 @@
if(activated)
to_chat(user, SPAN_NOTICE("You flip the goggles down."))
icon_state = active_icon_state
- if(nvg_charge > 0 && user.head == attached_item && shape > NVG_SHAPE_BROKEN)
+ if(!SEND_SIGNAL(src, COMSIG_CELL_CHECK_CHARGE) && user.head == attached_item && shape > NVG_SHAPE_BROKEN)
enable_nvg(user)
else
icon_state = active_icon_state
@@ -462,7 +453,7 @@
/obj/item/prop/helmetgarb/helmet_nvg/marsoc //for Marine Raiders
name = "\improper Tactical M3 night vision goggles"
desc = "With an integrated self-recharging battery, nothing can stop you. Put them on your helmet and press the button and it's go-time."
- infinite_charge = TRUE
+ cell_max_charge = -1
#undef NVG_SHAPE_COSMETIC
#undef NVG_SHAPE_BROKEN
@@ -527,29 +518,38 @@
icon = 'icons/obj/items/items.dmi'
icon_state = "photo"
///The human who spawns with the photo
- var/mob/living/carbon/human/owner
+ var/datum/weakref/owner
+ ///The belonging human name
+ var/owner_name
+ ///The belonging human faction
+ var/owner_faction
///Text written on the back
var/scribble
-/obj/item/prop/helmetgarb/family_photo/Initialize(mapload, ...)
+/obj/item/prop/helmetgarb/family_photo/pickup(mob/user, silent)
. = ..()
- if(!mapload)
- RegisterSignal(src, COMSIG_POST_SPAWN_UPDATE, PROC_REF(set_owner))
+ if(!owner)
+ RegisterSignal(user, COMSIG_POST_SPAWN_UPDATE, PROC_REF(set_owner))
+
///Sets the owner of the family photo to the human it spawns with, needs var/source for signals
-/obj/item/prop/helmetgarb/family_photo/proc/set_owner(source = src, mob/living/carbon/human/user)
- UnregisterSignal(src, COMSIG_POST_SPAWN_UPDATE)
- owner = user
+/obj/item/prop/helmetgarb/family_photo/proc/set_owner(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, COMSIG_POST_SPAWN_UPDATE)
+ var/mob/living/carbon/human/user = source
+ owner = WEAKREF(user)
+ owner_name = user.name
+ owner_faction = user.faction
/obj/item/prop/helmetgarb/family_photo/get_examine_text(mob/user)
. = ..()
if(scribble)
. += "\"[scribble]\" is written on the back of the photo."
- if(user == owner)
+ if(user.weak_reference == owner)
. += "A photo of you and your family."
return
- if(user.faction == owner?.faction)
- . += "A photo of [owner] and their family."
+ if(user.faction == owner_faction)
+ . += "A photo of [owner_name] and their family."
return
. += "A photo of a family you do not know."
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 95c5f181cd..32158a7fe1 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -670,6 +670,7 @@
RegisterSignal(H, COMSIG_GRENADE_PRE_PRIME, PROC_REF(cloak_grenade_callback))
RegisterSignal(H, COMSIG_HUMAN_EXTINGUISH, PROC_REF(wrapper_fizzle_camouflage))
+ RegisterSignal(H, COMSIG_MOB_EFFECT_CLOAK_CANCEL, PROC_REF(deactivate_camouflage))
camo_active = TRUE
ADD_TRAIT(H, TRAIT_CLOAKED, TRAIT_SOURCE_EQUIPMENT(WEAR_BACK))
@@ -699,12 +700,14 @@
deactivate_camouflage(wearer, TRUE, TRUE)
/obj/item/storage/backpack/marine/satchel/scout_cloak/proc/deactivate_camouflage(mob/living/carbon/human/H, anim = TRUE, forced)
+ SIGNAL_HANDLER
if(!istype(H))
return FALSE
UnregisterSignal(H, list(
COMSIG_GRENADE_PRE_PRIME,
- COMSIG_HUMAN_EXTINGUISH
+ COMSIG_HUMAN_EXTINGUISH,
+ COMSIG_MOB_EFFECT_CLOAK_CANCEL,
))
if(forced)
diff --git a/code/game/objects/items/storage/pouch.dm b/code/game/objects/items/storage/pouch.dm
index 618356f44a..3b1726fd68 100644
--- a/code/game/objects/items/storage/pouch.dm
+++ b/code/game/objects/items/storage/pouch.dm
@@ -833,6 +833,15 @@
new /obj/item/stack/medical/advanced/ointment(src)
new /obj/item/stack/medical/splint(src)
+/obj/item/storage/pouch/medkit/full/toxin/fill_preset_inventory()
+ new /obj/item/device/healthanalyzer(src)
+ new /obj/item/storage/pill_bottle/antitox(src)
+ new /obj/item/storage/pill_bottle/antitox(src)
+ new /obj/item/roller(src)
+ new /obj/item/stack/medical/splint(src)
+ new /obj/item/stack/medical/advanced/bruise_pack(src)
+ new /obj/item/stack/medical/advanced/ointment(src)
+
/obj/item/storage/pouch/pressurized_reagent_canister
name = "Pressurized Reagent Canister Pouch"
max_w_class = SIZE_SMALL
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index cfdb2f88b0..6e7e891d6b 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -852,6 +852,7 @@ W is always an item. stop_warning prevents messaging. user may be null.**/
return ..()
/obj/item/storage/emp_act(severity)
+ . = ..()
if(!istype(src.loc, /mob/living))
for(var/obj/O in contents)
O.emp_act(severity)
diff --git a/code/game/objects/items/tools/misc_tools.dm b/code/game/objects/items/tools/misc_tools.dm
index 98dc89321d..0b4a7cc987 100644
--- a/code/game/objects/items/tools/misc_tools.dm
+++ b/code/game/objects/items/tools/misc_tools.dm
@@ -284,21 +284,24 @@
matter = list("metal" = 20, "gold" = 10)
var/static/list/colour_list = list("red", "blue", "green", "yellow", "purple", "pink", "brown", "black", "orange") // Can add more colors as required
var/current_colour_index = 1
- var/owner = "hard to read text"
+ var/owner_name
-/obj/item/tool/pen/fountain/Initialize(mapload, ...)
+/obj/item/tool/pen/fountain/pickup(mob/user, silent)
. = ..()
- if(!mapload)
- RegisterSignal(src, COMSIG_POST_SPAWN_UPDATE, PROC_REF(set_owner))
+ if(!owner_name)
+ RegisterSignal(user, COMSIG_POST_SPAWN_UPDATE, PROC_REF(set_owner))
///Sets the owner of the pen to who it spawns with, requires var/source for signals
-/obj/item/tool/pen/fountain/proc/set_owner(source = src, mob/living/carbon/human/user)
- UnregisterSignal(src, COMSIG_POST_SPAWN_UPDATE)
- owner = user
+/obj/item/tool/pen/fountain/proc/set_owner(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, COMSIG_POST_SPAWN_UPDATE)
+ var/mob/living/carbon/human/user = source
+ owner_name = user.name
/obj/item/tool/pen/fountain/get_examine_text(mob/user)
. = ..()
- . += "There's a laser engraving of [owner] on it."
+ if(owner_name)
+ . += "There's a laser engraving of [owner_name] on it."
/obj/item/tool/pen/fountain/attack_self(mob/living/carbon/human/user)
if(on)
diff --git a/code/game/objects/items/toys/toys.dm b/code/game/objects/items/toys/toys.dm
index 7a17904635..851f203c52 100644
--- a/code/game/objects/items/toys/toys.dm
+++ b/code/game/objects/items/toys/toys.dm
@@ -553,15 +553,15 @@
///Hexadecimal 0-F (0-15)
var/static/list/hexadecimal = list("0", "1", "2", "3" , "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")
-/obj/item/toy/plush/therapy/random_color/New(loc, ...)
+/obj/item/toy/plush/therapy/random_color/Initialize(mapload, ...)
. = ..()
var/color_code = "#[pick(hexadecimal)][pick(hexadecimal)][pick(hexadecimal)][pick(hexadecimal)][pick(hexadecimal)][pick(hexadecimal)]" //This is dumb and I hope theres a better way I'm missing
color = color_code
- desc = "A custom therapy plush, in a unique color. This one is labeled with \"#[color_code]\"."
+ desc = "A custom therapy plush, in a unique color."
/obj/item/toy/plush/random_plushie //Not using an effect so it can fit into storage from loadout
name = "random plush"
- desc = "You should not be seeing this"
+ desc = "This plush looks awfully standard and bland. Is it actually yours?"
/// Standard plushies for the spawner to pick from
var/list/plush_list = list(
/obj/item/toy/plush/farwa,
@@ -587,22 +587,32 @@
. = ..()
if(mapload) //Placed in mapping, will be randomized instantly on spawn
create_plushie()
- return
- RegisterSignal(src, COMSIG_POST_SPAWN_UPDATE, PROC_REF(create_plushie))
+ return INITIALIZE_HINT_QDEL
+
+/obj/item/toy/plush/random_plushie/pickup(mob/user, silent)
+ . = ..()
+ RegisterSignal(user, COMSIG_POST_SPAWN_UPDATE, PROC_REF(create_plushie))
///The randomizer picking and spawning a plushie on either the ground or in the humans backpack. Needs var/source due to signals
-/obj/item/toy/plush/random_plushie/proc/create_plushie(source = src, mob/living/user)
- UnregisterSignal(src, COMSIG_POST_SPAWN_UPDATE)
+/obj/item/toy/plush/random_plushie/proc/create_plushie(datum/source)
+ SIGNAL_HANDLER
+ if(source)
+ UnregisterSignal(source, COMSIG_POST_SPAWN_UPDATE)
+ var/turf/spawn_location = get_turf(src)
var/plush_list_variety = pick(60; plush_list, 40; therapy_plush_list)
var/random_plushie = pick(plush_list_variety)
- var/obj/item/toy/plush/plush = new random_plushie(get_turf(src)) //Starts on floor by default
+ var/obj/item/toy/plush/plush = new random_plushie(spawn_location) //Starts on floor by default
+ var/mob/living/carbon/human/user = source
if(!user) //If it didn't spawn on a humanoid
qdel(src)
return
+
var/obj/item/storage/backpack/storage = locate() in user //If the user has a backpack, put it there
if(storage?.can_be_inserted(plush, user, stop_messages = TRUE))
storage.attempt_item_insertion(plush, TRUE, user)
+ if(plush.loc == spawn_location) // Still on the ground
+ user.put_in_hands(plush, drop_on_fail = TRUE)
qdel(src)
//Admin plushies
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 6cb9f58aae..82fdf30f0f 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -197,9 +197,9 @@
return TRUE
/obj/item/weapon/baton/emp_act(severity)
+ . = ..()
if(bcell)
bcell.emp_act(severity) //let's not duplicate code everywhere if we don't have to please.
- ..()
//secborg stun baton module
/obj/item/weapon/baton/robot/attack_self(mob/user)
diff --git a/code/game/objects/prop.dm b/code/game/objects/prop.dm
index e59c24b30d..c067a9730e 100644
--- a/code/game/objects/prop.dm
+++ b/code/game/objects/prop.dm
@@ -11,6 +11,66 @@
w_class = SIZE_SMALL
garbage = TRUE
+/obj/item/prop/geiger_counter
+ name = "geiger counter"
+ desc = "A geiger counter measures the radiation it receives. This type automatically records and transfers any information it reads, provided it has a battery, with no user input required beyond being enabled."
+ icon = 'icons/obj/items/devices.dmi'
+ icon_state = "geiger"
+ item_state = ""
+ w_class = SIZE_SMALL
+ flags_equip_slot = SLOT_WAIST
+ ///Whether the geiger counter is on or off
+ var/toggled_on = FALSE
+ ///Iconstate of geiger counter when on
+ var/enabled_state = "geiger_on"
+ ///Iconstate of geiger counter when off
+ var/disabled_state = "geiger"
+ ///New battery it will spawn with
+ var/starting_battery = /obj/item/cell/crap
+ ///Battery inside geiger counter
+ var/obj/item/cell/battery //It doesn't drain the battery, but it has a battery for emergency use
+
+/obj/item/prop/geiger_counter/Initialize(mapload, ...)
+ . = ..()
+ if(!starting_battery)
+ return
+ battery = new starting_battery(src)
+
+/obj/item/prop/geiger_counter/Destroy()
+ . = ..()
+ if(battery)
+ qdel(battery)
+
+/obj/item/prop/geiger_counter/attack_self(mob/user)
+ . = ..()
+ toggled_on = !toggled_on
+ if(!battery)
+ to_chat(user, SPAN_NOTICE("[src] is missing a battery."))
+ return
+ to_chat(user, SPAN_NOTICE("You [toggled_on ? "enable" : "disable"] [src]."))
+ update_icon()
+
+/obj/item/prop/geiger_counter/attackby(obj/item/attacking_item, mob/user)
+ . = ..()
+ if(!HAS_TRAIT(attacking_item, TRAIT_TOOL_SCREWDRIVER) && !HAS_TRAIT(attacking_item, TRAIT_TOOL_CROWBAR))
+ return
+
+ if(!battery)
+ to_chat(user, SPAN_NOTICE("There is no battery for you to remove."))
+ return
+ to_chat(user, SPAN_NOTICE("You jam [battery] out of [src] with [attacking_item], prying it out irreversibly."))
+ user.put_in_hands(battery)
+ battery = null
+ update_icon()
+
+/obj/item/prop/geiger_counter/update_icon()
+ . = ..()
+
+ if(battery && toggled_on)
+ icon_state = enabled_state
+ return
+ icon_state = disabled_state
+
/obj/item/prop/tableflag
name = "United Americas table flag"
icon = 'icons/obj/items/items.dmi'
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
index e290a23a61..331cb884bd 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
@@ -30,6 +30,7 @@
return 0
/obj/structure/closet/secure_closet/emp_act(severity)
+ . = ..()
for(var/obj/O in src)
O.emp_act(severity)
if(!broken)
@@ -42,7 +43,6 @@
else
src.req_access = list()
src.req_access += pick(get_access(ACCESS_LIST_MARINE_MAIN))
- ..()
/obj/structure/closet/secure_closet/proc/togglelock(mob/living/user)
if(src.opened)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 9f0417ccb3..119615ab7a 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -12,6 +12,19 @@
throwpass = 1 //prevents moving crates by hurling things at them
store_mobs = FALSE
var/rigged = 0
+ /// Types this crate can be made into
+ var/list/crate_customizing_types = list(
+ "Plain" = /obj/structure/closet/crate,
+ "Weapons" = /obj/structure/closet/crate/weapon,
+ "Supply" = /obj/structure/closet/crate/supply,
+ "Ammo" = /obj/structure/closet/crate/ammo,
+ "Construction" = /obj/structure/closet/crate/construction,
+ "Explosives" = /obj/structure/closet/crate/explosives,
+ "Alpha" = /obj/structure/closet/crate/alpha,
+ "Bravo" = /obj/structure/closet/crate/bravo,
+ "Charlie" = /obj/structure/closet/crate/charlie,
+ "Delta" = /obj/structure/closet/crate/delta,
+ )
/obj/structure/closet/crate/initialize_pass_flags(datum/pass_flags_container/PF)
..()
@@ -207,6 +220,7 @@
icon_state = "closed_freezer"
icon_opened = "open_freezer"
icon_closed = "closed_freezer"
+ crate_customizing_types = null
var/target_temp = T0C - 40
var/cooling_power = 40
diff --git a/code/game/objects/structures/crates_lockers/secure_crates.dm b/code/game/objects/structures/crates_lockers/secure_crates.dm
index a308c4c0a2..28a77e0c81 100644
--- a/code/game/objects/structures/crates_lockers/secure_crates.dm
+++ b/code/game/objects/structures/crates_lockers/secure_crates.dm
@@ -4,6 +4,7 @@
icon_state = "secure_locked_basic"
icon_opened = "secure_open_basic"
icon_closed = "secure_locked_basic"
+ crate_customizing_types = null
var/icon_locked = "secure_locked_basic"
var/icon_unlocked = "secure_unlocked_basic"
var/sparks = "securecratesparks"
@@ -86,6 +87,7 @@
..()
/obj/structure/closet/crate/secure/emp_act(severity)
+ . = ..()
for(var/obj/O in src)
O.emp_act(severity)
if(!broken && !opened && prob(50/severity))
@@ -105,7 +107,6 @@
else
src.req_access = list()
src.req_access += pick(get_access(ACCESS_LIST_MARINE_MAIN))
- ..()
//------------------------------------
diff --git a/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm b/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
index 7a4274c2c1..6375fcd138 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
@@ -53,7 +53,7 @@
current_mob.pixel_y = buckling_y["[dir]"]
current_mob.pixel_x = buckling_x["[dir]"]
current_mob.dir = turn(dir, 180)
- current_mob.density = FALSE
+ ADD_TRAIT(current_mob, TRAIT_UNDENSE, XENO_NEST_TRAIT)
pixel_y = buckling_y["[dir]"]
pixel_x = buckling_x["[dir]"]
if(dir == SOUTH)
@@ -67,7 +67,7 @@
current_mob.pixel_y = initial(buckled_mob.pixel_y)
current_mob.pixel_x = initial(buckled_mob.pixel_x)
- current_mob.density = !(current_mob.lying || current_mob.stat == DEAD)
+ REMOVE_TRAIT(current_mob, TRAIT_UNDENSE, XENO_NEST_TRAIT)
if(dir == SOUTH)
current_mob.layer = initial(current_mob.layer)
if(!ishuman(current_mob))
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 5042167023..38b63b9457 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -261,7 +261,7 @@
if("Remove")
if(!GLOB.trait_name_map)
GLOB.trait_name_map = generate_trait_name_map()
- for(var/trait in D.status_traits)
+ for(var/trait in D._status_traits)
var/name = GLOB.trait_name_map[trait] || trait
available_traits[name] = trait
@@ -282,7 +282,7 @@
if("All")
source = null
if("Specific")
- source = input("Source to be removed","Trait Remove/Add") as null|anything in sort_list(D.status_traits[chosen_trait])
+ source = input("Source to be removed","Trait Remove/Add") as null|anything in sort_list(D._status_traits[chosen_trait])
if(!source)
return
REMOVE_TRAIT(D,chosen_trait,source)
diff --git a/code/modules/admin/fax_templates.dm b/code/modules/admin/fax_templates.dm
index 91b23abb24..459ab675d3 100644
--- a/code/modules/admin/fax_templates.dm
+++ b/code/modules/admin/fax_templates.dm
@@ -1,11 +1,13 @@
/proc/generate_templated_fax(show_wy_logo, fax_header, fax_subject, addressed_to, message_body, sent_by, sent_title, sent_department)
+ var/datum/asset/asset = get_asset_datum(/datum/asset/simple/paper)
+
var/dat = ""
dat += "