diff --git a/.travis.yml b/.travis.yml
index 9d0d4c664e..f9860d0126 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ sudo: false
env:
global:
- BYOND_MAJOR="512"
- - BYOND_MINOR="1414"
+ - BYOND_MINOR="1453"
- MACRO_COUNT=4
matrix:
- TEST_DEFINE="MAP_TEST" TEST_FILE="code/_map_tests.dm" RUN="0"
diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
index d696aad039..a72aae425b 100644
--- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
@@ -194,7 +194,7 @@
return 1
-/obj/machinery/atmospherics/binary/dp_vent_pump/initialize()
+/obj/machinery/atmospherics/binary/dp_vent_pump/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
index 3988b790c8..accedb543d 100644
--- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
@@ -129,7 +129,7 @@
return 1
-/obj/machinery/atmospherics/binary/passive_gate/initialize()
+/obj/machinery/atmospherics/binary/passive_gate/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm
index b03206b262..cacdf5240b 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm
@@ -149,7 +149,7 @@ Thus, the two variables affect pump operation are set in New():
ui.open() // open the new ui window
ui.set_auto_update(1) // auto update every Master Controller tick
-/obj/machinery/atmospherics/binary/pump/initialize()
+/obj/machinery/atmospherics/binary/pump/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
index 49cb05e9af..ea3bd60456 100755
--- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
@@ -105,7 +105,7 @@
return 1
-/obj/machinery/atmospherics/trinary/atmos_filter/initialize()
+/obj/machinery/atmospherics/trinary/atmos_filter/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index cfc990c6e5..c0d796d45b 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -302,7 +302,7 @@
-/obj/machinery/atmospherics/tvalve/digital/initialize()
+/obj/machinery/atmospherics/tvalve/digital/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
index 4a9403a64b..ee1379ac28 100644
--- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm
+++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
@@ -122,7 +122,7 @@
return 1
-/obj/machinery/atmospherics/unary/outlet_injector/initialize()
+/obj/machinery/atmospherics/unary/outlet_injector/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm
index f385ae5ed6..2e5e3e08ee 100644
--- a/code/ATMOSPHERICS/components/valve.dm
+++ b/code/ATMOSPHERICS/components/valve.dm
@@ -263,7 +263,7 @@
if(frequency)
radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
-/obj/machinery/atmospherics/valve/digital/initialize()
+/obj/machinery/atmospherics/valve/digital/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm
index e5d2b7eb11..394fe72478 100644
--- a/code/ZAS/Airflow.dm
+++ b/code/ZAS/Airflow.dm
@@ -22,9 +22,6 @@ mob/proc/airflow_stun()
mob/living/silicon/airflow_stun()
return
-mob/living/simple_animal/slime/airflow_stun()
- return
-
mob/living/carbon/human/airflow_stun()
if(shoes && (shoes.item_flags & NOSLIP))
to_chat(src, "Air suddenly rushes past you!")
diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm
index 67dc74e491..2683922820 100644
--- a/code/__defines/MC.dm
+++ b/code/__defines/MC.dm
@@ -1,15 +1,81 @@
-#define MC_TICK_CHECK ( ( TICK_USAGE > GLOB.CURRENT_TICKLIMIT || src.state != SS_RUNNING ) ? pause() : 0 )
-
-// Used for splitting up your remaining time into phases, if you want to evenly divide it.
-#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = GLOB.CURRENT_TICKLIMIT; var/split_tick_phases = ##phase_count
+#define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
+#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count
#define MC_SPLIT_TICK \
- if(split_tick_phases > 1){\
- GLOB.CURRENT_TICKLIMIT = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
- --split_tick_phases;\
- } else {\
- GLOB.CURRENT_TICKLIMIT = original_tick_limit;\
- }
+ if(split_tick_phases > 1){\
+ Master.current_ticklimit = ((original_tick_limit - TICK_USAGE) / split_tick_phases) + TICK_USAGE;\
+ --split_tick_phases;\
+ } else {\
+ Master.current_ticklimit = original_tick_limit;\
+ }
+
+// Used to smooth out costs to try and avoid oscillation.
+#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
+#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
+#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current))
+
+#define MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
+#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
+
+#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
+
+#define START_PROCESSING(Processor, Datum) if (!Datum.is_processing) {Datum.is_processing = TRUE;Processor.processing += Datum}
+#define STOP_PROCESSING(Processor, Datum) Datum.is_processing = FALSE;Processor.processing -= Datum
+
+//! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
+
+/// subsystem does not initialize.
+#define SS_NO_INIT 1
+
+/** subsystem does not fire. */
+/// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
+/// (Requires a MC restart to change)
+#define SS_NO_FIRE 2
+
+/** subsystem only runs on spare cpu (after all non-background subsystems have ran that tick) */
+/// SS_BACKGROUND has its own priority bracket
+#define SS_BACKGROUND 4
+
+/// subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
+#define SS_NO_TICK_CHECK 8
+
+/** Treat wait as a tick count, not DS, run every wait ticks. */
+/// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
+/// (implies all runlevels because of how it works)
+/// (overrides SS_BACKGROUND)
+/// This is designed for basically anything that works as a mini-mc (like SStimer)
+#define SS_TICKER 16
+
+/** keep the subsystem's timing on point by firing early if it fired late last fire because of lag */
+/// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
+#define SS_KEEP_TIMING 32
+
+/** Calculate its next fire after its fired. */
+/// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
+/// This flag overrides SS_KEEP_TIMING
+#define SS_POST_FIRE_TIMING 64
+
+//! SUBSYSTEM STATES
+#define SS_IDLE 0 /// aint doing shit.
+#define SS_QUEUED 1 /// queued to run
+#define SS_RUNNING 2 /// actively running
+#define SS_PAUSED 3 /// paused by mc_tick_check
+#define SS_SLEEPING 4 /// fire() slept.
+#define SS_PAUSING 5 /// in the middle of pausing
+
+#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
+/datum/controller/subsystem/##X/New(){\
+ NEW_SS_GLOBAL(SS##X);\
+ PreInit();\
+}\
+/datum/controller/subsystem/##X
+
+#define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\
+/datum/controller/subsystem/processing/##X/New(){\
+ NEW_SS_GLOBAL(SS##X);\
+ PreInit();\
+}\
+/datum/controller/subsystem/processing/##X
// Boilerplate code for multi-step processors. See machines.dm for example use.
#define INTERNAL_PROCESS_STEP(this_step, initial_step, proc_to_call, cost_var, next_step)\
@@ -23,71 +89,3 @@ if(current_step == this_step || (initial_step && !resumed)) /* So we start at st
resumed = 0;\
current_step = next_step;\
}
-
-// Used to smooth out costs to try and avoid oscillation.
-#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
-#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
-#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current))
-
-#define MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
-#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
-
-#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
-
-#define START_PROCESSING(Processor, Datum) if (!Datum.is_processing) {Datum.is_processing = 1;Processor.processing += Datum}
-#define STOP_PROCESSING(Processor, Datum) Datum.is_processing = 0;Processor.processing -= Datum
-
-//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
-
-//subsystem does not initialize.
-#define SS_NO_INIT 1
-
-//subsystem does not fire.
-// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
-// (Requires a MC restart to change)
-#define SS_NO_FIRE 2
-
-//subsystem only runs on spare cpu (after all non-background subsystems have ran that tick)
-// SS_BACKGROUND has its own priority bracket
-#define SS_BACKGROUND 4
-
-//subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
-#define SS_NO_TICK_CHECK 8
-
-//Treat wait as a tick count, not DS, run every wait ticks.
-// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
-// (implies all runlevels because of how it works)
-// (overrides SS_BACKGROUND)
-// This is designed for basically anything that works as a mini-mc (like SStimer)
-#define SS_TICKER 16
-
-//keep the subsystem's timing on point by firing early if it fired late last fire because of lag
-// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
-#define SS_KEEP_TIMING 32
-
-//Calculate its next fire after its fired.
-// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
-// This flag overrides SS_KEEP_TIMING
-#define SS_POST_FIRE_TIMING 64
-
-//SUBSYSTEM STATES
-#define SS_IDLE 0 //aint doing shit.
-#define SS_QUEUED 1 //queued to run
-#define SS_RUNNING 2 //actively running
-#define SS_PAUSED 3 //paused by mc_tick_check
-#define SS_SLEEPING 4 //fire() slept.
-#define SS_PAUSING 5 //in the middle of pausing
-
-// Standard way to define a global subsystem, keep boilerplate organized here!
-#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
-/datum/controller/subsystem/##X/New(){\
- NEW_SS_GLOBAL(SS##X);\
- PreInit();\
-}\
-/datum/controller/subsystem/##X
- #define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\
-/datum/controller/subsystem/processing/##X/New(){\
- NEW_SS_GLOBAL(SS##X);\
- PreInit();\
-}\
-/datum/controller/subsystem/processing/##X
\ No newline at end of file
diff --git a/code/__defines/_lists.dm b/code/__defines/_lists.dm
new file mode 100644
index 0000000000..f011662cec
--- /dev/null
+++ b/code/__defines/_lists.dm
@@ -0,0 +1,54 @@
+// Helper macros to aid in optimizing lazy instantiation of lists.
+// All of these are null-safe, you can use them without knowing if the list var is initialized yet
+
+//Picks from the list, with some safeties, and returns the "default" arg if it fails
+#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
+// Ensures L is initailized after this point
+#define LAZYINITLIST(L) if (!L) L = list()
+// Sets a L back to null iff it is empty
+#define UNSETEMPTY(L) if (L && !length(L)) L = null
+// Removes I from list L, and sets I to null if it is now empty
+#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
+// Adds I to L, initalizing I if necessary
+#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
+#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
+#define LAZYFIND(L, V) L ? L.Find(V) : 0
+// Reads I from L safely - Works with both associative and traditional lists.
+#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
+// Turns LAZYINITLIST(L) L[K] = V into ... for associated lists
+#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
+// Reads the length of L, returning 0 if null
+#define LAZYLEN(L) length(L)
+// Null-safe L.Cut()
+#define LAZYCLEARLIST(L) if(L) L.Cut()
+// Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression.
+#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
+#define reverseList(L) reverseRange(L.Copy())
+
+// binary search sorted insert
+// IN: Object to be inserted
+// LIST: List to insert object into
+// TYPECONT: The typepath of the contents of the list
+// COMPARE: The variable on the objects to compare
+#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \
+ var/__BIN_CTTL = length(LIST);\
+ if(!__BIN_CTTL) {\
+ LIST += IN;\
+ } else {\
+ var/__BIN_LEFT = 1;\
+ var/__BIN_RIGHT = __BIN_CTTL;\
+ var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
+ var/##TYPECONT/__BIN_ITEM;\
+ while(__BIN_LEFT < __BIN_RIGHT) {\
+ __BIN_ITEM = LIST[__BIN_MID];\
+ if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\
+ __BIN_LEFT = __BIN_MID + 1;\
+ } else {\
+ __BIN_RIGHT = __BIN_MID;\
+ };\
+ __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
+ };\
+ __BIN_ITEM = LIST[__BIN_MID];\
+ __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\
+ LIST.Insert(__BIN_MID, IN);\
+ }
diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 4d258cd91f..e37bef86d4 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -58,7 +58,10 @@ What is the naming convention for planes or layers?
#define ATMOS_LAYER 2.4 // Pipe-like atmos machinery that goes on the floor, like filters.
#define ABOVE_UTILITY 2.5 // Above stuff like pipes and wires
#define TURF_PLANE -45 // Turfs themselves, most flooring
- #define ABOVE_TURF_LAYER 2.1 // Snow and wallmounted/floormounted equipment
+ #define WATER_FLOOR_LAYER 2.0 // The 'bottom' of water tiles.
+ #define UNDERWATER_LAYER 2.5 // Anything on this layer will render under the water layer.
+ #define WATER_LAYER 3.0 // Layer for water overlays.
+ #define ABOVE_TURF_LAYER 3.1 // Snow and wallmounted/floormounted equipment
#define DECAL_PLANE -44 // Permanent decals
#define DIRTY_PLANE -43 // Nonpermanent decals
#define BLOOD_PLANE -42 // Blood is really dirty, but we can do special stuff if we separate it
diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm
index 7ca3fb23a2..2c761b86f9 100644
--- a/code/__defines/_tick.dm
+++ b/code/__defines/_tick.dm
@@ -3,7 +3,7 @@
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98
-#define TICK_CHECK ( TICK_USAGE > GLOB.CURRENT_TICKLIMIT )
+#define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit )
#define CHECK_TICK if TICK_CHECK stoplag()
#define TICK_USAGE world.tick_usage
diff --git a/code/__defines/admin.dm b/code/__defines/admin.dm
index d2afb7846e..84e0656c97 100644
--- a/code/__defines/admin.dm
+++ b/code/__defines/admin.dm
@@ -45,3 +45,27 @@
#define SMITE_BLUESPACEARTILLERY "Bluespace Artillery"
#define SMITE_SPONTANEOUSCOMBUSTION "Spontaneous Combustion"
#define SMITE_LIGHTNINGBOLT "Lightning Bolt"
+
+#define ADMIN_QUE(user) "(?)"
+#define ADMIN_FLW(user) "(FLW)"
+#define ADMIN_PP(user) "(PP)"
+#define ADMIN_VV(atom) "(VV)"
+#define ADMIN_SM(user) "(SM)"
+#define ADMIN_TP(user) "(TP)"
+#define ADMIN_BSA(user) "(BSA)"
+#define ADMIN_KICK(user) "(KICK)"
+#define ADMIN_CENTCOM_REPLY(user) "(RPLY)"
+#define ADMIN_SYNDICATE_REPLY(user) "(RPLY)"
+#define ADMIN_SC(user) "(SC)"
+#define ADMIN_SMITE(user) "(SMITE)"
+#define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]"
+#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
+#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)]"
+#define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]"
+#define ADMIN_JMP(src) "(JMP)"
+#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
+#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
+
+#define AHELP_ACTIVE 1
+#define AHELP_CLOSED 2
+#define AHELP_RESOLVED 3
\ No newline at end of file
diff --git a/code/__defines/color.dm b/code/__defines/color.dm
new file mode 100644
index 0000000000..ba9efe20de
--- /dev/null
+++ b/code/__defines/color.dm
@@ -0,0 +1,17 @@
+//Color defines used by the assembly detailer.
+#define COLOR_ASSEMBLY_BLACK "#545454"
+#define COLOR_ASSEMBLY_BGRAY "#9497AB"
+#define COLOR_ASSEMBLY_WHITE "#E2E2E2"
+#define COLOR_ASSEMBLY_RED "#CC4242"
+#define COLOR_ASSEMBLY_ORANGE "#E39751"
+#define COLOR_ASSEMBLY_BEIGE "#AF9366"
+#define COLOR_ASSEMBLY_BROWN "#97670E"
+#define COLOR_ASSEMBLY_GOLD "#AA9100"
+#define COLOR_ASSEMBLY_YELLOW "#CECA2B"
+#define COLOR_ASSEMBLY_GURKHA "#999875"
+#define COLOR_ASSEMBLY_LGREEN "#789876"
+#define COLOR_ASSEMBLY_GREEN "#44843C"
+#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
+#define COLOR_ASSEMBLY_BLUE "#38559E"
+#define COLOR_ASSEMBLY_PURPLE "#6F6192"
+#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4"
\ No newline at end of file
diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm
index 2e14fb96eb..c3eef1099e 100644
--- a/code/__defines/damage_organs.dm
+++ b/code/__defines/damage_organs.dm
@@ -7,6 +7,7 @@
#define CLONE "clone"
#define HALLOSS "halloss"
#define ELECTROCUTE "electrocute"
+#define BIOACID "bioacid"
#define CUT "cut"
#define BRUISE "bruise"
diff --git a/code/__defines/integrated_circuits.dm b/code/__defines/integrated_circuits.dm
new file mode 100644
index 0000000000..b2859a232d
--- /dev/null
+++ b/code/__defines/integrated_circuits.dm
@@ -0,0 +1,3 @@
+// Methods of obtaining a circuit.
+#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer.
+#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it.
\ No newline at end of file
diff --git a/code/__defines/math.dm b/code/__defines/math.dm
index 3132c4228d..88c459ba78 100644
--- a/code/__defines/math.dm
+++ b/code/__defines/math.dm
@@ -4,15 +4,19 @@
#define NUM_E 2.71828183
-#define INFINITY 1e31 //closer then enough
+#define M_PI (3.14159265)
+#define INFINITY (1.#INF) //closer then enough
-#define SHORT_REAL_LIMIT 16777216 // 2^24 - Maximum integer that can be exactly represented in a float (BYOND num var)
+#define SHORT_REAL_LIMIT 16777216
//"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks
//percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio)
//collapsed to percent_of_tick_used * tick_lag
#define TICK_DELTA_TO_MS(percent_of_tick_used) ((percent_of_tick_used) * world.tick_lag)
-#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(TICK_USAGE-starting_tickusage))
+#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(world.tick_usage - starting_tickusage))
+
+#define PERCENT(val) (round((val)*100, 0.1))
+#define CLAMP01(x) (CLAMP(x, 0, 1))
//time of day but automatically adjusts to the server going into the next day within the same round.
//for when you need a reliable time number that doesn't depend on byond time.
@@ -22,6 +26,7 @@
#define SIGN(x) ( (x)!=0 ? (x) / abs(x) : 0 )
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
+
// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) )
@@ -33,9 +38,6 @@
// Real modulus that handles decimals
#define MODULUS(x, y) ( (x) - (y) * round((x) / (y)) )
-// Check if a BYOND dir var is a cardinal direction (power of two)
-#define IS_CARDINAL(x) ((x & (x - 1)) == 0)
-
// Tangent
#define TAN(x) (sin(x) / cos(x))
@@ -50,8 +52,71 @@
#define ATAN2(x, y) ( !(x) && !(y) ? 0 : (y) >= 0 ? arccos((x) / sqrt((x)*(x) + (y)*(y))) : -arccos((x) / sqrt((x)*(x) + (y)*(y))) )
+// Greatest Common Divisor - Euclid's algorithm
+/proc/GCD(a, b)
+ return b ? GCD(b, (a) % (b)) : a
+
+// Least Common Multiple
+#define LCM(a, b) (abs(a) / GCD(a, b) * abs(b))
+
+#define IS_CARDINAL(x) ((x & (x - 1)) == 0)
+
+#define INVERSE(x) ( 1/(x) )
+
+// Used for calculating the radioactive strength falloff
+#define INVERSE_SQUARE(initial_strength,cur_distance,initial_distance) ( (initial_strength)*((initial_distance)**2/(cur_distance)**2) )
+
+#define ISABOUTEQUAL(a, b, deviation) (deviation ? abs((a) - (b)) <= deviation : abs((a) - (b)) <= 0.1)
+
+#define ISEVEN(x) (x % 2 == 0)
+
+#define ISODD(x) (x % 2 != 0)
+
+// Returns true if val is from min to max, inclusive.
+#define ISINRANGE(val, min, max) (min <= val && val <= max)
+
+// Same as above, exclusive.
+#define ISINRANGE_EX(val, min, max) (min < val && val > max)
+
+#define ISINTEGER(x) (round(x) == x)
+
+#define ISMULTIPLE(x, y) ((x) % (y) == 0)
+
+// Performs a linear interpolation between a and b.
+// Note that amount=0 returns a, amount=1 returns b, and
+// amount=0.5 returns the mean of a and b.
+#define LERP(a, b, amount) ( amount ? ((a) + ((b) - (a)) * (amount)) : a )
+
+// Returns the nth root of x.
+#define ROOT(n, x) ((x) ** (1 / (n)))
+
+/proc/Mean(...)
+ var/sum = 0
+ for(var/val in args)
+ sum += val
+ return sum / args.len
+
+// The quadratic formula. Returns a list with the solutions, or an empty list
+// if they are imaginary.
+/proc/SolveQuadratic(a, b, c)
+ ASSERT(a)
+ . = list()
+ var/d = b*b - 4 * a * c
+ var/bottom = 2 * a
+ // Return if the roots are imaginary.
+ if(d < 0)
+ return
+ var/root = sqrt(d)
+ . += (-b + root) / bottom
+ // If discriminant == 0, there would be two roots at the same position.
+ if(!d)
+ return
+ . += (-b - root) / bottom
+
+ // 180 / Pi ~ 57.2957795
#define TODEGREES(radians) ((radians) * 57.2957795)
+ // Pi / 180 ~ 0.0174532925
#define TORADIANS(degrees) ((degrees) * 0.0174532925)
// Will filter out extra rotations and negative rotations
@@ -73,3 +138,91 @@
if(dec < 0)
dec += 360
. = inc > dec? -dec : inc
+
+//A logarithm that converts an integer to a number scaled between 0 and 1.
+//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions.
+#define TRANSFORM_USING_VARIABLE(input, max) ( sin((90*(input))/(max))**2 )
+
+//converts a uniform distributed random number into a normal distributed one
+//since this method produces two random numbers, one is saved for subsequent calls
+//(making the cost negligble for every second call)
+//This will return +/- decimals, situated about mean with standard deviation stddev
+//68% chance that the number is within 1stddev
+//95% chance that the number is within 2stddev
+//98% chance that the number is within 3stddev...etc
+#define ACCURACY 10000
+/proc/gaussian(mean, stddev)
+ var/static/gaussian_next
+ var/R1;var/R2;var/working
+ if(gaussian_next != null)
+ R1 = gaussian_next
+ gaussian_next = null
+ else
+ do
+ R1 = rand(-ACCURACY,ACCURACY)/ACCURACY
+ R2 = rand(-ACCURACY,ACCURACY)/ACCURACY
+ working = R1*R1 + R2*R2
+ while(working >= 1 || working==0)
+ working = sqrt(-2 * log(working) / working)
+ R1 *= working
+ gaussian_next = R2 * working
+ return (mean + stddev * R1)
+#undef ACCURACY
+
+/proc/get_turf_in_angle(angle, turf/starting, increments)
+ var/pixel_x = 0
+ var/pixel_y = 0
+ for(var/i in 1 to increments)
+ pixel_x += sin(angle)+16*sin(angle)*2
+ pixel_y += cos(angle)+16*cos(angle)*2
+ var/new_x = starting.x
+ var/new_y = starting.y
+ while(pixel_x > 16)
+ pixel_x -= 32
+ new_x++
+ while(pixel_x < -16)
+ pixel_x += 32
+ new_x--
+ while(pixel_y > 16)
+ pixel_y -= 32
+ new_y++
+ while(pixel_y < -16)
+ pixel_y += 32
+ new_y--
+ new_x = CLAMP(new_x, 0, world.maxx)
+ new_y = CLAMP(new_y, 0, world.maxy)
+ return locate(new_x, new_y, starting.z)
+
+// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles
+/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4)
+ var/list/region_x1 = list()
+ var/list/region_y1 = list()
+ var/list/region_x2 = list()
+ var/list/region_y2 = list()
+
+ // These loops create loops filled with x/y values that the boundaries inhabit
+ // ex: list(5, 6, 7, 8, 9)
+ for(var/i in min(x1, x2) to max(x1, x2))
+ region_x1["[i]"] = TRUE
+ for(var/i in min(y1, y2) to max(y1, y2))
+ region_y1["[i]"] = TRUE
+ for(var/i in min(x3, x4) to max(x3, x4))
+ region_x2["[i]"] = TRUE
+ for(var/i in min(y3, y4) to max(y3, y4))
+ region_y2["[i]"] = TRUE
+
+ return list(region_x1 & region_x2, region_y1 & region_y2)
+
+// )
+
+#define RAND_F(LOW, HIGH) (rand()*(HIGH-LOW) + LOW)
+
+#define SQUARE(x) (x*x)
+
+//Vector Algebra
+#define SQUAREDNORM(x, y) (x*x+y*y)
+#define NORM(x, y) (sqrt(SQUAREDNORM(x,y)))
+#define ISPOWEROFTWO(x) ((x & (x - 1)) == 0)
+#define ROUNDUPTOPOWEROFTWO(x) (2 ** -round(-log(2,x)))
+
+#define DEFAULT(a, b) (a? a : b)
diff --git a/code/__defines/math_physics.dm b/code/__defines/math_physics.dm
index eb4f103065..90950d0ca0 100644
--- a/code/__defines/math_physics.dm
+++ b/code/__defines/math_physics.dm
@@ -1,10 +1,13 @@
// Math constants.
-#define M_PI 3.14159265
-
#define R_IDEAL_GAS_EQUATION 8.31 // kPa*L/(K*mol).
#define ONE_ATMOSPHERE 101.325 // kPa.
#define IDEAL_GAS_ENTROPY_CONSTANT 1164 // (mol^3 * s^3) / (kg^3 * L).
+#define T0C 273.15 // 0.0 degrees celcius
+#define T20C 293.15 // 20.0 degrees celcius
+#define TCMB 2.7 // -270.3 degrees celcius
+#define TN60C 213.15 // -60 degrees celcius
+
// Radiation constants.
#define STEFAN_BOLTZMANN_CONSTANT 5.6704e-8 // W/(m^2*K^4).
#define COSMIC_RADIATION_TEMPERATURE 3.15 // K.
@@ -15,16 +18,7 @@
#define RADIATOR_EXPOSED_SURFACE_AREA_RATIO 0.04 // (3 cm + 100 cm * sin(3deg))/(2*(3+100 cm)). Unitless ratio.
#define HUMAN_EXPOSED_SURFACE_AREA 5.2 //m^2, surface area of 1.7m (H) x 0.46m (D) cylinder
-#define T0C 273.15 // 0.0 degrees celcius
-#define T20C 293.15 // 20.0 degrees celcius
-#define TCMB 2.7 // -270.3 degrees celcius
-#define TN60C 213.15 // -60 degrees celcius
-
-#define CLAMP01(x) max(0, min(1, x))
#define QUANTIZE(variable) (round(variable,0.0001))
-#define TICKS_IN_DAY 24*60*60*10
-#define TICKS_IN_SECOND 10
-
-#define SIMPLE_SIGN(X) ((X) < 0 ? -1 : 1)
-#define SIGN(X) ((X) ? SIMPLE_SIGN(X) : 0)
+#define TICKS_IN_DAY (TICKS_IN_SECOND * 60 * 60 * 24)
+#define TICKS_IN_SECOND (world.fps)
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index d30b6a1a6e..95f0424fa4 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -149,6 +149,7 @@
#define MAT_GLASS "glass"
#define MAT_SILVER "silver"
#define MAT_GOLD "gold"
+#define MAT_URANIUM "uranium" //Did it
#define MAT_TITANIUM "titanium"
#define MAT_PHORON "phoron"
#define MAT_DIAMOND "diamond"
@@ -257,7 +258,7 @@
// If the GLOB system is ever ported, you can change this macro in one place and have less work to do than you otherwise would.
#define GLOBAL_LIST_BOILERPLATE(LIST_NAME, PATH)\
var/global/list/##LIST_NAME = list();\
-##PATH/initialize(mapload, ...)\
+##PATH/Initialize(mapload, ...)\
{\
##LIST_NAME += src;\
return ..();\
@@ -288,6 +289,20 @@ var/global/list/##LIST_NAME = list();\
#define IS_WIRECUTTER "wirecutter"
#define IS_WRENCH "wrench"
+
// Diagonal movement
#define FIRST_DIAG_STEP 1
#define SECOND_DIAG_STEP 2
+
+// RCD modes. Used on the RCD, and gets passed to an object's rcd_act() when an RCD is used on it, to determine what happens.
+#define RCD_FLOORWALL "Floor / Wall" // Builds plating on space/ground/open tiles. Builds a wall when on floors. Finishes walls when used on girders.
+#define RCD_AIRLOCK "Airlock" // Builds an airlock on the tile if one isn't already there.
+#define RCD_WINDOWGRILLE "Window / Grille" // Builds a full tile window and grille pair on floors.
+#define RCD_DECONSTRUCT "Deconstruction" // Removes various things. Still consumes compressed matter.
+
+#define RCD_VALUE_MODE "mode"
+#define RCD_VALUE_DELAY "delay"
+#define RCD_VALUE_COST "cost"
+
+#define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units.
+#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 679962b210..3221a595e4 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -27,6 +27,9 @@
#define BORGXRAY 0x4
#define BORGMATERIAL 8
+#define STANCE_ATTACK 11 // Backwards compatability
+#define STANCE_ATTACKING 12 // Ditto
+/*
#define STANCE_IDLE 1 // Looking for targets if hostile. Does idle wandering.
#define STANCE_ALERT 2 // Bears
#define STANCE_ATTACK 3 // Attempting to get into attack position
@@ -34,6 +37,20 @@
#define STANCE_TIRED 5 // Bears
#define STANCE_FOLLOW 6 // Following somone
#define STANCE_BUSY 7 // Do nothing on life ticks (Other code is running)
+*/
+#define STANCE_SLEEP 0 // Doing (almost) nothing, to save on CPU because nobody is around to notice or the mob died.
+#define STANCE_IDLE 1 // The more or less default state. Wanders around, looks for baddies, and spouts one-liners.
+#define STANCE_ALERT 2 // A baddie is visible but not too close, and essentially we tell them to go away or die.
+#define STANCE_APPROACH 3 // Attempting to get into range to attack them.
+#define STANCE_FIGHT 4 // Actually fighting, with melee or ranged.
+#define STANCE_BLINDFIGHT 5 // Fighting something that cannot be seen by the mob, from invisibility or out of sight.
+#define STANCE_REPOSITION 6 // Relocating to a better position while in combat. Also used when moving away from a danger like grenades.
+#define STANCE_MOVE 7 // Similar to above but for out of combat. If a baddie is seen, they'll cancel and fight them.
+#define STANCE_FOLLOW 8 // Following somone, without trying to murder them.
+#define STANCE_FLEE 9 // Run away from the target because they're too spooky/we're dying/some other reason.
+#define STANCE_DISABLED 10 // Used when the holder is afflicted with certain status effects, such as stuns or confusion.
+
+#define STANCES_COMBAT list(STANCE_ALERT, STANCE_APPROACH, STANCE_FIGHT, STANCE_BLINDFIGHT, STANCE_REPOSITION)
#define LEFT 0x1
#define RIGHT 0x2
@@ -279,11 +296,34 @@
#define SA_ROBOTIC 3
#define SA_HUMANOID 4
+// More refined version of SA_* ""intelligence"" seperators.
+// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID'
+#define MOB_CLASS_NONE 0 // Default value, and used to invert for _ALL.
+
+#define MOB_CLASS_PLANT 1 // Unused at the moment.
+#define MOB_CLASS_ANIMAL 2 // Animals and beasts like spiders, saviks, and bears.
+#define MOB_CLASS_HUMANOID 4 // Non-robotic humanoids, including /simple_mob and /carbon/humans and their alien variants.
+#define MOB_CLASS_SYNTHETIC 8 // Silicons, mechanical simple mobs, FBPs, and anything else that would pass is_synthetic()
+#define MOB_CLASS_SLIME 16 // Everyone's favorite xenobiology specimen (and maybe prometheans?).
+#define MOB_CLASS_ABERRATION 32 // Weird shit.
+#define MOB_CLASS_DEMONIC 64 // Cult stuff.
+#define MOB_CLASS_BOSS 128 // Future megafauna hopefully someday.
+#define MOB_CLASS_ILLUSION 256 // Fake mobs, e.g. Technomancer illusions.
+#define MOB_CLASS_PHOTONIC 512 // Holographic mobs like holocarp, similar to _ILLUSION, but that make no attempt to hide their true nature.
+
+#define MOB_CLASS_ALL (~MOB_CLASS_NONE)
+
// For slime commanding. Higher numbers allow for more actions.
#define SLIME_COMMAND_OBEY 1 // When disciplined.
#define SLIME_COMMAND_FACTION 2 // When in the same 'faction'.
#define SLIME_COMMAND_FRIEND 3 // When befriended with a slime friendship agent.
+// Threshold for mobs being able to damage things like airlocks or reinforced glass windows.
+// If the damage is below this, nothing will happen besides a message saying that the attack was ineffective.
+// Generally, this was not a define but was commonly set to 10, however 10 may be too low now since simple_mobs now attack twice as fast,
+// at half damage compared to the old mob system, meaning mobs who could hurt structures may not be able to now, so now it is 5.
+#define STRUCTURE_MIN_DAMAGE_THRESHOLD 5
+
//Vision flags, for dealing with plane visibility
#define VIS_FULLBRIGHT 1
#define VIS_LIGHTING 2
diff --git a/code/__defines/objects.dm b/code/__defines/objects.dm
new file mode 100644
index 0000000000..879bed2cd1
--- /dev/null
+++ b/code/__defines/objects.dm
@@ -0,0 +1,10 @@
+/*
+ * Defines used for miscellaneous objects.
+ * Currently only the home of the Multiool.
+ */
+
+// Multitool Mode Defines.
+
+#define MULTITOOL_MODE_STANDARD "Standard"
+#define MULTITOOL_MODE_INTCIRCUITS "Modular Wiring"
+#define MULTITOOL_MODE_DOORHACK "Advanced Jacking"
diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm
index 82ec12a211..ab85326658 100644
--- a/code/__defines/qdel.dm
+++ b/code/__defines/qdel.dm
@@ -20,4 +20,17 @@
#define QDELING(X) (X.gc_destroyed)
#define QDELETED(X) (!X || X.gc_destroyed)
-#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
\ No newline at end of file
+#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+
+//Qdel helper macros.
+#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
+#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
+#define QDEL_NULL(item) qdel(item); item = null
+#define QDEL_LIST_NULL(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
+#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
+#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)
+#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
+#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
+
+/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly.
+ QDEL_LIST(L)
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index e8656edd32..409448bef3 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -52,31 +52,37 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
// Subsystem init_order, from highest priority to lowest priority
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
-#define INIT_ORDER_DECALS 16
-#define INIT_ORDER_ATOMS 15
-#define INIT_ORDER_MACHINES 10
-#define INIT_ORDER_SHUTTLES 3
-#define INIT_ORDER_TIMER 1
-#define INIT_ORDER_DEFAULT 0
-#define INIT_ORDER_LIGHTING 0
-#define INIT_ORDER_AIR -1
-#define INIT_ORDER_PLANETS -4
-#define INIT_ORDER_HOLOMAPS -5
-#define INIT_ORDER_OVERLAY -6
-#define INIT_ORDER_XENOARCH -20
+#define INIT_ORDER_DECALS 16
+#define INIT_ORDER_ATOMS 15
+#define INIT_ORDER_MACHINES 10
+#define INIT_ORDER_SHUTTLES 3
+#define INIT_ORDER_TIMER 1
+#define INIT_ORDER_DEFAULT 0
+#define INIT_ORDER_LIGHTING 0
+#define INIT_ORDER_AIR -1
+#define INIT_ORDER_PLANETS -4
+#define INIT_ORDER_HOLOMAPS -5
+#define INIT_ORDER_OVERLAY -6
+#define INIT_ORDER_XENOARCH -20
+#define INIT_ORDER_CIRCUIT -21
+#define INIT_ORDER_AI -22
// Subsystem fire priority, from lowest to highest priority
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
#define FIRE_PRIORITY_SHUTTLES 5
#define FIRE_PRIORITY_ORBIT 8
+#define FIRE_PRIORITY_VOTE 9
+#define FIRE_PRIORITY_AI 10
#define FIRE_PRIORITY_GARBAGE 15
#define FIRE_PRIORITY_AIRFLOW 30
#define FIRE_PRIORITY_AIR 35
-#define FIRE_PRIORITY_PROCESS 35
+#define FIRE_PRIORITY_OBJ 40
+#define FIRE_PRIORITY_PROCESS 45
#define FIRE_PRIORITY_DEFAULT 50
#define FIRE_PRIORITY_PLANETS 75
#define FIRE_PRIORITY_MACHINES 100
+#define FIRE_PRIORITY_PROJECTILES 150
#define FIRE_PRIORITY_OVERLAYS 500
// Macro defining the actual code applying our overlays lists to the BYOND overlays list. (I guess a macro for speed)
diff --git a/code/__defines/typeids.dm b/code/__defines/typeids.dm
new file mode 100644
index 0000000000..9af310012b
--- /dev/null
+++ b/code/__defines/typeids.dm
@@ -0,0 +1,6 @@
+//Byond type ids
+#define TYPEID_NULL "0"
+#define TYPEID_NORMAL_LIST "f"
+//helper macros
+#define GET_TYPEID(ref) ( ( (lentext(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, lentext(ref)-6) ) )
+#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
\ No newline at end of file
diff --git a/code/__defines/vote.dm b/code/__defines/vote.dm
new file mode 100644
index 0000000000..48be19e909
--- /dev/null
+++ b/code/__defines/vote.dm
@@ -0,0 +1,5 @@
+#define VOTE_RESTART "restart"
+#define VOTE_GAMEMODE "gamemode"
+#define VOTE_CREW_TRANSFER "crew_transfer"
+#define VOTE_ADD_ANTAGONIST "add_antagonist"
+#define VOTE_CUSTOM "custom"
diff --git a/code/_global_vars/misc.dm b/code/_global_vars/misc.dm
index de2779c45b..504246e304 100644
--- a/code/_global_vars/misc.dm
+++ b/code/_global_vars/misc.dm
@@ -1 +1,5 @@
-GLOBAL_LIST_EMPTY(all_observable_events)
\ No newline at end of file
+GLOBAL_LIST_EMPTY(all_observable_events)
+
+GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks.
+
+GLOBAL_VAR_INIT(TAB, " ")
diff --git a/code/_global_vars/mobs.dm b/code/_global_vars/mobs.dm
index 7fa2b5d162..a5099a68d2 100644
--- a/code/_global_vars/mobs.dm
+++ b/code/_global_vars/mobs.dm
@@ -1,2 +1,5 @@
GLOBAL_LIST_EMPTY(admins) //all clients whom are admins
-GLOBAL_PROTECT(admins)
\ No newline at end of file
+GLOBAL_PROTECT(admins)
+GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb.
+GLOBAL_LIST_EMPTY(stealthminID)
+GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm
index 946a9fc886..1e00d5d7f4 100644
--- a/code/_helpers/_lists.dm
+++ b/code/_helpers/_lists.dm
@@ -281,6 +281,17 @@ proc/listclearnulls(list/list)
for(var/i in L)
. |= i
+//same, but returns nothing and acts on list in place (also handles associated values properly)
+/proc/uniqueList_inplace(list/L)
+ var/temp = L.Copy()
+ L.len = 0
+ for(var/key in temp)
+ if (isnum(key))
+ L |= key
+ else
+ L[key] = temp[key]
+
+
//Mergesort: divides up the list into halves to begin the sort
/proc/sortKey(var/list/client/L, var/order = 1)
if(isnull(L) || L.len < 2)
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index a9ac87e4aa..9ba432779f 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -26,17 +26,17 @@
max_z = max(z, max_z)
return max_z
-/proc/get_area(O)
- var/turf/loc = get_turf(O)
- if(loc)
- var/area/res = loc.loc
- .= res
+/proc/get_area(atom/A)
+ if(isarea(A))
+ return A
+ var/turf/T = get_turf(A)
+ return T ? T.loc : null
-/proc/get_area_name(N) //get area by its name
- for(var/area/A in all_areas)
- if(A.name == N)
- return A
- return 0
+/proc/get_area_name(atom/X, format_text = FALSE)
+ var/area/A = isarea(X) ? X : get_area(X)
+ if(!A)
+ return null
+ return format_text ? format_text(A.name) : A.name
/proc/get_area_master(const/O)
var/area/A = get_area(O)
@@ -602,3 +602,8 @@ datum/projectile_data
/proc/SecondsToTicks(var/seconds)
return seconds * 10
+
+/proc/window_flash(var/client_or_usr)
+ if (!client_or_usr)
+ return
+ winset(client_or_usr, "mainwindow", "flash=5")
\ No newline at end of file
diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm
index da4385ef6f..c6cae7b464 100644
--- a/code/_helpers/icons.dm
+++ b/code/_helpers/icons.dm
@@ -634,7 +634,6 @@ as a single icon. Useful for when you want to manipulate an icon via the above a
The _flatIcons list is a cache for generated icon files.
*/
-// Creates a single icon from a given /atom or /image. Only the first argument is required.
/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE)
// We start with a blank canvas, otherwise some icon procs crash silently
var/icon/flat = icon('icons/effects/effects.dmi', "nothing") // Final flattened icon
@@ -677,7 +676,7 @@ The _flatIcons list is a cache for generated icon files.
var/curdir
var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have
-
+
//These should use the parent's direction (most likely)
if(!A.dir || A.dir == SOUTH)
curdir = defdir
@@ -686,7 +685,7 @@ The _flatIcons list is a cache for generated icon files.
//Let's check if the icon actually contains any diagonals, just skip if it's south to save (lot of) time
if(curdir != SOUTH)
- var/icon/test_icon
+ var/icon/test_icon
var/directionals_exist = FALSE
var/list/dirs_to_check = cardinal - SOUTH
outer:
@@ -824,6 +823,9 @@ The _flatIcons list is a cache for generated icon files.
else
return icon(flat, "", SOUTH)
+
+
+
/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A.
for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it.
@@ -874,9 +876,10 @@ The _flatIcons list is a cache for generated icon files.
if(4) I.pixel_y++
overlays += I//And finally add the overlay.
-/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created.
+/proc/getHologramIcon(icon/A, safety=1, no_color = FALSE)//If safety is on, a new icon is not created.
var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon.
- flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish.
+ if(!no_color)
+ flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish.
flat_icon.ChangeOpacity(0.5)//Make it half transparent.
var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect.
flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect.
diff --git a/code/_helpers/maths.dm b/code/_helpers/maths.dm
deleted file mode 100644
index 4cbe75bffc..0000000000
--- a/code/_helpers/maths.dm
+++ /dev/null
@@ -1,131 +0,0 @@
-// Macro functions.
-#define RAND_F(LOW, HIGH) (rand()*(HIGH-LOW) + LOW)
-#define ceil(x) (-round(-(x)))
-
-// min is inclusive, max is exclusive
-/proc/Wrap(val, min, max)
- var/d = max - min
- var/t = Floor((val - min) / d)
- return val - (t * d)
-
-/proc/Default(a, b)
- return a ? a : b
-
-// Trigonometric functions.
-/proc/Tan(x)
- return sin(x) / cos(x)
-
-/proc/Csc(x)
- return 1 / sin(x)
-
-/proc/Sec(x)
- return 1 / cos(x)
-
-/proc/Cot(x)
- return 1 / Tan(x)
-
-/proc/Atan2(x, y)
- if(!x && !y) return 0
- var/a = arccos(x / sqrt(x*x + y*y))
- return y >= 0 ? a : -a
-
-/proc/Floor(x)
- return round(x)
-
-/proc/Ceiling(x)
- return -round(-x)
-
-// Greatest Common Divisor: Euclid's algorithm.
-/proc/Gcd(a, b)
- while (1)
- if (!b) return a
- a %= b
- if (!a) return b
- b %= a
-
-// Least Common Multiple. The formula is a consequence of: a*b = LCM*GCD.
-/proc/Lcm(a, b)
- return abs(a) * abs(b) / Gcd(a, b)
-
-// Useful in the cases when x is a large expression, e.g. x = 3a/2 + b^2 + Function(c)
-/proc/Square(x)
- return x*x
-
-/proc/Inverse(x)
- return 1 / x
-
-// Condition checks.
-/proc/IsAboutEqual(a, b, delta = 0.1)
- return abs(a - b) <= delta
-
-// Returns true if val is from min to max, inclusive.
-/proc/IsInRange(val, min, max)
- return (val >= min) && (val <= max)
-
-/proc/IsInteger(x)
- return Floor(x) == x
-
-/proc/IsMultiple(x, y)
- return x % y == 0
-
-/proc/IsEven(x)
- return !(x & 0x1)
-
-/proc/IsOdd(x)
- return (x & 0x1)
-
-// Performs a linear interpolation between a and b.
-// Note: weight=0 returns a, weight=1 returns b, and weight=0.5 returns the mean of a and b.
-/proc/Interpolate(a, b, weight = 0.5)
- return a + (b - a) * weight // Equivalent to: a*(1 - weight) + b*weight
-
-/proc/Mean(...)
- var/sum = 0
- for(var/val in args)
- sum += val
- return sum / args.len
-
-// Returns the nth root of x.
-/proc/Root(n, x)
- return x ** (1 / n)
-
-// The quadratic formula. Returns a list with the solutions, or an empty list
-// if they are imaginary.
-/proc/SolveQuadratic(a, b, c)
- ASSERT(a)
-
- . = list()
- var/discriminant = b*b - 4*a*c
- var/bottom = 2*a
-
- // Return if the roots are imaginary.
- if(discriminant < 0)
- return
-
- var/root = sqrt(discriminant)
- . += (-b + root) / bottom
-
- // If discriminant == 0, there would be two roots at the same position.
- if(discriminant != 0)
- . += (-b - root) / bottom
-
-/proc/ToDegrees(radians)
- // 180 / Pi ~ 57.2957795
- return radians * 57.2957795
-
-/proc/ToRadians(degrees)
- // Pi / 180 ~ 0.0174532925
- return degrees * 0.0174532925
-
-// Vector algebra.
-/proc/squaredNorm(x, y)
- return x*x + y*y
-
-/proc/norm(x, y)
- return sqrt(squaredNorm(x, y))
-
-/proc/IsPowerOfTwo(var/val)
- return (val & (val-1)) == 0
-
-/proc/RoundUpToPowerOfTwo(var/val)
- return 2 ** -round(-log(2,val))
diff --git a/code/_helpers/sorts/comparators.dm b/code/_helpers/sorts/comparators.dm
index 73ad3d847c..6b8491fd89 100644
--- a/code/_helpers/sorts/comparators.dm
+++ b/code/_helpers/sorts/comparators.dm
@@ -45,3 +45,6 @@
. = B[STAT_ENTRY_TIME] - A[STAT_ENTRY_TIME]
if (!.)
. = B[STAT_ENTRY_COUNT] - A[STAT_ENTRY_COUNT]
+
+/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b)
+ return a.timeToRun - b.timeToRun
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index 43c18e1cc9..d4c918a55a 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -64,6 +64,11 @@ var/next_station_date_change = 1 DAY
var/time_portion = time2text(world.timeofday, "hh:mm:ss")
return "[date_portion]T[time_portion]"
+/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
+ if(!wtime)
+ wtime = world.time
+ return time2text(wtime - GLOB.timezoneOffset, format)
+
/* Returns 1 if it is the selected month and day */
proc/isDay(var/month, var/day)
if(isnum(month) && isnum(day))
@@ -135,6 +140,103 @@ var/round_start_time = 0
. += CEILING(i*DELTA_CALC, 1)
sleep(i*world.tick_lag*DELTA_CALC)
i *= 2
- while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, GLOB.CURRENT_TICKLIMIT))
+ while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
-#undef DELTA_CALC
\ No newline at end of file
+#undef DELTA_CALC
+
+
+//Takes a value of time in deciseconds.
+//Returns a text value of that number in hours, minutes, or seconds.
+/proc/DisplayTimeText(time_value, truncate = FALSE)
+ var/second = (time_value)*0.1
+ var/second_adjusted = null
+ var/second_rounded = FALSE
+ var/minute = null
+ var/hour = null
+ var/day = null
+
+ if(!second)
+ return "0 seconds"
+ if(second >= 60)
+ minute = FLOOR(second/60, 1)
+ second = round(second - (minute*60), 0.1)
+ second_rounded = TRUE
+ if(second) //check if we still have seconds remaining to format, or if everything went into minute.
+ second_adjusted = round(second) //used to prevent '1 seconds' being shown
+ if(day || hour || minute)
+ if(second_adjusted == 1 && second >= 1)
+ second = " and 1 second"
+ else if(second > 1)
+ second = " and [second_adjusted] seconds"
+ else //shows a fraction if seconds is < 1
+ if(second_rounded) //no sense rounding again if it's already done
+ second = " and [second] seconds"
+ else
+ second = " and [round(second, 0.1)] seconds"
+ else
+ if(second_adjusted == 1 && second >= 1)
+ second = "[truncate ? "second" : "1 second"]"
+ else if(second > 1)
+ second = "[second_adjusted] seconds"
+ else
+ if(second_rounded)
+ second = "[second] seconds"
+ else
+ second = "[round(second, 0.1)] seconds"
+ else
+ second = null
+
+ if(!minute)
+ return "[second]"
+ if(minute >= 60)
+ hour = FLOOR(minute/60, 1)
+ minute = (minute - (hour*60))
+ if(minute) //alot simpler from here since you don't have to worry about fractions
+ if(minute != 1)
+ if((day || hour) && second)
+ minute = ", [minute] minutes"
+ else if((day || hour) && !second)
+ minute = " and [minute] minutes"
+ else
+ minute = "[minute] minutes"
+ else
+ if((day || hour) && second)
+ minute = ", 1 minute"
+ else if((day || hour) && !second)
+ minute = " and 1 minute"
+ else
+ minute = "[truncate ? "minute" : "1 minute"]"
+ else
+ minute = null
+
+ if(!hour)
+ return "[minute][second]"
+ if(hour >= 24)
+ day = FLOOR(hour/24, 1)
+ hour = (hour - (day*24))
+ if(hour)
+ if(hour != 1)
+ if(day && (minute || second))
+ hour = ", [hour] hours"
+ else if(day && (!minute || !second))
+ hour = " and [hour] hours"
+ else
+ hour = "[hour] hours"
+ else
+ if(day && (minute || second))
+ hour = ", 1 hour"
+ else if(day && (!minute || !second))
+ hour = " and 1 hour"
+ else
+ hour = "[truncate ? "hour" : "1 hour"]"
+ else
+ hour = null
+
+ if(!day)
+ return "[hour][minute][second]"
+ if(day > 1)
+ day = "[day] days"
+ else
+ day = "[truncate ? "day" : "1 day"]"
+
+ return "[day][hour][minute][second]"
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 1af2fe7034..13689de7e0 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -501,7 +501,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
moblist.Add(M)
for(var/mob/new_player/M in sortmob)
moblist.Add(M)
- for(var/mob/living/simple_animal/M in sortmob)
+ for(var/mob/living/simple_mob/M in sortmob)
moblist.Add(M)
// for(var/mob/living/silicon/hivebot/M in sortmob)
// mob_list.Add(M)
@@ -519,6 +519,14 @@ Turf and target are seperate in case you want to teleport some distance from a t
return "[round((powerused * 0.000001),0.001)] MW"
return "[round((powerused * 0.000000001),0.0001)] GW"
+/proc/get_mob_by_ckey(key)
+ if(!key)
+ return
+ var/list/mobs = sortmobs()
+ for(var/mob/M in mobs)
+ if(M.ckey == key)
+ return M
+
//Forces a variable to be posative
/proc/modulus(var/M)
if(M >= 0)
@@ -1443,4 +1451,4 @@ var/mob/dview/dview_mob = new
return "North-Northwest"
/proc/pass()
- return
\ No newline at end of file
+ return
diff --git a/code/_macros.dm b/code/_macros.dm
index d8737d8d26..13afdb27b8 100644
--- a/code/_macros.dm
+++ b/code/_macros.dm
@@ -10,7 +10,7 @@
#define isalien(A) istype(A, /mob/living/carbon/alien)
-#define isanimal(A) istype(A, /mob/living/simple_animal)
+#define isanimal(A) istype(A, /mob/living/simple_mob)
#define isairlock(A) istype(A, /obj/machinery/door/airlock)
@@ -18,7 +18,7 @@
#define iscarbon(A) istype(A, /mob/living/carbon)
-#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi)
+#define iscorgi(A) istype(A, /mob/living/simple_mob/animal/passive/dog/corgi)
#define isEye(A) istype(A, /mob/observer/eye)
@@ -26,7 +26,7 @@
#define isliving(A) istype(A, /mob/living)
-#define ismouse(A) istype(A, /mob/living/simple_animal/mouse)
+#define ismouse(A) istype(A, /mob/living/simple_mob/animal/passive/mouse)
#define isnewplayer(A) istype(A, /mob/new_player)
@@ -42,11 +42,11 @@
#define isvoice(A) istype(A, /mob/living/voice)
-#define isslime(A) istype(A, /mob/living/simple_animal/slime)
+#define isslime(A) istype(A, /mob/living/simple_mob/slime)
#define isbot(A) istype(A, /mob/living/bot)
-#define isxeno(A) istype(A, /mob/living/simple_animal/xeno)
+#define isxeno(A) istype(A, /mob/living/simple_mob/animal/space/alien)
#define isopenspace(A) istype(A, /turf/simulated/open)
@@ -65,30 +65,4 @@
#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)
-#define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
-
-#define QDEL_NULL(x) if(x) { qdel(x) ; x = null }
-
#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") }
-
-// Helper macros to aid in optimizing lazy instantiation of lists.
-// All of these are null-safe, you can use them without knowing if the list var is initialized yet
-
-//Picks from the list, with some safeties, and returns the "default" arg if it fails
-#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
-// Ensures L is initailized after this point
-#define LAZYINITLIST(L) if (!L) L = list()
-// Sets a L back to null iff it is empty
-#define UNSETEMPTY(L) if (L && !L.len) L = null
-// Removes I from list L, and sets I to null if it is now empty
-#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
-// Adds I to L, initalizing I if necessary
-#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
-// Reads I from L safely - Works with both associative and traditional lists.
-#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
-// Reads the length of L, returning 0 if null
-#define LAZYLEN(L) length(L)
-// Null-safe L.Cut()
-#define LAZYCLEARLIST(L) if(L) L.Cut()
-// Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression.
-#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
\ No newline at end of file
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index 94a067200a..2a92ba20bc 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -98,7 +98,7 @@
/obj/screen/fullscreen/flash
icon = 'icons/mob/screen1.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
- icon_state = "flash"
+ icon_state = "flash_static"
/obj/screen/fullscreen/flash/noise
icon_state = "noise"
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index b5a4e68d6f..ba2e73c1a0 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -310,8 +310,6 @@ datum/hud/New(mob/owner)
mymob.instantiate_hud(src)
else if(isalien(mymob))
larva_hud()
- else if(isslime(mymob))
- slime_hud()
else if(isAI(mymob))
ai_hud()
else if(isobserver(mymob))
diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm
index f1b9165a26..b0f55f5503 100644
--- a/code/_onclick/hud/other_mobs.dm
+++ b/code/_onclick/hud/other_mobs.dm
@@ -23,7 +23,7 @@
mymob.client.screen += list(blobpwrdisplay, blobhealthdisplay)
mymob.client.screen += mymob.client.void
-
+/*
/datum/hud/proc/slime_hud(ui_style = 'icons/mob/screen1_Midnight.dmi')
src.adding = list()
@@ -92,10 +92,7 @@
mymob.client.screen += mymob.client.void
return
-
-
-/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
- ..(HUD)
+*/
// HUD.construct_hud() //Archaic.
/*
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 8baf430138..afe99e2c1d 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -59,7 +59,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
// Same as above but actually does useful things.
// W is the item being used in the attack, if any. modifier is if the attack should be longer or shorter than usual, for whatever reason.
/mob/living/get_attack_speed(var/obj/item/W)
- var/speed = DEFAULT_ATTACK_COOLDOWN
+ var/speed = base_attack_cooldown
if(W && istype(W))
speed = W.attackspeed
for(var/datum/modifier/M in modifiers)
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 729330ddaa..e367769b07 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -68,58 +68,3 @@
*/
/mob/new_player/ClickOn()
return
-
-/*
- Animals
-*/
-/mob/living/simple_animal/UnarmedAttack(var/atom/A, var/proximity)
- if(!(. = ..()))
- return
-
- setClickCooldown(get_attack_speed())
-
- if(has_hands && istype(A,/obj) && a_intent != I_HURT)
- var/obj/O = A
- return O.attack_hand(src)
-
- switch(a_intent)
- if(I_HELP)
- if(isliving(A))
- custom_emote(1,"[pick(friendly)] [A]!")
-
- if(I_HURT)
- if(prob(spattack_prob))
- if(spattack_min_range <= 1)
- SpecialAtkTarget()
-
-
- else if(melee_damage_upper == 0 && istype(A,/mob/living))
- custom_emote(1,"[pick(friendly)] [A]!")
-
-
- else
- DoPunch(A)
-
- if(I_GRAB)
- if(has_hands)
- A.attack_hand(src)
-
- if(I_DISARM)
- if(has_hands)
- A.attack_hand(src)
-
-/mob/living/simple_animal/RangedAttack(var/atom/A)
- setClickCooldown(get_attack_speed())
- var/distance = get_dist(src, A)
-
- if(prob(spattack_prob) && (distance >= spattack_min_range) && (distance <= spattack_max_range))
- target_mob = A
- SpecialAtkTarget()
- target_mob = null
- return
-
- if(ranged && distance <= shoot_range)
- target_mob = A
- ShootTarget(A)
- target_mob = null
-
diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm
deleted file mode 100644
index 6032cbb541..0000000000
--- a/code/controllers/Processes/obj.dm
+++ /dev/null
@@ -1,26 +0,0 @@
-/datum/controller/process/obj/setup()
- name = "obj"
- schedule_interval = 20 // every 2 seconds
- start_delay = 8
-
-/datum/controller/process/obj/started()
- ..()
- if(!processing_objects)
- processing_objects = list()
-
-/datum/controller/process/obj/doWork()
- for(last_object in processing_objects)
- var/datum/O = last_object
- if(!QDELETED(O))
- try
- O:process()
- catch(var/exception/e)
- catchException(e, O)
- SCHECK
- else
- catchBadType(O)
- processing_objects -= O
-
-/datum/controller/process/obj/statProcess()
- ..()
- stat(null, "[processing_objects.len] objects")
\ No newline at end of file
diff --git a/code/controllers/Processes/sun.dm b/code/controllers/Processes/sun.dm
deleted file mode 100644
index f09806cef5..0000000000
--- a/code/controllers/Processes/sun.dm
+++ /dev/null
@@ -1,7 +0,0 @@
-/datum/controller/process/sun/setup()
- name = "sun"
- schedule_interval = 20 // every second
- sun = new
-
-/datum/controller/process/sun/doWork()
- sun.calc_position()
diff --git a/code/controllers/Processes/vote.dm b/code/controllers/Processes/vote.dm
deleted file mode 100644
index 5df5ce6979..0000000000
--- a/code/controllers/Processes/vote.dm
+++ /dev/null
@@ -1,6 +0,0 @@
-/datum/controller/process/vote/setup()
- name = "vote"
- schedule_interval = 10 // every second
-
-/datum/controller/process/vote/doWork()
- vote.process()
diff --git a/code/controllers/autotransfer.dm b/code/controllers/autotransfer.dm
index c9bc4ac2c0..6f68a8d9dc 100644
--- a/code/controllers/autotransfer.dm
+++ b/code/controllers/autotransfer.dm
@@ -5,13 +5,13 @@ datum/controller/transfer_controller
var/currenttick = 0
datum/controller/transfer_controller/New()
timerbuffer = config.vote_autotransfer_initial
- processing_objects += src
+ START_PROCESSING(SSobj, src)
datum/controller/transfer_controller/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
datum/controller/transfer_controller/process()
currenttick = currenttick + 1
if (round_duration_in_ticks >= timerbuffer - 1 MINUTE)
- vote.autotransfer()
+ SSvote.autotransfer()
timerbuffer = timerbuffer + config.vote_autotransfer_interval
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 9e29ba9998..3a31de1c6a 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -228,6 +228,8 @@ var/list/gamemode_cache = list()
var/radiation_resistance_multiplier = 6.5
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
+ var/random_submap_orientation = FALSE // If true, submaps loaded automatically can be rotated.
+
/datum/configuration/New()
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
for (var/T in L)
@@ -748,6 +750,9 @@ var/list/gamemode_cache = list()
if ("paranoia_logging")
config.paranoia_logging = 1
+ if("random_submap_orientation")
+ config.random_submap_orientation = 1
+
else
log_misc("Unknown setting in configuration: '[name]'")
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 050251fb0d..0b292e45c4 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -13,14 +13,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
//THIS IS THE INIT ORDER
//Master -> SSPreInit -> GLOB -> world -> config -> SSInit -> Failsafe
//GOT IT MEMORIZED?
-GLOBAL_VAR_INIT(MC_restart_clear, 0)
-GLOBAL_VAR_INIT(MC_restart_timeout, 0)
-GLOBAL_VAR_INIT(MC_restart_count, 0)
-
-//current tick limit, assigned by the queue controller before running a subsystem.
-//used by check_tick as well so that the procs subsystems call can obey that SS's tick limits
-GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
-
/datum/controller/master
name = "Master"
@@ -62,6 +54,10 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
var/static/restart_timeout = 0
var/static/restart_count = 0
+ //current tick limit, assigned by the queue controller before running a subsystem.
+ //used by check_tick as well so that the procs subsystems call can obey that SS's tick limits
+ var/static/current_ticklimit
+
/datum/controller/master/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
var/list/_subsystems = list()
@@ -98,14 +94,14 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// -1 if we encountered a runtime trying to recreate it
/proc/Recreate_MC()
. = -1 //so if we runtime, things know we failed
- if (world.time < GLOB.MC_restart_timeout)
+ if (world.time < Master.restart_timeout)
return 0
- if (world.time < GLOB.MC_restart_clear)
- GLOB.MC_restart_count *= 0.5
+ if (world.time < Master.restart_clear)
+ Master.restart_count *= 0.5
- var/delay = 50 * ++GLOB.MC_restart_count
- GLOB.MC_restart_timeout = world.time + delay
- GLOB.MC_restart_clear = world.time + (delay * 2)
+ var/delay = 50 * ++Master.restart_count
+ Master.restart_timeout = world.time + delay
+ Master.restart_clear = world.time + (delay * 2)
Master.processing = FALSE //stop ticking this one
try
new/datum/controller/master()
@@ -176,13 +172,13 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
var/start_timeofday = REALTIMEOFDAY
// Initialize subsystems.
- GLOB.CURRENT_TICKLIMIT = config.tick_limit_mc_init
+ current_ticklimit = config.tick_limit_mc_init
for (var/datum/controller/subsystem/SS in subsystems)
if (SS.flags & SS_NO_INIT)
continue
SS.Initialize(REALTIMEOFDAY)
CHECK_TICK
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
+ current_ticklimit = TICK_LIMIT_RUNNING
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
@@ -291,7 +287,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
var/starting_tick_usage = TICK_USAGE
if (processing <= 0)
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
+ current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
@@ -300,7 +296,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// (because sleeps are processed in the order received, longer sleeps are more likely to run first)
if (starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
sleep_delta *= 2
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING * 0.5
+ current_ticklimit = TICK_LIMIT_RUNNING * 0.5
sleep(world.tick_lag * (processing * sleep_delta))
continue
@@ -346,7 +342,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
if (!error_level)
iteration++
error_level++
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
+ current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
@@ -358,7 +354,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
if (!error_level)
iteration++
error_level++
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
+ current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
error_level--
@@ -369,9 +365,9 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
+ current_ticklimit = TICK_LIMIT_RUNNING
if (processing * sleep_delta <= world.tick_lag)
- GLOB.CURRENT_TICKLIMIT -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
+ current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
sleep(world.tick_lag * (processing * sleep_delta))
@@ -463,7 +459,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// Reduce tick allocation for subsystems that overran on their last tick.
tick_precentage = max(tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
- GLOB.CURRENT_TICKLIMIT = round(TICK_USAGE + tick_precentage)
+ current_ticklimit = round(TICK_USAGE + tick_precentage)
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
diff --git a/code/controllers/subsystems/ai.dm b/code/controllers/subsystems/ai.dm
new file mode 100644
index 0000000000..8977818755
--- /dev/null
+++ b/code/controllers/subsystems/ai.dm
@@ -0,0 +1,36 @@
+SUBSYSTEM_DEF(ai)
+ name = "AI"
+ init_order = INIT_ORDER_AI
+ priority = FIRE_PRIORITY_AI
+ wait = 5 // This gets run twice a second, however this is technically two loops in one, with the second loop being run every four iterations.
+ flags = SS_NO_INIT|SS_TICKER
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/list/processing = list()
+ var/list/currentrun = list()
+
+/datum/controller/subsystem/ai/stat_entry(msg_prefix)
+ var/list/msg = list(msg_prefix)
+ msg += "P:[processing.len]"
+ ..(msg.Join())
+
+/datum/controller/subsystem/ai/fire(resumed = 0)
+ if (!resumed)
+ src.currentrun = processing.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(currentrun.len)
+ // var/mob/living/L = currentrun[currentrun.len]
+ var/datum/ai_holder/A = currentrun[currentrun.len]
+ --currentrun.len
+ if(!A || QDELETED(A)) // Doesn't exist or won't exist soon.
+ continue
+ if(times_fired % 4 == 0 && A.holder.stat != DEAD)
+ A.handle_strategicals()
+ if(A.holder.stat != DEAD) // The /TG/ version checks stat twice, presumably in-case processing somehow got the mob killed in that instant.
+ A.handle_tactics()
+
+ if(MC_TICK_CHECK)
+ return
diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm
index 7dae153d66..60fccf6dd3 100644
--- a/code/controllers/subsystems/atoms.dm
+++ b/code/controllers/subsystems/atoms.dm
@@ -77,7 +77,7 @@ SUBSYSTEM_DEF(atoms)
var/start_tick = world.time
- var/result = A.initialize(arglist(arguments))
+ var/result = A.Initialize(arglist(arguments))
if(start_tick != world.time)
BadInitializeCalls[the_type] |= BAD_INIT_SLEPT
diff --git a/code/controllers/subsystems/circuits.dm b/code/controllers/subsystems/circuits.dm
new file mode 100644
index 0000000000..8fda048c60
--- /dev/null
+++ b/code/controllers/subsystems/circuits.dm
@@ -0,0 +1,96 @@
+//
+// This is for custom circuits, mostly the initialization of global properties about them.
+// Might make this also process them in the future if its better to do that than using the obj ticker.
+//
+SUBSYSTEM_DEF(circuit)
+ name = "Circuit"
+ init_order = INIT_ORDER_CIRCUIT
+ flags = SS_NO_FIRE
+ var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs
+ var/list/cached_components = list() // Associative list of [component_path]:[component] pairs
+ var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs
+ var/list/cached_assemblies = list() // Associative list of [assembly_path]:[assembly] pairs
+ var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs
+ var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs
+// var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3
+
+/datum/controller/subsystem/circuit/Recover()
+ flags |= SS_NO_INIT // Make extra sure we don't initialize twice.
+
+/datum/controller/subsystem/circuit/Initialize(timeofday)
+ circuits_init()
+ return ..()
+
+/datum/controller/subsystem/circuit/proc/circuits_init()
+ //Cached lists for free performance
+ for(var/path in typesof(/obj/item/integrated_circuit))
+ var/obj/item/integrated_circuit/IC = path
+ var/name = initial(IC.name)
+ all_components[name] = path // Populating the component lists
+ cached_components[IC] = new path
+
+ if(!(initial(IC.spawn_flags) & (IC_SPAWN_DEFAULT | IC_SPAWN_RESEARCH)))
+ continue
+
+ var/category = initial(IC.category_text)
+ if(!circuit_fabricator_recipe_list[category])
+ circuit_fabricator_recipe_list[category] = list()
+ var/list/category_list = circuit_fabricator_recipe_list[category]
+ category_list += IC // Populating the fabricator categories
+
+ for(var/path in typesof(/obj/item/device/electronic_assembly))
+ var/obj/item/device/electronic_assembly/A = path
+ var/name = initial(A.name)
+ all_assemblies[name] = path
+ cached_assemblies[A] = new path
+
+
+ circuit_fabricator_recipe_list["Assemblies"] = list(
+ /obj/item/device/electronic_assembly/default,
+ /obj/item/device/electronic_assembly/calc,
+ /obj/item/device/electronic_assembly/clam,
+ /obj/item/device/electronic_assembly/simple,
+ /obj/item/device/electronic_assembly/hook,
+ /obj/item/device/electronic_assembly/pda,
+ /obj/item/device/electronic_assembly/tiny/default,
+ /obj/item/device/electronic_assembly/tiny/cylinder,
+ /obj/item/device/electronic_assembly/tiny/scanner,
+ /obj/item/device/electronic_assembly/tiny/hook,
+ /obj/item/device/electronic_assembly/tiny/box,
+ /obj/item/device/electronic_assembly/medium/default,
+ /obj/item/device/electronic_assembly/medium/box,
+ /obj/item/device/electronic_assembly/medium/clam,
+ /obj/item/device/electronic_assembly/medium/medical,
+ /obj/item/device/electronic_assembly/medium/gun,
+ /obj/item/device/electronic_assembly/medium/radio,
+ /obj/item/device/electronic_assembly/large/default,
+ /obj/item/device/electronic_assembly/large/scope,
+ /obj/item/device/electronic_assembly/large/terminal,
+ /obj/item/device/electronic_assembly/large/arm,
+ /obj/item/device/electronic_assembly/large/tall,
+ /obj/item/device/electronic_assembly/large/industrial,
+ /obj/item/device/electronic_assembly/drone/default,
+ /obj/item/device/electronic_assembly/drone/arms,
+ /obj/item/device/electronic_assembly/drone/secbot,
+ /obj/item/device/electronic_assembly/drone/medbot,
+ /obj/item/device/electronic_assembly/drone/genbot,
+ /obj/item/device/electronic_assembly/drone/android,
+ /obj/item/device/electronic_assembly/wallmount/tiny,
+ /obj/item/device/electronic_assembly/wallmount/light,
+ /obj/item/device/electronic_assembly/wallmount,
+ /obj/item/device/electronic_assembly/wallmount/heavy,
+ /obj/item/weapon/implant/integrated_circuit,
+ /obj/item/clothing/under/circuitry,
+ /obj/item/clothing/gloves/circuitry,
+ /obj/item/clothing/glasses/circuitry,
+ /obj/item/clothing/shoes/circuitry,
+ /obj/item/clothing/head/circuitry,
+ /obj/item/clothing/ears/circuitry,
+ /obj/item/clothing/suit/circuitry
+ )
+
+ circuit_fabricator_recipe_list["Tools"] = list(
+ /obj/item/device/integrated_electronics/wirer,
+ /obj/item/device/integrated_electronics/debugger,
+ /obj/item/device/integrated_electronics/detailer
+ )
diff --git a/code/controllers/Processes/inactivity.dm b/code/controllers/subsystems/inactivity.dm
similarity index 80%
rename from code/controllers/Processes/inactivity.dm
rename to code/controllers/subsystems/inactivity.dm
index 3b118995bf..671b59d918 100644
--- a/code/controllers/Processes/inactivity.dm
+++ b/code/controllers/subsystems/inactivity.dm
@@ -1,11 +1,12 @@
-/datum/controller/process/inactivity/setup()
- name = "inactivity"
- schedule_interval = 600 // Once every minute (approx.)
+SUBSYSTEM_DEF(inactivity)
+ name = "AFK Kick"
+ wait = 600
+ flags = SS_BACKGROUND | SS_NO_TICK_CHECK
-/datum/controller/process/inactivity/doWork()
+/datum/controller/subsystem/inactivity/fire()
if(config.kick_inactive)
- for(last_object in clients)
- var/client/C = last_object
+ for(var/i in clients)
+ var/client/C = i
if(C.is_afk(config.kick_inactive MINUTES))
to_chat(C,"You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.")
var/information
@@ -33,4 +34,3 @@
log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob)
qdel(C)
- SCHECK
diff --git a/code/controllers/subsystems/overlays.dm b/code/controllers/subsystems/overlays.dm
index 5c8b3531c9..60b08c71d2 100644
--- a/code/controllers/subsystems/overlays.dm
+++ b/code/controllers/subsystems/overlays.dm
@@ -168,7 +168,7 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
* Adds specific overlay(s) to the atom.
* It is designed so any of the types allowed to be added to /atom/overlays can be added here too. More details below.
*
- * @param overlays The overlay(s) to add. These may be
+ * @param overlays The overlay(s) to add. These may be
* - A string: In which case it is treated as an icon_state of the atom's icon.
* - An icon: It is treated as an icon.
* - An atom: Its own overlays are compiled and then it's appearance is added. (Meaning its current apperance is frozen).
@@ -205,7 +205,7 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
/**
* Copy the overlays from another atom, either replacing all of ours or appending to our existing overlays.
* Note: This copies only the normal overlays, not the "priority" overlays.
- *
+ *
* @param other The atom to copy overlays from.
* @param cut_old If true, all of our overlays will be *replaced* by the other's. If other is null, that means cutting all ours.
*/
diff --git a/code/controllers/subsystems/processing/fastprocess.dm b/code/controllers/subsystems/processing/fastprocess.dm
new file mode 100644
index 0000000000..9622e02146
--- /dev/null
+++ b/code/controllers/subsystems/processing/fastprocess.dm
@@ -0,0 +1,6 @@
+//Fires five times every second.
+
+PROCESSING_SUBSYSTEM_DEF(fastprocess)
+ name = "Fast Processing"
+ wait = 2
+ stat_tag = "FP"
diff --git a/code/controllers/subsystems/processing/obj.dm b/code/controllers/subsystems/processing/obj.dm
new file mode 100644
index 0000000000..26021fb267
--- /dev/null
+++ b/code/controllers/subsystems/processing/obj.dm
@@ -0,0 +1,5 @@
+PROCESSING_SUBSYSTEM_DEF(obj)
+ name = "Objects"
+ priority = FIRE_PRIORITY_OBJ
+ flags = SS_NO_INIT
+ wait = 20
diff --git a/code/controllers/subsystems/processing/projectiles.dm b/code/controllers/subsystems/processing/projectiles.dm
index 6c0147b918..87c9f097de 100644
--- a/code/controllers/subsystems/processing/projectiles.dm
+++ b/code/controllers/subsystems/processing/projectiles.dm
@@ -2,6 +2,7 @@ PROCESSING_SUBSYSTEM_DEF(projectiles)
name = "Projectiles"
wait = 1
stat_tag = "PP"
+ priority = FIRE_PRIORITY_PROJECTILES
flags = SS_NO_INIT|SS_TICKER
var/global_max_tick_moves = 10
var/global_pixel_speed = 2
diff --git a/code/controllers/subsystems/sun.dm b/code/controllers/subsystems/sun.dm
new file mode 100644
index 0000000000..551130a20b
--- /dev/null
+++ b/code/controllers/subsystems/sun.dm
@@ -0,0 +1,7 @@
+SUBSYSTEM_DEF(sun)
+ name = "Sun"
+ wait = 600
+ var/static/datum/sun/sun = new
+
+/datum/controller/subsystem/sun/fire()
+ sun.calc_position()
diff --git a/code/controllers/voting.dm b/code/controllers/subsystems/vote.dm
similarity index 81%
rename from code/controllers/voting.dm
rename to code/controllers/subsystems/vote.dm
index 9a4d9bad46..480e0da813 100644
--- a/code/controllers/voting.dm
+++ b/code/controllers/subsystems/vote.dm
@@ -1,64 +1,48 @@
-var/datum/controller/vote/vote = new()
+SUBSYSTEM_DEF(vote)
+ name = "Vote"
+ wait = 10
+ priority = FIRE_PRIORITY_VOTE
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+ flags = SS_KEEP_TIMING | SS_NO_INIT
+ var/list/round_voters = list()
-var/global/list/round_voters = list() // Keeps track of the individuals voting for a given round, for use in forcedrafting.
-
-#define VOTE_RESTART "restart"
-#define VOTE_GAMEMODE "gamemode"
-#define VOTE_CREW_TRANSFER "crew_transfer"
-#define VOTE_ADD_ANTAGONIST "add_antagonist"
-#define VOTE_CUSTOM "custom"
-
-/datum/controller/vote
- var/initiator = null // Key of the one who started the vote or "the server"
- var/started_time = null
- var/time_remaining = 0
- var/mode = null
- var/question = null
+ //Current vote
+ var/initiator
+ var/started_time
+ var/time_remaining
+ var/duration
+ var/mode
+ var/question
var/list/choices = list()
var/list/gamemode_names = list()
var/list/voted = list()
var/list/current_votes = list()
var/list/additional_text = list()
-/datum/controller/vote/New()
- if(vote != src)
- if(istype(vote))
- Recover()
- qdel(vote)
- vote = src
-
-/datum/controller/vote/Destroy()
- ..()
- // Tell qdel() to Del() this object.
- return QDEL_HINT_HARDDEL_NOW
-
-/datum/controller/vote/process() //called by master_controller
+/datum/controller/subsystem/vote/fire(resumed)
if(mode)
- // No more change mode votes after the game has started.
+ time_remaining = round((started_time + duration - world.time)/10)
if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
- world << "Voting aborted due to game start."
- src.reset()
+ to_chat(world, "Gamemode vote aborted: Game has already started.")
+ reset()
return
-
- // Calculate how much time is remaining by comparing current time, to time of vote start,
- // plus vote duration
- time_remaining = round((started_time + config.vote_period - world.time)/10)
-
- if(time_remaining < 0)
+ if(time_remaining <= 0)
result()
reset()
-/datum/controller/vote/proc/autotransfer()
+/datum/controller/subsystem/vote/proc/autotransfer()
initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
- log_debug("The server has called a crew transfer vote")
+ log_debug("The server has called a crew transfer vote.")
-/datum/controller/vote/proc/autogamemode()
+/datum/controller/subsystem/vote/proc/autogamemode()
initiate_vote(VOTE_GAMEMODE, "the server", 1)
- log_debug("The server has called a gamemode vote")
+ log_debug("The server has called a gamemode vote.")
-/datum/controller/vote/proc/reset()
+/datum/controller/subsystem/vote/proc/reset()
initiator = null
- time_remaining = 0
+ started_time = null
+ duration = null
+ time_remaining = null
mode = null
question = null
choices.Cut()
@@ -66,7 +50,7 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
current_votes.Cut()
additional_text.Cut()
-/datum/controller/vote/proc/get_result() // Get the highest number of votes
+/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes
var/greatest_votes = 0
var/total_votes = 0
@@ -111,7 +95,7 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
if(choices[option] == greatest_votes)
. += option
-/datum/controller/vote/proc/announce_result()
+/datum/controller/subsystem/vote/proc/announce_result()
var/list/winners = get_result()
var/text
if(winners.len > 0)
@@ -135,9 +119,9 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
if(mode == VOTE_ADD_ANTAGONIST)
antag_add_failed = 1
log_vote(text)
- world << "[text]"
+ to_chat(world, "[text]")
-/datum/controller/vote/proc/result()
+/datum/controller/subsystem/vote/proc/result()
. = announce_result()
var/restart = 0
if(.)
@@ -175,7 +159,7 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
log_game("Rebooting due to restart vote")
world.Reboot()
-/datum/controller/vote/proc/submit_vote(var/ckey, var/newVote)
+/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote)
if(mode)
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
return
@@ -187,7 +171,7 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
else
current_votes[ckey] = null
-/datum/controller/vote/proc/initiate_vote(var/vote_type, var/initiator_key, var/automatic = 0)
+/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
if(!mode)
if(started_time != null && !(check_rights(R_ADMIN) || automatic))
var/next_allowed_time = (started_time + config.vote_delay)
@@ -243,6 +227,7 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
mode = vote_type
initiator = initiator_key
started_time = world.time
+ duration = time
var/text = "[capitalize(mode)] vote started by [initiator]."
if(mode == VOTE_CUSTOM)
text += "\n[question]"
@@ -261,13 +246,13 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
return 1
return 0
-/datum/controller/vote/proc/interface(var/client/C)
+/datum/controller/subsystem/vote/proc/interface(var/client/C)
if(!istype(C))
return
- var/admin = 0
+ var/admin = FALSE
if(C.holder)
if(C.holder.rights & R_ADMIN)
- admin = 1
+ admin = TRUE
. = "
Voting Panel"
if(mode)
@@ -337,7 +322,7 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
. += "Close"
-/datum/controller/vote/Topic(href, href_list[])
+/datum/controller/subsystem/vote/Topic(href, href_list[])
if(!usr || !usr.client)
return
switch(href_list["vote"])
@@ -384,5 +369,5 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
set category = "OOC"
set name = "Vote"
- if(vote)
- src << browse(vote.interface(src), "window=vote;size=500x[300 + vote.choices.len * 25]")
+ if(SSvote)
+ src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]")
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index 113072af4f..7d22f23c18 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -85,9 +85,6 @@
if("Jobs")
debug_variables(job_master)
feedback_add_details("admin_verb","DJobs")
- if("Sun")
- debug_variables(sun)
- feedback_add_details("admin_verb","DSun")
if("Radio")
debug_variables(radio_controller)
feedback_add_details("admin_verb","DRadio")
@@ -130,8 +127,4 @@
if("Chemistry")
debug_variables(chemistryProcess)
feedback_add_details("admin_verb", "DChem")
- if("Vote")
- debug_variables(vote)
- feedback_add_details("admin_verb", "DVote")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
- return
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 12c22430db..8cb040e5e1 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -59,6 +59,10 @@
return ..()
/datum/beam/proc/Draw()
+ if(QDELETED(target) || !QDELETED(origin))
+ qdel(src)
+ return
+
var/Angle = round(Get_Angle(origin,target))
var/matrix/rot_matrix = matrix()
@@ -102,17 +106,19 @@
//Position the effect so the beam is one continous line
var/a
if(abs(Pixel_x)>32)
- a = Pixel_x > 0 ? round(Pixel_x/32) : Ceiling(Pixel_x/32)
+ a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1)
X.x += a
Pixel_x %= 32
if(abs(Pixel_y)>32)
- a = Pixel_y > 0 ? round(Pixel_y/32) : Ceiling(Pixel_y/32)
+ a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1)
X.y += a
Pixel_y %= 32
X.pixel_x = Pixel_x
X.pixel_y = Pixel_y
+ X.on_drawn()
+
/obj/effect/ebeam
mouse_opacity = 0
anchored = TRUE
@@ -127,10 +133,53 @@
/obj/effect/ebeam/singularity_act()
return
+// Called when the beam datum finishes drawing and the ebeam object is placed correctly.
+/obj/effect/ebeam/proc/on_drawn()
+ return
+
/obj/effect/ebeam/deadly/Crossed(atom/A)
..()
A.ex_act(1)
+// 'Reactive' beam parts do something when touched or stood in.
+/obj/effect/ebeam/reactive
+
+/obj/effect/ebeam/reactive/Initialize()
+ START_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/effect/ebeam/reactive/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/effect/ebeam/reactive/on_drawn()
+ for(var/A in loc)
+ on_contact(A)
+
+/obj/effect/ebeam/reactive/Crossed(atom/A)
+ ..()
+ on_contact(A)
+
+/obj/effect/ebeam/reactive/process()
+ for(var/A in loc)
+ on_contact(A)
+
+// Override for things to do when someone touches the beam.
+/obj/effect/ebeam/reactive/proc/on_contact(atom/movable/AM)
+ return
+
+
+// Shocks things that touch it.
+/obj/effect/ebeam/reactive/electric
+ var/shock_amount = 25 // Be aware that high numbers may stun and result in dying due to not being able to get out of the beam.
+
+/obj/effect/ebeam/reactive/electric/on_contact(atom/movable/AM)
+ if(isliving(AM))
+ var/mob/living/L = AM
+ L.inflict_shock_damage(shock_amount)
+
+
+
/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
spawn(0)
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 765e1ee9e9..45d50ced1d 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -262,6 +262,9 @@
var/icon/charicon = cached_character_icon(H)
front = icon(charicon, dir = SOUTH)
side = icon(charicon, dir = WEST)
+ else // Sending null things through browse_rsc() makes a runtime and breaks the console trying to view the record.
+ front = icon('html/images/no_image32.png')
+ side = icon('html/images/no_image32.png')
if(!id)
id = text("[]", add_zero(num2hex(rand(1, 65536)), 4))
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index f4a90060a0..1b9df2498c 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -5,8 +5,8 @@
/datum
var/gc_destroyed //Time when this object was destroyed.
- var/weakref/weakref // Holder of weakref instance pointing to this datum
var/list/active_timers //for SStimer
+ var/weakref/weakref // Holder of weakref instance pointing to this datum
var/is_processing = FALSE // If this datum is in an MC processing list, this will be set to its name.
#ifdef TESTING
@@ -18,6 +18,16 @@
// This should be overridden to remove all references pointing to the object being destroyed.
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
/datum/proc/Destroy(force=FALSE)
+
+ //clear timers
+ var/list/timers = active_timers
+ active_timers = null
+ for(var/thing in timers)
+ var/datum/timedevent/timer = thing
+ if (timer.spent)
+ continue
+ qdel(timer)
+
weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
var/list/timers = active_timers
diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm
index d041558a0c..2f7942a9b2 100644
--- a/code/datums/ghost_query.dm
+++ b/code/datums/ghost_query.dm
@@ -106,6 +106,13 @@
check_bans = list("AI", "Cyborg", "Syndicate")
cutoff_number = 1
+/datum/ghost_query/borer
+ role_name = "Cortical Borer"
+ question = "A cortical borer has just been created on the facility. Would you like to play as them?"
+ be_special_flag = BE_ALIEN
+ check_bans = list("Syndicate", "Borer")
+ cutoff_number = 1
+
// Surface stuff.
/datum/ghost_query/lost_drone
role_name = "Lost Drone"
diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm
index 999f13e5ee..2ebab5582d 100644
--- a/code/datums/helper_datums/global_iterator.dm
+++ b/code/datums/helper_datums/global_iterator.dm
@@ -1,3 +1,7 @@
+/*
+ DO NOT USE THIS. THIS IS BEING DEPRECATED BY PROCESSING SUBSYSTEMS (controllers/subsystems/processing) AND TIMERS.
+*/
+
/*
README:
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 56c3710989..1492c8b65c 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -502,7 +502,7 @@
if(!mind.assigned_role) mind.assigned_role = "Assistant" //defualt
//slime
-/mob/living/simple_animal/slime/mind_initialize()
+/mob/living/simple_mob/slime/mind_initialize()
. = ..()
mind.assigned_role = "slime"
@@ -527,29 +527,30 @@
mind.special_role = ""
//Animals
-/mob/living/simple_animal/mind_initialize()
+/mob/living/simple_mob/mind_initialize()
. = ..()
- mind.assigned_role = "Animal"
+ mind.assigned_role = "Simple Mob"
-/mob/living/simple_animal/corgi/mind_initialize()
+/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize()
. = ..()
mind.assigned_role = "Corgi"
-/mob/living/simple_animal/shade/mind_initialize()
+/mob/living/simple_mob/construct/shade/mind_initialize()
. = ..()
mind.assigned_role = "Shade"
+ mind.special_role = "Cultist"
-/mob/living/simple_animal/construct/builder/mind_initialize()
+/mob/living/simple_mob/construct/artificer/mind_initialize()
. = ..()
mind.assigned_role = "Artificer"
mind.special_role = "Cultist"
-/mob/living/simple_animal/construct/wraith/mind_initialize()
+/mob/living/simple_mob/construct/wraith/mind_initialize()
. = ..()
mind.assigned_role = "Wraith"
mind.special_role = "Cultist"
-/mob/living/simple_animal/construct/armoured/mind_initialize()
+/mob/living/simple_mob/construct/juggernaut/mind_initialize()
. = ..()
mind.assigned_role = "Juggernaut"
mind.special_role = "Cultist"
diff --git a/code/datums/observation/task_triggered.dm b/code/datums/observation/task_triggered.dm
deleted file mode 100644
index 0c04ef2ead..0000000000
--- a/code/datums/observation/task_triggered.dm
+++ /dev/null
@@ -1,13 +0,0 @@
-//
-// Observer Pattern Implementation: Scheduled task triggered
-// Registration type: /datum/scheduled_task
-//
-// Raised when: When a scheduled task reaches its trigger time.
-//
-// Arguments that the called proc should expect:
-// /datum/scheduled_task/task: The task that reached its trigger time.
-var/decl/observ/task_triggered/task_triggered_event = new()
-
-/decl/observ/task_triggered
- name = "Task Triggered"
- expected_type = /datum/scheduled_task
diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm
index 8d2edbeeb9..bf38ab7a1c 100644
--- a/code/datums/progressbar.dm
+++ b/code/datums/progressbar.dm
@@ -15,6 +15,7 @@
bar = image('icons/effects/progessbar.dmi', target, "prog_bar_0")
bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
bar.pixel_y = 32
+ bar.plane = PLANE_PLAYER_HUD
src.user = user
if(user)
client = user.client
diff --git a/code/datums/repositories/radiation.dm b/code/datums/repositories/radiation.dm
index 46f4a9a5b7..88510e0525 100644
--- a/code/datums/repositories/radiation.dm
+++ b/code/datums/repositories/radiation.dm
@@ -122,7 +122,7 @@ var/global/repository/radiation/radiation_repository = new()
/turf/simulated/wall/calc_rad_resistance()
radiation_repository.resistance_cache[src] = (length(contents) + 1)
- cached_rad_resistance = (density ? material.weight : 0)
+ cached_rad_resistance = (density ? material.weight + material.radiation_resistance : 0)
/obj
var/rad_resistance = 0 // Allow overriding rad resistance
diff --git a/code/datums/sun.dm b/code/datums/sun.dm
index a78c89ac46..69b725f305 100644
--- a/code/datums/sun.dm
+++ b/code/datums/sun.dm
@@ -1,38 +1,21 @@
-#define SOLAR_UPDATE_TIME 600 //duration between two updates of the whole sun/solars positions
-
/datum/sun
var/angle
var/dx
var/dy
var/rate
- var/list/solars // for debugging purposes, references solars_list at the constructor
var/solar_next_update // last time the sun position was checked and adjusted
/datum/sun/New()
-
- solars = solars_list
rate = rand(50,200)/100 // 50% - 200% of standard rotation
if(prob(50)) // same chance to rotate clockwise than counter-clockwise
rate = -rate
- solar_next_update = world.time // init the timer
angle = rand (0,360) // the station position to the sun is randomised at round start
-/*/hook/startup/proc/createSun() // handled in scheduler
- sun = new /datum/sun()
- return 1*/
-
// calculate the sun's position given the time of day
// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute.
// a full rotation thus take a game hour in that case
/datum/sun/proc/calc_position()
-
- if(world.time < solar_next_update) //if less than 60 game secondes have passed, do nothing
- return;
-
angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate
-
- solar_next_update += SOLAR_UPDATE_TIME // since we updated the angle, set the proper time for the next loop
-
// now calculate and cache the (dx,dy) increments for line drawing
var/s = sin(angle)
@@ -51,8 +34,8 @@
dy = c / abs(s)
//now tell the solar control computers to update their status and linked devices
- for(var/obj/machinery/power/solar_control/SC in solars_list)
+ for(var/obj/machinery/power/solar_control/SC in GLOB.solars_list)
if(!SC.powernet)
- solars_list.Remove(SC)
+ GLOB.solars_list.Remove(SC)
continue
SC.update()
diff --git a/code/datums/supplypacks/robotics.dm b/code/datums/supplypacks/robotics.dm
index 67292de654..1c1eb7f2bb 100644
--- a/code/datums/supplypacks/robotics.dm
+++ b/code/datums/supplypacks/robotics.dm
@@ -176,4 +176,25 @@
cost = 30
containertype = /obj/structure/closet/crate/secure/science
containername = "Jumper kit crate"
- access = access_robotics
\ No newline at end of file
+ access = access_robotics
+
+/datum/supply_pack/robotics/bike
+ name = "Spacebike Crate"
+ contains = list()
+ cost = 350
+ containertype = /obj/structure/largecrate/vehicle/bike
+ containername = "Spacebike Crate"
+
+/datum/supply_pack/robotics/quadbike
+ name = "ATV Crate"
+ contains = list()
+ cost = 300
+ containertype = /obj/structure/largecrate/vehicle/quadbike
+ containername = "ATV Crate"
+
+/datum/supply_pack/robotics/quadtrailer
+ name = "ATV Trailer Crate"
+ contains = list()
+ cost = 250
+ containertype = /obj/structure/largecrate/vehicle/quadtrailer
+ containername = "ATV Trailer Crate"
diff --git a/code/datums/supplypacks/supply.dm b/code/datums/supplypacks/supply.dm
index 0a40cae6a3..8397dc8c87 100644
--- a/code/datums/supplypacks/supply.dm
+++ b/code/datums/supplypacks/supply.dm
@@ -124,14 +124,14 @@
/datum/supply_pack/supply/cargotrain
name = "Cargo Train Tug"
- contains = list(/obj/vehicle/train/cargo/engine)
+ contains = list(/obj/vehicle/train/engine)
cost = 35
containertype = /obj/structure/largecrate
containername = "Cargo Train Tug Crate"
/datum/supply_pack/supply/cargotrailer
name = "Cargo Train Trolley"
- contains = list(/obj/vehicle/train/cargo/trolley)
+ contains = list(/obj/vehicle/train/trolley)
cost = 15
containertype = /obj/structure/largecrate
containername = "Cargo Train Trolley Crate"
\ No newline at end of file
diff --git a/code/datums/underwear/socks.dm b/code/datums/underwear/socks.dm
index fc1c96ce53..e1fe7e07f2 100644
--- a/code/datums/underwear/socks.dm
+++ b/code/datums/underwear/socks.dm
@@ -56,4 +56,8 @@
/datum/category_item/underwear/socks/fishnet
name = "Fishnet"
- icon_state = "fishnet"
\ No newline at end of file
+ icon_state = "fishnet"
+
+/datum/category_item/underwear/socks/leggings
+ name = "Leggings"
+ icon_state = "leggings"
\ No newline at end of file
diff --git a/code/datums/underwear/top.dm b/code/datums/underwear/top.dm
index 72ac72c232..28a17c3ab7 100644
--- a/code/datums/underwear/top.dm
+++ b/code/datums/underwear/top.dm
@@ -62,4 +62,12 @@
/datum/category_item/underwear/top/striped_bra
name = "Striped Bra"
icon_state = "striped_bra"
- has_color = TRUE
\ No newline at end of file
+ has_color = TRUE
+
+/datum/category_item/underwear/top/binder
+ name = "Binder"
+ icon_state = "binder_s"
+
+/datum/category_item/underwear/top/straplessbinder
+ name = "Binder Strapless"
+ icon_state = "straplessbinder_s"
\ No newline at end of file
diff --git a/code/datums/underwear/undershirts.dm b/code/datums/underwear/undershirts.dm
index 1bbbae7fb5..1f91d1222c 100644
--- a/code/datums/underwear/undershirts.dm
+++ b/code/datums/underwear/undershirts.dm
@@ -165,4 +165,16 @@
/datum/category_item/underwear/undershirt/tiedye
name = "Tiedye Shirt"
- icon_state = "tiedye"
\ No newline at end of file
+ icon_state = "tiedye"
+
+/datum/category_item/underwear/undershirt/longstripe_pink
+ name = "Longsleeve Striped Shirt, Pink"
+ icon_state = "longstripe_pink_s"
+
+/datum/category_item/underwear/undershirt/wingshirt
+ name = "Pink Wing Shirt"
+ icon_state = "wing_shirt_s"
+
+/datum/category_item/underwear/undershirt/pinkblack_tshirt
+ name = "Pink and Black T-Shirt"
+ icon_state = "pinkblack_tshirt"
\ No newline at end of file
diff --git a/code/defines/obj.dm b/code/defines/obj.dm
index f2cec96aab..3b36f5099e 100644
--- a/code/defines/obj.dm
+++ b/code/defines/obj.dm
@@ -144,7 +144,7 @@ var/global/list/PDA_Manifest = list()
for(var/mob/living/silicon/robot/robot in mob_list)
// No combat/syndicate cyborgs, no drones.
- if(!robot.scrambledcodes && !(robot.module && robot.module.hide_on_manifest))
+ if(robot.scrambledcodes || (robot.module && robot.module.hide_on_manifest))
continue
bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active")
diff --git a/code/game/antagonist/alien/borer.dm b/code/game/antagonist/alien/borer.dm
index e852bc8418..34b79271f7 100644
--- a/code/game/antagonist/alien/borer.dm
+++ b/code/game/antagonist/alien/borer.dm
@@ -5,7 +5,7 @@ var/datum/antagonist/borer/borers
role_type = BE_ALIEN
role_text = "Cortical Borer"
role_text_plural = "Cortical Borers"
- mob_path = /mob/living/simple_animal/borer
+ mob_path = /mob/living/simple_mob/animal/borer
bantype = "Borer"
welcome_text = "Use your Infest power to crawl into the ear of a host and fuse with their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your fellow borers with :x."
antag_indicator = "brainworm"
@@ -40,7 +40,7 @@ var/datum/antagonist/borer/borers
player.objectives += new /datum/objective/escape()
/datum/antagonist/borer/place_mob(var/mob/living/mob)
- var/mob/living/simple_animal/borer/borer = mob
+ var/mob/living/simple_mob/animal/borer/borer = mob
if(istype(borer))
var/mob/living/carbon/human/host
for(var/mob/living/carbon/human/H in mob_list)
diff --git a/code/game/antagonist/station/cultist.dm b/code/game/antagonist/station/cultist.dm
index e40085f90a..e5c7d8f652 100644
--- a/code/game/antagonist/station/cultist.dm
+++ b/code/game/antagonist/station/cultist.dm
@@ -111,12 +111,12 @@ var/datum/antagonist/cultist/cult
. = ..()
if(.)
player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back."
- if(player.current && !istype(player.current, /mob/living/simple_animal/construct))
+ if(player.current && !istype(player.current, /mob/living/simple_mob/construct))
player.current.add_language(LANGUAGE_CULT)
/datum/antagonist/cultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
. = ..()
- if(. && player.current && !istype(player.current, /mob/living/simple_animal/construct))
+ if(. && player.current && !istype(player.current, /mob/living/simple_mob/construct))
player.current.remove_language(LANGUAGE_CULT)
/datum/antagonist/cultist/can_become_antag(var/datum/mind/player)
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 31f6da1603..ecddd5cb4b 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -59,7 +59,7 @@
..()
-/area/initialize()
+/area/Initialize()
. = ..()
return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms.
@@ -68,10 +68,8 @@
power_light = 0
power_equip = 0
power_environ = 0
- return INITIALIZE_HINT_LATELOAD
-
-/area/LateInitialize()
power_change() // all machines set to current power level, also updates lighting icon
+ return INITIALIZE_HINT_LATELOAD
/area/proc/get_contents()
return contents
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 80552b08c8..83413fd724 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -64,7 +64,7 @@
// Must not sleep!
// Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE
// Must return an Initialize hint. Defined in code/__defines/subsystems.dm
-/atom/proc/initialize(mapload, ...)
+/atom/proc/Initialize(mapload, ...)
if(QDELETED(src))
crash_with("GC: -- [type] had initialize() called after qdel() --")
if(initialized)
@@ -210,6 +210,16 @@
/atom/proc/fire_act()
return
+
+// Returns an assoc list of RCD information.
+// Example would be: list(RCD_VALUE_MODE = RCD_DECONSTRUCT, RCD_VALUE_DELAY = 50, RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4)
+// This occurs before rcd_act() is called, and it won't be called if it returns FALSE.
+/atom/proc/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode)
+ return FALSE
+
+/atom/proc/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode)
+ return
+
/atom/proc/melt()
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 09f54640c5..b32a52add9 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -17,6 +17,7 @@
var/mob/pulledby = null
var/item_state = null // Used to specify the item state for the on-mob overlays.
var/icon_scale = 1 // Used to scale icons up or down in update_transform().
+ var/icon_rotation = 0 // Used to rotate icons in update_transform()
var/old_x = 0
var/old_y = 0
var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P)
@@ -390,7 +391,7 @@
// Special handling of windows, which are dense but block only from some directions
if(istype(A, /obj/structure/window))
var/obj/structure/window/W = A
- if (!W.is_full_window() && !(turn(src.last_move, 180) & A.dir))
+ if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir))
continue
// Same thing for (closed) windoors, which have the same problem
else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir))
@@ -549,9 +550,14 @@
/atom/movable/proc/update_transform()
var/matrix/M = matrix()
M.Scale(icon_scale)
+ M.Turn(icon_rotation)
src.transform = M
// Use this to set the object's scale.
/atom/movable/proc/adjust_scale(new_scale)
icon_scale = new_scale
update_transform()
+
+/atom/movable/proc/adjust_rotation(new_rotation)
+ icon_rotation = new_rotation
+ update_transform()
\ No newline at end of file
diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm
index 35526be0d6..ed27a49ba0 100644
--- a/code/game/gamemodes/changeling/powers/armblade.dm
+++ b/code/game/gamemodes/changeling/powers/armblade.dm
@@ -64,7 +64,7 @@
/obj/item/weapon/melee/changeling/New(location)
..()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
if(ismob(loc))
visible_message("A grotesque weapon forms around [loc.name]\'s arm!",
"Our arm twists and mutates, transforming it into a deadly weapon.",
@@ -81,7 +81,7 @@
qdel(src)
/obj/item/weapon/melee/changeling/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
creator = null
..()
diff --git a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
index d79af62b4c..b90b402e78 100644
--- a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
+++ b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
@@ -268,7 +268,7 @@ var/global/list/changeling_fabricated_clothing = list(
..()
registered_user = user
-/obj/item/weapon/card/id/syndicate/changeling/initialize()
+/obj/item/weapon/card/id/syndicate/changeling/Initialize()
. = ..()
access = null
diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm
index 2c6bc87825..937284629b 100644
--- a/code/game/gamemodes/cult/construct_spells.dm
+++ b/code/game/gamemodes/cult/construct_spells.dm
@@ -476,14 +476,14 @@ proc/findNullRod(var/atom/target)
/obj/item/weapon/spell/construct/run_checks()
if(owner)
- if((iscultist(owner) || istype(owner, /mob/living/simple_animal/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed?
+ if((iscultist(owner) || istype(owner, /mob/living/simple_mob/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed?
last_castcheck = world.time
return 1
return 0
/obj/item/weapon/spell/construct/pay_energy(var/amount)
if(owner)
- if(istype(owner, /mob/living/simple_animal/construct))
+ if(istype(owner, /mob/living/simple_mob/construct))
return 1
if(iscultist(owner) && pay_blood(amount))
return 1
@@ -608,7 +608,7 @@ proc/findNullRod(var/atom/target)
name = "sphere of agony"
desc = "Call forth a portal to a dimension of naught but pain at your target."
- spawner_type = /obj/effect/temporary_effect/pulsar/agonizing_sphere
+ spawner_type = /obj/effect/temporary_effect/pulse/agonizing_sphere
/obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_ranged_cast(atom/hit_atom, mob/user)
if(within_range(hit_atom) && pay_energy(10))
@@ -620,7 +620,7 @@ proc/findNullRod(var/atom/target)
var/mob/living/L = hit_atom
L.add_modifier(/datum/modifier/agonize, 10 SECONDS)
-/obj/effect/temporary_effect/pulsar/agonizing_sphere
+/obj/effect/temporary_effect/pulse/agonizing_sphere
name = "agonizing sphere"
desc = "A portal to some hellish place. Its screams wrack your body with pain.."
icon_state = "red_static_sphere"
@@ -629,19 +629,15 @@ proc/findNullRod(var/atom/target)
light_power = 5
light_color = "#FF0000"
pulses_remaining = 10
+ pulse_delay = 1 SECOND
-/obj/effect/temporary_effect/pulsar/agonizing_sphere/pulse_loop()
- while(pulses_remaining)
- sleep(1 SECONDS)
- spawn()
- for(var/mob/living/L in view(4,src))
- if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct))
- L.add_modifier(/datum/modifier/agonize, 2 SECONDS)
- if(L.isSynthetic())
- to_chat(L, "Your chassis warps as the [src] pulses!")
- L.adjustFireLoss(4)
- pulses_remaining--
- qdel(src)
+/obj/effect/temporary_effect/pulse/agonizing_sphere/on_pulse()
+ for(var/mob/living/L in view(4,src))
+ if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct))
+ L.add_modifier(/datum/modifier/agonize, 2 SECONDS)
+ if(L.isSynthetic())
+ to_chat(L, "Your chassis warps as the [src] pulses!")
+ L.adjustFireLoss(4)
//Artificer Heal
@@ -660,7 +656,7 @@ proc/findNullRod(var/atom/target)
L.add_modifier(/datum/modifier/mend_occult, 150)
qdel(src)
-//Juggernaut + Behemoth Slam
+//Juggernaut Slam
/obj/item/weapon/spell/construct/slam
name = "slam"
desc = "Empower your FIST, to send an opponent flying."
@@ -673,8 +669,8 @@ proc/findNullRod(var/atom/target)
/obj/item/weapon/spell/construct/slam/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
var/attack_message = "slams"
- if(istype(user, /mob/living/simple_animal))
- var/mob/living/simple_animal/S = user
+ if(istype(user, /mob/living/simple_mob))
+ var/mob/living/simple_mob/S = user
attack_message = pick(S.attacktext)
if(isliving(hit_atom))
var/mob/living/L = hit_atom
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 082404b271..7118aafe80 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -14,7 +14,7 @@
return
/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone)
- if(iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
+ if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct))
return ..()
var/zone = (user.hand ? "l_arm":"r_arm")
@@ -25,7 +25,7 @@
//random amount of damage between half of the blade's force and the full force of the blade.
user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
user.Weaken(5)
- else if(!istype(user, /mob/living/simple_animal/construct))
+ else if(!istype(user, /mob/living/simple_mob/construct))
to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!")
else
to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.")
@@ -39,10 +39,10 @@
return 1
/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
- if(!iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
+ if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct))
to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.")
user.make_dizzy(120)
- if(istype(user, /mob/living/simple_animal/construct))
+ if(istype(user, /mob/living/simple_mob/construct))
to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.")
/obj/item/clothing/head/culthood
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 0f2c4d83b6..c87427bc44 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -108,18 +108,18 @@
light_range=5
light_color="#ff0000"
spawnable=list(
- /mob/living/simple_animal/hostile/scarybat,
- /mob/living/simple_animal/hostile/creature,
- /mob/living/simple_animal/hostile/faithless
+ /mob/living/simple_mob/animal/space/bats,
+ /mob/living/simple_mob/creature,
+ /mob/living/simple_mob/faithless
)
/obj/effect/gateway/active/cult
light_range=5
light_color="#ff0000"
spawnable=list(
- /mob/living/simple_animal/hostile/scarybat/cult,
- /mob/living/simple_animal/hostile/creature/cult,
- /mob/living/simple_animal/hostile/faithless/cult
+ /mob/living/simple_mob/animal/space/bats/cult,
+ /mob/living/simple_mob/creature/cult,
+ /mob/living/simple_mob/faithless/cult
)
/obj/effect/gateway/active/cult/cultify()
diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm
index 14bb380a19..d78956c40a 100644
--- a/code/game/gamemodes/cult/cultify/mob.dm
+++ b/code/game/gamemodes/cult/cultify/mob.dm
@@ -16,7 +16,7 @@
/mob/living/cultify()
if(iscultist(src) && client)
- var/mob/living/simple_animal/construct/harvester/C = new(get_turf(src))
+ var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src))
mind.transfer_to(C)
C << "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell."
dust()
diff --git a/code/modules/mob/living/simple_animal/constructs/soulstone.dm b/code/game/gamemodes/cult/soulstone.dm
similarity index 88%
rename from code/modules/mob/living/simple_animal/constructs/soulstone.dm
rename to code/game/gamemodes/cult/soulstone.dm
index 6f13789dfc..61db88917a 100644
--- a/code/modules/mob/living/simple_animal/constructs/soulstone.dm
+++ b/code/game/gamemodes/cult/soulstone.dm
@@ -1,242 +1,246 @@
-/obj/item/device/soulstone/cultify()
- return
-
-/obj/item/device/soulstone
- name = "Soul Stone Shard"
- icon = 'icons/obj/wizard.dmi'
- icon_state = "soulstone"
- item_state = "electronic"
- desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power."
- w_class = ITEMSIZE_SMALL
- slot_flags = SLOT_BELT
- origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
- var/imprinted = "empty"
- var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester")
-
-//////////////////////////////Capturing////////////////////////////////////////////////////////
-
-/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob)
- if(!istype(M, /mob/living/carbon/human))//If target is not a human.
- return ..()
- if(istype(M, /mob/living/carbon/human/dummy))
- return..()
- if(jobban_isbanned(M, "cultist"))
- user << "This person's soul is too corrupt and cannot be captured!"
- return..()
-
- if(M.has_brain_worms()) //Borer stuff - RR
- user << "This being is corrupted by an alien intelligence and cannot be soul trapped."
- return..()
-
- add_attack_logs(user,M,"Soulstone'd with [src.name]")
- transfer_soul("VICTIM", M, user)
- return
-
-
-///////////////////Options for using captured souls///////////////////////////////////////
-
-/obj/item/device/soulstone/attack_self(mob/user)
- if (!in_range(src, user))
- return
- user.set_machine(src)
- var/dat = "Soul Stone
"
- for(var/mob/living/simple_animal/shade/A in src)
- dat += "Captured Soul: [A.name]
"
- dat += {"Summon Shade"}
- dat += "
"
- dat += {" Close"}
- user << browse(dat, "window=aicard")
- onclose(user, "aicard")
- return
-
-
-
-
-/obj/item/device/soulstone/Topic(href, href_list)
- var/mob/U = usr
- if (!in_range(src, U)||U.machine!=src)
- U << browse(null, "window=aicard")
- U.unset_machine()
- return
-
- add_fingerprint(U)
- U.set_machine(src)
-
- switch(href_list["choice"])//Now we switch based on choice.
- if ("Close")
- U << browse(null, "window=aicard")
- U.unset_machine()
- return
-
- if ("Summon")
- for(var/mob/living/simple_animal/shade/A in src)
- A.status_flags &= ~GODMODE
- A.canmove = 1
- A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs."
- A.forceMove(U.loc)
- A.cancel_camera()
- src.icon_state = "soulstone"
- attack_self(U)
-
-///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
-/obj/structure/constructshell
- name = "empty shell"
- icon = 'icons/obj/wizard.dmi'
- icon_state = "construct"
- desc = "A wicked machine used by those skilled in magical arts. It is inactive."
-
-/obj/structure/constructshell/cultify()
- return
-
-/obj/structure/constructshell/cult
- icon_state = "construct-cult"
- desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient."
-
-/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob)
- if(istype(O, /obj/item/device/soulstone))
- var/obj/item/device/soulstone/S = O;
- S.transfer_soul("CONSTRUCT",src,user)
-
-
-////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
-/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U)
- if(!istype(T))
- return;
- if(src.imprinted != "empty")
- U << "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!"
- return
- if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD)
- U << "Capture failed!: Kill or maim the victim first!"
- return
- if(T.client == null)
- U << "Capture failed!: The soul has already fled it's mortal frame."
- return
- if(src.contents.len)
- U << "Capture failed!: The soul stone is full! Use or free an existing soul to make room."
- return
-
- for(var/obj/item/W in T)
- T.drop_from_inventory(W)
-
- new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
- T.invisibility = 101
-
- var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
- animation.icon_state = "blank"
- animation.icon = 'icons/mob/mob.dmi'
- animation.master = T
- flick("dust-h", animation)
- qdel(animation)
-
- var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade( T.loc )
- S.forceMove(src) //put shade in stone
- S.status_flags |= GODMODE //So they won't die inside the stone somehow
- S.canmove = 0//Can't move out of the soul stone
- S.name = "Shade of [T.real_name]"
- S.real_name = "Shade of [T.real_name]"
- S.icon = T.icon
- S.icon_state = T.icon_state
- S.overlays = T.overlays
- S.color = rgb(254,0,0)
- S.alpha = 127
- if (T.client)
- T.client.mob = S
- S.cancel_camera()
-
-
- src.icon_state = "soulstone2"
- src.name = "Soul Stone: [S.real_name]"
- to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.")
- to_chat(U, "Capture successful! : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.")
- to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.")
- src.imprinted = "[S.name]"
- qdel(T)
-
-/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_animal/shade/T,var/mob/U)
- if(!istype(T))
- return;
- if (T.stat == DEAD)
- to_chat(U, "Capture failed!: The shade has already been banished!")
- return
- if(src.contents.len)
- to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
- return
- if(T.name != src.imprinted)
- to_chat(U, "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!")
- return
-
- T.forceMove(src) //put shade in stone
- T.status_flags |= GODMODE
- T.canmove = 0
- T.health = T.getMaxHealth()
- src.icon_state = "soulstone2"
-
- to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
- to_chat(U, "Capture successful! : [T.name]'s has been recaptured and stored within the soul stone.")
-
-/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U)
- var/mob/living/simple_animal/shade/A = locate() in src
- if(!A)
- to_chat(U,"Capture failed!: The soul stone is empty! Go kill someone!")
- return;
- var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs
- switch(construct_class)
- if("Juggernaut")
- var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(T.loc))
- Z.key = A.key
- if(iscultist(U))
- cult.add_antagonist(Z.mind)
- qdel(T)
- to_chat(Z,"You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.")
- to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- Z.cancel_camera()
- qdel(src)
- if("Wraith")
- var/mob/living/simple_animal/construct/wraith/Z = new /mob/living/simple_animal/construct/wraith (get_turf(T.loc))
- Z.key = A.key
- if(iscultist(U))
- cult.add_antagonist(Z.mind)
- qdel(T)
- to_chat(Z,"You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.")
- to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- Z.cancel_camera()
- qdel(src)
- if("Artificer")
- var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/builder (get_turf(T.loc))
- Z.key = A.key
- if(iscultist(U))
- cult.add_antagonist(Z.mind)
- qdel(T)
- to_chat(Z,"You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs")
- to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- Z.cancel_camera()
- qdel(src)
- if("Harvester")
- var/mob/living/simple_animal/construct/harvester/Z = new /mob/living/simple_animal/construct/harvester (get_turf(T.loc))
- Z.key = A.key
- if(iscultist(U))
- cult.add_antagonist(Z.mind)
- qdel(T)
- to_chat(Z,"You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.")
- to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- Z.cancel_camera()
- qdel(src)
- if("Behemoth")
- var/mob/living/simple_animal/construct/behemoth/Z = new /mob/living/simple_animal/construct/behemoth (get_turf(T.loc))
- Z.key = A.key
- if(iscultist(U))
- cult.add_antagonist(Z.mind)
- qdel(T)
- to_chat(Z,"You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.")
- to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- Z.cancel_camera()
- qdel(src)
-
-/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
- switch(choice)
- if("VICTIM")
- transfer_human(target,U)
- if("SHADE")
- transfer_shade(target,U)
- if("CONSTRUCT")
- transfer_construct(target,U)
+/////////////////////////
+// Soulstone
+/////////////////////////
+
+/obj/item/device/soulstone
+ name = "Soul Stone Shard"
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "soulstone"
+ item_state = "electronic"
+ desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power."
+ w_class = ITEMSIZE_SMALL
+ slot_flags = SLOT_BELT
+ origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
+ var/imprinted = "empty"
+ var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester")
+
+/obj/item/device/soulstone/cultify()
+ return
+
+//////////////////////////////Capturing////////////////////////////////////////////////////////
+
+/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob)
+ if(!istype(M, /mob/living/carbon/human))//If target is not a human.
+ return ..()
+ if(istype(M, /mob/living/carbon/human/dummy))
+ return..()
+ if(jobban_isbanned(M, "cultist"))
+ user << "This person's soul is too corrupt and cannot be captured!"
+ return..()
+
+ if(M.has_brain_worms()) //Borer stuff - RR
+ user << "This being is corrupted by an alien intelligence and cannot be soul trapped."
+ return..()
+
+ add_attack_logs(user,M,"Soulstone'd with [src.name]")
+ transfer_soul("VICTIM", M, user)
+ return
+
+
+///////////////////Options for using captured souls///////////////////////////////////////
+
+/obj/item/device/soulstone/attack_self(mob/user)
+ if (!in_range(src, user))
+ return
+ user.set_machine(src)
+ var/dat = "Soul Stone
"
+ for(var/mob/living/simple_mob/construct/shade/A in src)
+ dat += "Captured Soul: [A.name]
"
+ dat += {"Summon Shade"}
+ dat += "
"
+ dat += {" Close"}
+ user << browse(dat, "window=aicard")
+ onclose(user, "aicard")
+ return
+
+
+
+
+/obj/item/device/soulstone/Topic(href, href_list)
+ var/mob/U = usr
+ if (!in_range(src, U)||U.machine!=src)
+ U << browse(null, "window=aicard")
+ U.unset_machine()
+ return
+
+ add_fingerprint(U)
+ U.set_machine(src)
+
+ switch(href_list["choice"])//Now we switch based on choice.
+ if ("Close")
+ U << browse(null, "window=aicard")
+ U.unset_machine()
+ return
+
+ if ("Summon")
+ for(var/mob/living/simple_mob/construct/shade/A in src)
+ A.status_flags &= ~GODMODE
+ A.canmove = 1
+ A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs."
+ A.forceMove(U.loc)
+ A.cancel_camera()
+ src.icon_state = "soulstone"
+ attack_self(U)
+
+///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
+/obj/structure/constructshell
+ name = "empty shell"
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "construct"
+ desc = "A wicked machine used by those skilled in magical arts. It is inactive."
+
+/obj/structure/constructshell/cultify()
+ return
+
+/obj/structure/constructshell/cult
+ icon_state = "construct-cult"
+ desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient."
+
+/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob)
+ if(istype(O, /obj/item/device/soulstone))
+ var/obj/item/device/soulstone/S = O;
+ S.transfer_soul("CONSTRUCT",src,user)
+
+
+////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
+/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U)
+ if(!istype(T))
+ return;
+ if(src.imprinted != "empty")
+ U << "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!"
+ return
+ if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD)
+ U << "Capture failed!: Kill or maim the victim first!"
+ return
+ if(T.client == null)
+ U << "Capture failed!: The soul has already fled it's mortal frame."
+ return
+ if(src.contents.len)
+ U << "Capture failed!: The soul stone is full! Use or free an existing soul to make room."
+ return
+
+ for(var/obj/item/W in T)
+ T.drop_from_inventory(W)
+
+ new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
+ T.invisibility = 101
+
+ var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
+ animation.icon_state = "blank"
+ animation.icon = 'icons/mob/mob.dmi'
+ animation.master = T
+ flick("dust-h", animation)
+ qdel(animation)
+
+ var/mob/living/simple_mob/construct/shade/S = new /mob/living/simple_mob/construct/shade( T.loc )
+ S.forceMove(src) //put shade in stone
+ S.status_flags |= GODMODE //So they won't die inside the stone somehow
+ S.canmove = 0//Can't move out of the soul stone
+ S.name = "Shade of [T.real_name]"
+ S.real_name = "Shade of [T.real_name]"
+ S.icon = T.icon
+ S.icon_state = T.icon_state
+ S.overlays = T.overlays
+ S.color = rgb(254,0,0)
+ S.alpha = 127
+ if (T.client)
+ T.client.mob = S
+ S.cancel_camera()
+
+
+ src.icon_state = "soulstone2"
+ src.name = "Soul Stone: [S.real_name]"
+ to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.")
+ to_chat(U, "Capture successful! : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.")
+ to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.")
+ src.imprinted = "[S.name]"
+ qdel(T)
+
+/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_mob/construct/shade/T,var/mob/U)
+ if(!istype(T))
+ return;
+ if (T.stat == DEAD)
+ to_chat(U, "Capture failed!: The shade has already been banished!")
+ return
+ if(src.contents.len)
+ to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
+ return
+ if(T.name != src.imprinted)
+ to_chat(U, "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!")
+ return
+
+ T.forceMove(src) //put shade in stone
+ T.status_flags |= GODMODE
+ T.canmove = 0
+ T.health = T.getMaxHealth()
+ src.icon_state = "soulstone2"
+
+ to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
+ to_chat(U, "Capture successful! : [T.name]'s has been recaptured and stored within the soul stone.")
+
+/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U)
+ var/mob/living/simple_mob/construct/shade/A = locate() in src
+ if(!A)
+ to_chat(U,"Capture failed!: The soul stone is empty! Go kill someone!")
+ return;
+ var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs
+ switch(construct_class)
+ if("Juggernaut")
+ var/mob/living/simple_mob/construct/juggernaut/Z = new /mob/living/simple_mob/construct/juggernaut (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+ if("Wraith")
+ var/mob/living/simple_mob/construct/wraith/Z = new /mob/living/simple_mob/construct/wraith (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+ if("Artificer")
+ var/mob/living/simple_mob/construct/artificer/Z = new /mob/living/simple_mob/construct/artificer (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+ if("Harvester")
+ var/mob/living/simple_mob/construct/harvester/Z = new /mob/living/simple_mob/construct/harvester (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+ if("Behemoth")
+ var/mob/living/simple_mob/construct/juggernaut/behemoth/Z = new /mob/living/simple_mob/construct/juggernaut/behemoth (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+
+/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
+ switch(choice)
+ if("VICTIM")
+ transfer_human(target,U)
+ if("SHADE")
+ transfer_shade(target,U)
+ if("CONSTRUCT")
+ transfer_construct(target,U)
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm
index 1051587d13..4559911b79 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm
@@ -29,7 +29,7 @@
// No more available directions? Shut down process().
if(avail_dirs.len==0)
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
return 1
// We're checking, reset the timer.
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm
index 2c47b8e162..996fc225b4 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm
@@ -16,7 +16,7 @@
/obj/singularity/narsie/large/exit/New()
..()
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
/obj/singularity/narsie/large/exit/update_icon()
overlays = 0
diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm
index fafae070ec..3e8d5551da 100644
--- a/code/game/gamemodes/events.dm
+++ b/code/game/gamemodes/events.dm
@@ -206,7 +206,7 @@ var/hadevent = 0
/proc/carp_migration() // -- Darem
for(var/obj/effect/landmark/C in landmarks_list)
if(C.name == "carpspawn")
- new /mob/living/simple_animal/hostile/carp(C.loc)
+ new /mob/living/simple_mob/animal/space/carp(C.loc)
//sleep(100)
spawn(rand(300, 600)) //Delayed announcements to keep the crew on their toes.
command_announcement.Announce("Unknown biological entities have been detected near \the [station_name()], please stand-by.", "Lifesign Alert", new_sound = 'sound/AI/commandreport.ogg')
diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm
index b2c4b68a5a..66e329ac1a 100644
--- a/code/game/gamemodes/events/holidays/Christmas.dm
+++ b/code/game/gamemodes/events/holidays/Christmas.dm
@@ -9,7 +9,7 @@
/proc/ChristmasEvent()
for(var/obj/structure/flora/tree/pine/xmas in world)
- var/mob/living/simple_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/tree(xmas.loc)
+ var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc)
evil_tree.icon_state = xmas.icon_state
evil_tree.icon_living = evil_tree.icon_state
evil_tree.icon_dead = evil_tree.icon_state
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index f3a01f9dcf..1e3dd96d01 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -51,21 +51,17 @@ var/global/datum/controller/gameticker/ticker
to_chat(world, "Welcome to the pregame lobby!")
to_chat(world, "Please set up your character and select ready. The round will start in [pregame_timeleft] seconds.")
while(current_state == GAME_STATE_PREGAME)
- for(var/i=0, i<10, i++)
- sleep(1)
- vote.process()
if(round_progressing)
pregame_timeleft--
if(pregame_timeleft == config.vote_autogamemode_timeleft)
- if(!vote.time_remaining)
- vote.autogamemode() //Quit calling this over and over and over and over.
- while(vote.time_remaining)
- for(var/i=0, i<10, i++)
- sleep(1)
- vote.process()
+ if(!SSvote.time_remaining)
+ SSvote.autogamemode() //Quit calling this over and over and over and over.
+ while(SSvote.time_remaining)
+ sleep(1)
if(pregame_timeleft <= 0)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
+ sleep(10)
while (!setup())
@@ -375,7 +371,7 @@ var/global/datum/controller/gameticker/ticker
if(!round_end_announced) // Spam Prevention. Now it should announce only once.
to_chat(world, "The round has ended!")
round_end_announced = 1
- vote.autotransfer()
+ SSvote.autotransfer()
return 1
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index c458b9c00a..0200c5588d 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -808,7 +808,7 @@ datum/objective/heist/salvage
/datum/objective/borer_survive/check_completion()
if(owner)
- var/mob/living/simple_animal/borer/B = owner
+ var/mob/living/simple_mob/animal/borer/B = owner
if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1
return 0
@@ -817,7 +817,7 @@ datum/objective/heist/salvage
/datum/objective/borer_reproduce/check_completion()
if(owner && owner.current)
- var/mob/living/simple_animal/borer/B = owner.current
+ var/mob/living/simple_mob/animal/borer/B = owner.current
if(istype(B) && B.has_reproduced) return 1
return 0
diff --git a/code/game/gamemodes/technomancer/assistance/golem.dm b/code/game/gamemodes/technomancer/assistance/golem.dm
deleted file mode 100644
index 6b8654e719..0000000000
--- a/code/game/gamemodes/technomancer/assistance/golem.dm
+++ /dev/null
@@ -1,258 +0,0 @@
-//An AI-controlled 'companion' for the Technomancer. It's tough, strong, and can also use spells.
-/mob/living/simple_animal/technomancer_golem
- name = "G.O.L.E.M."
- desc = "A rather unusual looking synthetic."
- icon = 'icons/mob/mob.dmi'
- icon_state = "technomancer_golem"
- health = 250
- maxHealth = 250
- stop_automated_movement = 1
- wander = 0
- response_help = "pets"
- response_disarm = "pushes away"
- response_harm = "punches"
- harm_intent_damage = 3
-
- heat_damage_per_tick = 0
- cold_damage_per_tick = 0
-
- min_oxy = 0
- max_oxy = 0
- min_tox = 0
- max_tox = 0
- min_co2 = 0
- max_co2 = 0
- min_n2 = 0
- max_n2 = 0
- unsuitable_atoms_damage = 0
- speed = 0
-
- melee_damage_lower = 30 // It has a built in esword.
- melee_damage_upper = 30
- attack_sound = 'sound/weapons/blade1.ogg'
- attacktext = list("slashed")
- friendly = "hugs"
- resistance = 0
- melee_miss_chance = 0
-
- var/obj/item/weapon/technomancer_core/golem/core = null
- var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells
- var/mob/living/master = null
-
- var/list/known_spells = list(
- "beam" = /obj/item/weapon/spell/projectile/beam,
- "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning,
- "force missile" = /obj/item/weapon/spell/projectile/force_missile,
- "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt,
- "lightning" = /obj/item/weapon/spell/projectile/lightning,
- "blink" = /obj/item/weapon/spell/blink,
- "dispel" = /obj/item/weapon/spell/dispel,
- "oxygenate" = /obj/item/weapon/spell/oxygenate,
- "mend life" = /obj/item/weapon/spell/modifier/mend_life,
- "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic,
- "mend organs" = /obj/item/weapon/spell/mend_organs,
- "purify" = /obj/item/weapon/spell/modifier/purify,
- "resurrect" = /obj/item/weapon/spell/resurrect,
- "passwall" = /obj/item/weapon/spell/passwall,
- "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles,
- "corona" = /obj/item/weapon/spell/modifier/corona,
- "haste" = /obj/item/weapon/spell/modifier/haste
- )
-
- // Holds the overlays, when idle or attacking.
- var/image/sword_image = null
- var/image/spell_image = null
- // These contain icon_states for each frame of an attack animation, which is swapped in and out manually, because BYOND.
- // They are assoc lists, to hold the frame duration and the frame icon_state in one list.
- var/list/spell_pre_attack_states = list(
- "golem_spell_attack_1" = 1,
- "golem_spell_attack_2" = 2,
- "golem_spell_attack_3" = 2
- )
- var/list/spell_post_attack_states = list(
- "golem_spell_attack_4" = 2,
- "golem_spell_attack_5" = 3,
- "golem_spell_attack_6" = 3
- )
- var/list/sword_pre_attack_states = list(
- "golem_sword_attack_1" = 1,
- "golem_sword_attack_2" = 5
- )
- var/list/sword_post_attack_states = list(
- "golem_sword_attack_3" = 1,
- "golem_sword_attack_4" = 3
- )
-
-/mob/living/simple_animal/technomancer_golem/New()
- ..()
- core = new(src)
- sword_image = image(icon, src, "golem_sword")
- spell_image = image(icon, src, "golem_spell")
- update_icon()
-
-/mob/living/simple_animal/technomancer_golem/Destroy()
- qdel(core)
- qdel(sword_image)
- qdel(spell_image)
- return ..()
-
-/mob/living/simple_animal/technomancer_golem/unref_spell()
- active_spell = null
- return ..()
-
-/mob/living/simple_animal/hostile/hivebot/death()
- ..()
- visible_message("\The [src] disintegrates!")
- new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc)
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- qdel(src)
-
-/mob/living/simple_animal/technomancer_golem/update_icon()
- overlays.Cut()
- overlays += sword_image
- overlays += spell_image
- update_modifier_visuals()
-
-// Unfortunately, BYOND does not let you flick() images or other overlays, so we need to do this in a terrible way.
-/atom/proc/manual_flick(var/list/frames, var/image/I, var/reset_to = null)
- // Swap in and out each frame manually.
- for(var/frame in frames)
- overlays -= I
- I.icon_state = frame
- overlays += I
- sleep(frames[frame])
- if(reset_to)
- // One more time to reset it to what it was before.
- overlays -= I
- I.icon_state = reset_to
- overlays += I
-
-/mob/living/simple_animal/technomancer_golem/proc/spellcast_pre_animation()
- setClickCooldown(5)
- manual_flick(spell_pre_attack_states, spell_image, reset_to = "golem_spell_attack_3")
-
-/mob/living/simple_animal/technomancer_golem/proc/spellcast_post_animation()
- setClickCooldown(8)
- manual_flick(spell_post_attack_states, spell_image, reset_to = "golem_spell")
-
-/mob/living/simple_animal/technomancer_golem/proc/sword_pre_animation()
- setClickCooldown(6)
- manual_flick(sword_pre_attack_states, sword_image)
-
-/mob/living/simple_animal/technomancer_golem/proc/sword_post_animation()
- setClickCooldown(3)
- manual_flick(sword_post_attack_states, sword_image, reset_to = "golem_sword")
-
-/mob/living/simple_animal/technomancer_golem/DoPunch(var/atom/A)
- sword_pre_animation()
- . = ..() // This does the actual attack and will check adjacency again.
- sword_post_animation()
-
-/mob/living/simple_animal/technomancer_golem/isSynthetic()
- return TRUE // So Mend Synthetic will work on them.
-
-/mob/living/simple_animal/technomancer_golem/speech_bubble_appearance()
- return "synthetic_evil"
-
-/mob/living/simple_animal/technomancer_golem/place_spell_in_hand(var/path)
- if(!path || !ispath(path))
- return 0
-
- if(active_spell)
- qdel(active_spell) // Get rid of our old spell.
-
- var/obj/item/weapon/spell/S = new path(src)
- active_spell = S
-
-/mob/living/simple_animal/technomancer_golem/verb/test_giving_spells()
- var/choice = input(usr, "What spell?", "Give spell") as null|anything in known_spells
- if(choice)
- place_spell_in_hand(known_spells[choice])
- else
- qdel(active_spell)
-
-// Used to cast spells.
-/mob/living/simple_animal/technomancer_golem/RangedAttack(var/atom/A, var/params)
- if(active_spell)
- spellcast_pre_animation()
- if(active_spell.cast_methods & CAST_RANGED)
- active_spell.on_ranged_cast(A, src)
- spellcast_post_animation()
-
-/mob/living/simple_animal/technomancer_golem/UnarmedAttack(var/atom/A, var/proximity)
- if(proximity)
- if(active_spell)
- spellcast_pre_animation()
- if(!Adjacent(A)) // Need to check again since they might've moved while 'warming up'.
- spellcast_post_animation()
- return
- var/effective_cooldown = round(active_spell.cooldown * core.cooldown_modifier, 5)
- if(active_spell.cast_methods & CAST_MELEE)
- active_spell.on_melee_cast(A, src)
- else if(active_spell.cast_methods & CAST_RANGED)
- active_spell.on_ranged_cast(A, src)
- spellcast_post_animation()
- src.setClickCooldown(effective_cooldown)
- else
- ..()
-
-/mob/living/simple_animal/technomancer_golem/get_technomancer_core()
- return core
-
-/mob/living/simple_animal/technomancer_golem/proc/bind_to_mob(mob/user)
- if(!user || master)
- return
- master = user
- name = "[master]'s [initial(name)]"
-
-/mob/living/simple_animal/technomancer_golem/examine(mob/user)
- ..()
- if(user.mind && technomancers.is_antagonist(user.mind))
- user << "Your pride and joy. It's a very special synthetic robot, capable of using functions similar to you, and you built it \
- yourself! It'll always stand by your side, ready to help you out. You have no idea what GOLEM stands for, however..."
-
-/mob/living/simple_animal/technomancer_golem/Life()
- ..()
- handle_ai()
-
-// This is where the real spaghetti begins.
-/mob/living/simple_animal/technomancer_golem/proc/handle_ai()
- if(!master)
- return
- if(get_dist(src, master) > 6 || src.z != master.z)
- targeted_blink(master)
-
- // Give our allies buffs and heals.
- for(var/mob/living/L in view(src))
- if(L in friends)
- support_friend(L)
- return
-
-/mob/living/simple_animal/technomancer_golem/proc/support_friend(var/mob/living/L)
- if(L.getBruteLoss() >= 10 || L.getFireLoss() >= 10)
- if(L.isSynthetic() && !L.has_modifier_of_type(/datum/modifier/technomancer/mend_synthetic))
- place_spell_in_hand(known_spells["mend synthetic"])
- targeted_blink(L)
- UnarmedAttack(L, 1)
- else if(!L.has_modifier_of_type(/datum/modifier/technomancer/mend_life))
- place_spell_in_hand(known_spells["mend life"])
- targeted_blink(L)
- UnarmedAttack(L, 1)
- return
-
-
- // Give them repel missiles if they lack it.
- if(!L.has_modifier_of_type(/datum/modifier/technomancer/repel_missiles))
- place_spell_in_hand(known_spells["repel missiles"])
- RangedAttack(L)
- return
-
-/mob/living/simple_animal/technomancer_golem/proc/targeted_blink(var/atom/target)
- var/datum/effect/effect/system/spark_spread/spark_system = new()
- spark_system.set_up(5, 0, get_turf(src))
- spark_system.start()
- src.visible_message("\The [src] vanishes!")
- src.forceMove(get_turf(target))
- return
\ No newline at end of file
diff --git a/code/game/gamemodes/technomancer/core_obj.dm b/code/game/gamemodes/technomancer/core_obj.dm
index 1e7488258b..9400e24bf1 100644
--- a/code/game/gamemodes/technomancer/core_obj.dm
+++ b/code/game/gamemodes/technomancer/core_obj.dm
@@ -38,11 +38,11 @@
/obj/item/weapon/technomancer_core/New()
..()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
/obj/item/weapon/technomancer_core/Destroy()
dismiss_all_summons()
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
return ..()
// Add the spell buttons to the HUD.
@@ -130,7 +130,7 @@
for(var/mob/living/L in summoned_mobs)
summoned_mobs -= L
qdel(L)
- for(var/mob/living/simple_animal/ward/ward in wards_in_use)
+ for(var/mob/living/ward in wards_in_use)
wards_in_use -= ward
qdel(ward)
diff --git a/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm b/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm
index 0056675afc..11343fdeac 100644
--- a/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm
+++ b/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm
@@ -37,12 +37,12 @@
wearer = null
/obj/item/clothing/gloves/regen/New()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
..()
/obj/item/clothing/gloves/regen/Destroy()
wearer = null
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/clothing/gloves/regen/process()
diff --git a/code/game/gamemodes/technomancer/spell_objs_helpers.dm b/code/game/gamemodes/technomancer/spell_objs_helpers.dm
index 105b3b3e9c..cf83640775 100644
--- a/code/game/gamemodes/technomancer/spell_objs_helpers.dm
+++ b/code/game/gamemodes/technomancer/spell_objs_helpers.dm
@@ -1,12 +1,17 @@
-//Returns 1 if the turf is dense, or if there's dense objects on it, unless told to ignore them.
-/turf/proc/check_density(var/ignore_objs = 0)
+//Returns 1 if the turf is dense, or if there's dense objects/mobs on it, unless told to ignore them.
+/turf/proc/check_density(var/ignore_objs = FALSE, var/ignore_mobs = FALSE)
if(density)
- return 1
- if(!ignore_objs)
+ return TRUE
+ if(!ignore_objs || !ignore_mobs)
for(var/atom/movable/stuff in contents)
if(stuff.density)
- return 1
- return 0
+ if(ignore_objs && isobj(stuff))
+ continue
+ else if(ignore_mobs && isliving(stuff)) // Ghosts aren't dense but keeping this limited to living type will probably save headaches in the future.
+ continue
+ else
+ return TRUE
+ return FALSE
// Used to distinguish friend from foe.
/obj/item/weapon/spell/proc/is_ally(var/mob/living/L)
@@ -14,9 +19,9 @@
return 1
if(L.mind && technomancers.is_antagonist(L.mind)) // This should be done better since we might want opposing technomancers later.
return 1
- if(istype(L, /mob/living/simple_animal/hostile)) // Mind controlled simple mobs count as allies too.
- var/mob/living/simple_animal/SA = L
- if(owner in SA.friends)
+ if(istype(L, /mob/living/simple_mob)) // Mind controlled simple mobs count as allies too.
+ var/mob/living/simple_mob/SM = L
+ if(owner in SM.friends)
return 1
return 0
diff --git a/code/game/gamemodes/technomancer/spells/abjuration.dm b/code/game/gamemodes/technomancer/spells/abjuration.dm
index 146c90a987..0eb566a637 100644
--- a/code/game/gamemodes/technomancer/spells/abjuration.dm
+++ b/code/game/gamemodes/technomancer/spells/abjuration.dm
@@ -16,12 +16,12 @@
/obj/item/weapon/spell/abjuration/on_ranged_cast(atom/hit_atom, mob/user)
if(istype(hit_atom, /mob/living) && pay_energy(500) && within_range(hit_atom))
var/mob/living/L = hit_atom
- var/mob/living/simple_animal/SA = null
+ var/mob/living/simple_mob/SM = null
//Bit of a roundabout typecheck, in order to test for two variables from two different mob types in one line.
- if(istype(L, /mob/living/simple_animal))
- SA = L
- if(L.summoned || (SA && SA.supernatural) )
+ if(istype(L, /mob/living/simple_mob))
+ SM = L
+ if(L.summoned || (SM && SM.supernatural) )
if(L.client) // Player-controlled mobs are immune to being killed by this.
user << "\The [L] resists your attempt to banish it!"
L << "\The [user] tried to teleport you far away, but failed."
@@ -29,8 +29,8 @@
else
visible_message("\The [L] vanishes!")
qdel(L)
- else if(istype(L, /mob/living/simple_animal/construct))
- var/mob/living/simple_animal/construct/evil = L
+ else if(istype(L, /mob/living/simple_mob/construct))
+ var/mob/living/simple_mob/construct/evil = L
evil << "\The [user]'s abjuration purges your form!"
evil.purge = 3
adjust_instability(5)
diff --git a/code/game/gamemodes/technomancer/spells/aspect_aura.dm b/code/game/gamemodes/technomancer/spells/aspect_aura.dm
index 577f356229..7a2b15af19 100644
--- a/code/game/gamemodes/technomancer/spells/aspect_aura.dm
+++ b/code/game/gamemodes/technomancer/spells/aspect_aura.dm
@@ -44,10 +44,10 @@
/obj/item/weapon/spell/aura/New()
..()
set_light(7, 4, l_color = glow_color)
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
/obj/item/weapon/spell/aura/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
..()
/obj/item/weapon/spell/aura/process()
diff --git a/code/game/gamemodes/technomancer/spells/aura/aura.dm b/code/game/gamemodes/technomancer/spells/aura/aura.dm
index f3c7c93876..be986bb7ee 100644
--- a/code/game/gamemodes/technomancer/spells/aura/aura.dm
+++ b/code/game/gamemodes/technomancer/spells/aura/aura.dm
@@ -9,11 +9,11 @@
/obj/item/weapon/spell/aura/New()
..()
set_light(calculate_spell_power(7), calculate_spell_power(4), l_color = glow_color)
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
log_and_message_admins("has started casting [src].")
/obj/item/weapon/spell/aura/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
log_and_message_admins("has stopped maintaining [src].")
return ..()
diff --git a/code/game/gamemodes/technomancer/spells/control.dm b/code/game/gamemodes/technomancer/spells/control.dm
index 78c709040a..b41823dc54 100644
--- a/code/game/gamemodes/technomancer/spells/control.dm
+++ b/code/game/gamemodes/technomancer/spells/control.dm
@@ -21,93 +21,68 @@
aspect = ASPECT_BIOMED //Not sure if this should be something else.
var/image/control_overlay = null
var/list/controlled_mobs = list()
- var/list/allowed_mobs = list(
- /mob/living/bot,
- /mob/living/simple_animal/cat,
- /mob/living/simple_animal/chick,
- /mob/living/simple_animal/chicken,
- /mob/living/simple_animal/corgi,
- /mob/living/simple_animal/cow,
- /mob/living/simple_animal/crab,
- /mob/living/simple_animal/lizard,
- /mob/living/simple_animal/mouse,
- /mob/living/simple_animal/parrot,
- /mob/living/simple_animal/slime,
-// /mob/living/simple_animal/adultslime,
- /mob/living/simple_animal/tindalos,
- /mob/living/simple_animal/yithian,
- /mob/living/simple_animal/hostile/bear,
- /mob/living/simple_animal/hostile/carp,
- /mob/living/simple_animal/hostile/scarybat,
- /mob/living/simple_animal/hostile/viscerator,
- /mob/living/simple_animal/hostile/malf_drone,
- /mob/living/simple_animal/hostile/giant_spider,
- /mob/living/simple_animal/hostile/hivebot,
- /mob/living/simple_animal/retaliate/diyaab, //Doubt these will get used but might as well,
- /mob/living/simple_animal/hostile/savik,
- /mob/living/simple_animal/hostile/shantak
- )
+ var/allowed_mob_classes = MOB_CLASS_ANIMAL|MOB_CLASS_SYNTHETIC
//This unfortunately is gonna be rather messy due to the various mobtypes involved.
/obj/item/weapon/spell/control/proc/select(var/mob/living/L)
- if(!(is_type_in_list(L, allowed_mobs)))
- return 0
+ if(!(L.mob_class & allowed_mob_classes))
+ return FALSE
- if(istype(L, /mob/living/simple_animal))
- var/mob/living/simple_animal/SA = L
- SA.ai_inactive = 1
- SA.friends |= src.owner
- SA.stance = STANCE_IDLE
+ if(!L.has_AI())
+ return FALSE
- L.overlays |= control_overlay
+ var/datum/ai_holder/AI = L.ai_holder
+ AI.hostile = FALSE // The Technomancer chooses the target, not the AI.
+ AI.retaliate = TRUE
+ AI.wander = FALSE
+ AI.forget_everything()
+
+ if(istype(L, /mob/living/simple_mob))
+ var/mob/living/simple_mob/SM = L
+ SM.friends |= src.owner
+
+ L.add_overlay(control_overlay, TRUE)
controlled_mobs |= L
/obj/item/weapon/spell/control/proc/deselect(var/mob/living/L)
if(!(L in controlled_mobs))
- return 0
+ return FALSE
- if(istype(L, /mob/living/simple_animal))
- var/mob/living/simple_animal/SA = L
- SA.ai_inactive = 1
- if(istype(SA, /mob/living/simple_animal/hostile))
- var/mob/living/simple_animal/hostile/SAH = SA
- SAH.friends.Remove(owner)
+ if(L.has_AI())
+ var/datum/ai_holder/AI = L.ai_holder
+ AI.hostile = initial(AI.hostile)
+ AI.retaliate = initial(AI.retaliate)
+ AI.wander = initial(AI.wander)
+ AI.forget_everything()
- L.overlays.Remove(control_overlay)
+ if(istype(L, /mob/living/simple_mob))
+ var/mob/living/simple_mob/SM = L
+ SM.friends -= owner
+
+ L.cut_overlay(control_overlay, TRUE)
controlled_mobs.Remove(L)
/obj/item/weapon/spell/control/proc/move_all(turf/T)
- for(var/mob/living/living in controlled_mobs)
- if(living.stat)
- deselect(living)
+ for(var/mob/living/L in controlled_mobs)
+ if(!L.has_AI() || L.stat)
+ deselect(L)
continue
- if(istype(living, /mob/living/simple_animal))
- var/mob/living/simple_animal/SA = living
- SA.target_mob = null
- SA.stance = STANCE_IDLE
- walk_towards(SA,T,SA.speed)
- else
- walk_towards(living,T,5)
+ L.ai_holder.give_destination(T, 0, TRUE)
/obj/item/weapon/spell/control/proc/attack_all(mob/target)
for(var/mob/living/L in controlled_mobs)
- if(L.stat)
+ if(!L.has_AI() || L.stat)
deselect(L)
continue
- if(istype(L, /mob/living/simple_animal/hostile))
- var/mob/living/simple_animal/hostile/SAH
- SAH.target_mob = target
- else if(istype(L, /mob/living/bot))
- var/mob/living/bot/B = L
- B.UnarmedAttack(L)
+ L.ai_holder.give_target(target)
-/obj/item/weapon/spell/control/New()
+/obj/item/weapon/spell/control/Initialize()
control_overlay = image('icons/obj/spells.dmi',"controlled")
- ..()
+ return ..()
/obj/item/weapon/spell/control/Destroy()
- for(var/mob/living/simple_animal/hostile/SM in controlled_mobs)
- deselect(SM)
+ for(var/mob/living/L in controlled_mobs)
+ deselect(L)
controlled_mobs = list()
return ..()
@@ -129,11 +104,14 @@
trying to use it on yourself, perhaps you're an exception? Regardless, nothing happens."
return 0
- if(is_type_in_list(L, allowed_mobs))
+ if(L.mob_class & allowed_mob_classes)
if(!(L in controlled_mobs)) //Selecting
if(L.client)
user << "\The [L] seems to resist you!"
return 0
+ if(!L.has_AI())
+ to_chat(user, span("warning", "\The [L] seems too dim for this to work on them."))
+ return FALSE
if(pay_energy(500))
select(L)
user << "\The [L] is now under your (limited) control."
diff --git a/code/game/gamemodes/technomancer/spells/energy_siphon.dm b/code/game/gamemodes/technomancer/spells/energy_siphon.dm
index 65ac3794c4..aab144d907 100644
--- a/code/game/gamemodes/technomancer/spells/energy_siphon.dm
+++ b/code/game/gamemodes/technomancer/spells/energy_siphon.dm
@@ -22,11 +22,11 @@
/obj/item/weapon/spell/energy_siphon/New()
..()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
/obj/item/weapon/spell/energy_siphon/Destroy()
stop_siphoning()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/weapon/spell/energy_siphon/process()
diff --git a/code/game/gamemodes/technomancer/spells/gambit.dm b/code/game/gamemodes/technomancer/spells/gambit.dm
index e0116073dc..5e03a8ca19 100644
--- a/code/game/gamemodes/technomancer/spells/gambit.dm
+++ b/code/game/gamemodes/technomancer/spells/gambit.dm
@@ -75,9 +75,9 @@
for(var/mob/living/L in view(owner))
// Spiders, carp... bears.
- if(istype(L, /mob/living/simple_animal))
- var/mob/living/simple_animal/SM = L
- if(!is_ally(SM) && SM.hostile)
+ if(istype(L, /mob/living/simple_mob))
+ var/mob/living/simple_mob/SM = L
+ if(!is_ally(SM) && SM.has_AI() && SM.ai_holder.hostile)
hostile_mobs++
if(SM.summoned || SM.supernatural) // Our creations might be trying to kill us.
potential_spells |= /obj/item/weapon/spell/abjuration
diff --git a/code/game/gamemodes/technomancer/spells/illusion.dm b/code/game/gamemodes/technomancer/spells/illusion.dm
index 418df75539..8dccc2b9ec 100644
--- a/code/game/gamemodes/technomancer/spells/illusion.dm
+++ b/code/game/gamemodes/technomancer/spells/illusion.dm
@@ -14,7 +14,7 @@
aspect = ASPECT_LIGHT
cast_methods = CAST_RANGED | CAST_USE
var/atom/movable/copied = null
- var/mob/living/simple_animal/illusion/illusion = null
+ var/mob/living/simple_mob/illusion/illusion = null
/obj/item/weapon/spell/illusion/on_ranged_cast(atom/hit_atom, mob/user)
if(istype(hit_atom, /atom/movable))
@@ -33,17 +33,13 @@
if(pay_energy(500))
illusion = new(T)
illusion.copy_appearance(copied)
- if(ishuman(copied))
- var/mob/living/carbon/human/H = copied
- // This is to try to have the illusion move at the same rate the real mob world.
- illusion.step_delay = max(H.movement_delay() + 4, 3)
user << "An illusion of \the [copied] is made on \the [T]."
user << 'sound/effects/pop.ogg'
return 1
else
if(pay_energy(100))
- spawn(1)
- illusion.walk_loop(T)
+ var/datum/ai_holder/AI = illusion.ai_holder
+ AI.give_destination(T)
/obj/item/weapon/spell/illusion/on_use_cast(mob/user)
if(illusion)
@@ -76,116 +72,3 @@
temp_image.transform = M
// temp_image.pixel_y = 8
src.overlays.Add(temp_image)
-
-
-/mob/living/simple_animal/illusion
- name = "illusion" // gets overwritten
- desc = "If you can read me, the game broke. Please report this to a coder."
- resistance = 1000 // holograms are tough
- wander = 0
- response_help = "pushes a hand through"
- response_disarm = "tried to disarm"
- response_harm = "tried to punch"
- var/atom/movable/copying = null
- universal_speak = 1
- var/realistic = 0
- var/list/path = list() //Used for AStar pathfinding.
- var/walking = 0
- var/step_delay = 10
-
-/mob/living/simple_animal/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance().
- return
-
-/mob/living/simple_animal/illusion/proc/copy_appearance(var/atom/movable/thing_to_copy)
- if(!thing_to_copy)
- return 0
- name = thing_to_copy.name
- desc = thing_to_copy.desc
- gender = thing_to_copy.gender
- appearance = thing_to_copy.appearance
- copying = thing_to_copy
- return 1
-
-// We use special movement code for illusions, because BYOND's default pathfinding will use diagonal movement if it results
-// in the shortest path. As players are incapable of moving in diagonals, we must do this or else illusions will not be convincing.
-/mob/living/simple_animal/illusion/proc/calculate_path(var/turf/targeted_loc)
- if(!path.len || !path)
- spawn(0)
- path = AStar(loc, targeted_loc, /turf/proc/CardinalTurfs, /turf/proc/Distance, 0, 10, id = null)
- if(!path)
- path = list()
- return
-
-/mob/living/simple_animal/illusion/proc/walk_path(var/turf/targeted_loc)
- if(path && path.len)
- step_to(src, path[1])
- path -= path[1]
- return
- else
- if(targeted_loc)
- calculate_path(targeted_loc)
-
-/mob/living/simple_animal/illusion/proc/walk_loop(var/turf/targeted_loc)
- if(walking) //Already busy moving somewhere else.
- return 0
- walking = 1
- calculate_path(targeted_loc)
- if(!targeted_loc)
- walking = 0
- return 0
- if(path.len == 0)
- calculate_path(targeted_loc)
- while(loc != targeted_loc)
- walk_path(targeted_loc)
- sleep(step_delay)
- walking = 0
- return 1
-
-// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but
-// this is to prevent easy checks from the opposing force.
-/mob/living/simple_animal/illusion/examine(mob/user)
- if(copying)
- copying.examine(user)
- return
- ..()
-
-/mob/living/simple_animal/illusion/bullet_act(var/obj/item/projectile/P)
- if(!P)
- return
-
- if(realistic)
- return ..()
-
- return PROJECTILE_FORCE_MISS
-
-/mob/living/simple_animal/illusion/attack_hand(mob/living/carbon/human/M)
- if(!realistic)
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
- visible_message("[M]'s hand goes through \the [src]!")
- return
- else
- switch(M.a_intent)
-
- if(I_HELP)
- var/datum/gender/T = gender_datums[src.get_visible_gender()]
- M.visible_message("[M] hugs [src] to make [T.him] feel better!", \
- "You hug [src] to make [T.him] feel better!") // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it
- playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
-
- if(I_DISARM)
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
- visible_message("[M] attempted to disarm [src]!")
- M.do_attack_animation(src)
-
- if(I_GRAB)
- ..()
-
- if(I_HURT)
- adjustBruteLoss(harm_intent_damage)
- M.visible_message("[M] [response_harm] \the [src]")
- M.do_attack_animation(src)
-
- return
-
-/mob/living/simple_animal/illusion/ex_act()
- return
diff --git a/code/game/gamemodes/technomancer/spells/modifier/modifier.dm b/code/game/gamemodes/technomancer/spells/modifier/modifier.dm
index a8b9306945..3ea278286e 100644
--- a/code/game/gamemodes/technomancer/spells/modifier/modifier.dm
+++ b/code/game/gamemodes/technomancer/spells/modifier/modifier.dm
@@ -15,11 +15,13 @@
/obj/item/weapon/spell/modifier/on_melee_cast(atom/hit_atom, mob/user)
if(istype(hit_atom, /mob/living))
- on_add_modifier(hit_atom)
+ return on_add_modifier(hit_atom)
+ return FALSE
/obj/item/weapon/spell/modifier/on_ranged_cast(atom/hit_atom, mob/user)
if(istype(hit_atom, /mob/living))
- on_add_modifier(hit_atom)
+ return on_add_modifier(hit_atom)
+ return FALSE
/obj/item/weapon/spell/modifier/proc/on_add_modifier(var/mob/living/L)
@@ -32,6 +34,7 @@
MT.spell_power = calculate_spell_power(1)
log_and_message_admins("has casted [src] on [L].")
qdel(src)
+ return TRUE
// Technomancer specific subtype which keeps track of spell power and gets targeted specificially by Dispel.
/datum/modifier/technomancer
diff --git a/code/game/gamemodes/technomancer/spells/phase_shift.dm b/code/game/gamemodes/technomancer/spells/phase_shift.dm
index 336dbc4314..d73e45774c 100644
--- a/code/game/gamemodes/technomancer/spells/phase_shift.dm
+++ b/code/game/gamemodes/technomancer/spells/phase_shift.dm
@@ -31,12 +31,12 @@
/obj/effect/phase_shift/New()
..()
set_light(3, 5, l_color = "#FA58F4")
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
/obj/effect/phase_shift/Destroy()
for(var/atom/movable/AM in contents) //Eject everything out.
AM.forceMove(get_turf(src))
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/phase_shift/process()
diff --git a/code/game/gamemodes/technomancer/spells/radiance.dm b/code/game/gamemodes/technomancer/spells/radiance.dm
index fe7d83f713..d86428354e 100644
--- a/code/game/gamemodes/technomancer/spells/radiance.dm
+++ b/code/game/gamemodes/technomancer/spells/radiance.dm
@@ -19,11 +19,11 @@
/obj/item/weapon/spell/radiance/New()
..()
set_light(7, 4, l_color = "#D9D900")
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
log_and_message_admins("has casted [src].")
/obj/item/weapon/spell/radiance/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
log_and_message_admins("has stopped maintaining [src].")
return ..()
diff --git a/code/game/gamemodes/technomancer/spells/resurrect.dm b/code/game/gamemodes/technomancer/spells/resurrect.dm
index 90a352c95f..2663a132a9 100644
--- a/code/game/gamemodes/technomancer/spells/resurrect.dm
+++ b/code/game/gamemodes/technomancer/spells/resurrect.dm
@@ -30,13 +30,13 @@
this point."
return 0
user << "You stab \the [L] with a hidden integrated hypo, attempting to bring them back..."
- if(istype(L, /mob/living/simple_animal))
- var/mob/living/simple_animal/SM = L
+ if(istype(L, /mob/living/simple_mob))
+ var/mob/living/simple_mob/SM = L
SM.health = SM.getMaxHealth() / 3
SM.stat = CONSCIOUS
dead_mob_list -= SM
living_mob_list += SM
- SM.icon_state = SM.icon_living
+ SM.update_icon()
adjust_instability(15)
else if(ishuman(L))
var/mob/living/carbon/human/H = L
diff --git a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
index 5641df2f87..6dcd2cf4b2 100644
--- a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
+++ b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
@@ -11,7 +11,7 @@
icon_state = "radiance"
cast_methods = CAST_RANGED | CAST_THROW
aspect = ASPECT_EMP
- spawner_type = /obj/effect/temporary_effect/pulsar
+ spawner_type = /obj/effect/temporary_effect/pulse/pulsar
/obj/item/weapon/spell/spawner/pulsar/New()
..()
@@ -25,7 +25,29 @@
/obj/item/weapon/spell/spawner/pulsar/on_throw_cast(atom/hit_atom, mob/user)
empulse(hit_atom, 1, 1, 1, 1, log=1)
-/obj/effect/temporary_effect/pulsar
+// Does something every so often. Deletes itself when pulses_remaining hits zero.
+/obj/effect/temporary_effect/pulse
+ var/pulses_remaining = 3
+ var/pulse_delay = 2 SECONDS
+
+/obj/effect/temporary_effect/pulse/Initialize()
+ spawn(0)
+ pulse_loop()
+ return ..()
+
+/obj/effect/temporary_effect/pulse/proc/pulse_loop()
+ while(pulses_remaining)
+ sleep(pulse_delay)
+ on_pulse()
+ pulses_remaining--
+ qdel(src)
+
+// Override for specific effects.
+/obj/effect/temporary_effect/pulse/proc/on_pulse()
+
+
+
+/obj/effect/temporary_effect/pulse/pulsar
name = "pulsar"
desc = "Not a real pulsar, but still emits loads of EMP."
icon_state = "shield2"
@@ -33,17 +55,14 @@
light_range = 4
light_power = 5
light_color = "#2ECCFA"
- var/pulses_remaining = 3
+ pulses_remaining = 3
+
+/obj/effect/temporary_effect/pulse/pulsar/on_pulse()
+ empulse(src, 1, 1, 2, 2, log = 1)
+
+
+
+
-/obj/effect/temporary_effect/pulsar/New()
- ..()
- spawn(0)
- pulse_loop()
-/obj/effect/temporary_effect/pulsar/proc/pulse_loop()
- while(pulses_remaining)
- sleep(2 SECONDS)
- empulse(src, 1, 1, 2, 2, log = 1)
- pulses_remaining--
- qdel(src)
diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm b/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm
index 0e7e96104c..c29d9827ec 100644
--- a/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm
+++ b/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm
@@ -16,34 +16,32 @@
desc = "Chitter chitter."
summoned_mob_type = null
summon_options = list(
- "Mouse" = /mob/living/simple_animal/mouse,
- "Lizard" = /mob/living/simple_animal/lizard,
- "Chicken" = /mob/living/simple_animal/chicken,
- "Chick" = /mob/living/simple_animal/chick,
- "Crab" = /mob/living/simple_animal/crab,
- "Parrot" = /mob/living/simple_animal/parrot,
- "Goat" = /mob/living/simple_animal/retaliate/goat,
- "Cat" = /mob/living/simple_animal/cat,
- "Kitten" = /mob/living/simple_animal/cat/kitten,
- "Corgi" = /mob/living/simple_animal/corgi,
- "Corgi Pup" = /mob/living/simple_animal/corgi/puppy,
- "BAT" = /mob/living/simple_animal/hostile/scarybat,
- "SPIDER" = /mob/living/simple_animal/hostile/giant_spider,
- "SPIDER HUNTER" = /mob/living/simple_animal/hostile/giant_spider/hunter,
- "SPIDER NURSE" = /mob/living/simple_animal/hostile/giant_spider/nurse,
- "CARP" = /mob/living/simple_animal/hostile/carp,
- "BEAR" = /mob/living/simple_animal/hostile/bear
+ "Mouse" = /mob/living/simple_mob/animal/passive/mouse,
+ "Lizard" = /mob/living/simple_mob/animal/passive/lizard,
+ "Chicken" = /mob/living/simple_mob/animal/passive/chicken,
+ "Chick" = /mob/living/simple_mob/animal/passive/chick,
+ "Crab" = /mob/living/simple_mob/animal/passive/crab,
+ "Parrot" = /mob/living/simple_mob/animal/passive/bird/parrot,
+ "Goat" = /mob/living/simple_mob/animal/goat,
+ "Cat" = /mob/living/simple_mob/animal/passive/cat,
+ "Kitten" = /mob/living/simple_mob/animal/passive/cat/kitten,
+ "Corgi" = /mob/living/simple_mob/animal/passive/dog/corgi,
+ "Corgi Pup" = /mob/living/simple_mob/animal/passive/dog/corgi/puppy,
+ "BAT" = /mob/living/simple_mob/animal/space/bats,
+ "SPIDER" = /mob/living/simple_mob/animal/giant_spider,
+ "SPIDER HUNTER" = /mob/living/simple_mob/animal/giant_spider/hunter,
+ "SPIDER NURSE" = /mob/living/simple_mob/animal/giant_spider/nurse,
+ "CARP" = /mob/living/simple_mob/animal/space/carp,
+ "BEAR" = /mob/living/simple_mob/animal/space/bear
)
cooldown = 30
instability_cost = 10
energy_cost = 1000
-/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/simple_animal/summoned)
+/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/simple_mob/summoned)
if(check_for_scepter())
// summoned.faction = "technomancer"
- if(istype(summoned, /mob/living/simple_animal/hostile))
- var/mob/living/simple_animal/SA = summoned
- SA.friends.Add(owner)
+ summoned.friends += owner
// Makes their new pal big and strong, if they have spell power.
summoned.maxHealth = calculate_spell_power(summoned.maxHealth)
@@ -51,15 +49,12 @@
summoned.melee_damage_lower = calculate_spell_power(summoned.melee_damage_lower)
summoned.melee_damage_upper = calculate_spell_power(summoned.melee_damage_upper)
// This makes the summon slower, so the crew has a chance to flee from massive monsters.
- summoned.move_to_delay = calculate_spell_power(round(summoned.move_to_delay))
+ summoned.movement_cooldown = calculate_spell_power(round(summoned.movement_cooldown))
var/new_size = calculate_spell_power(1)
if(new_size != 1)
- var/matrix/M = matrix()
- M.Scale(new_size)
- M.Translate(0, 16*(new_size-1))
- summoned.transform = M
+ adjust_scale(new_size)
// Now we hurt their new pal, because being forcefully abducted by teleportation can't be healthy.
- summoned.health = round(summoned.getMaxHealth() * 0.7)
\ No newline at end of file
+ summoned.adjustBruteLoss(summoned.getMaxHealth() * 0.3) // Lose 30% of max health on arrival (but could be healed back up).
\ No newline at end of file
diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
index c6e5a2804d..9500044581 100644
--- a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
+++ b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
@@ -1,9 +1,8 @@
/datum/technomancer/spell/summon_ward
- name = "Summon Ward"
- desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you and your allies when it sees entities \
- moving around it, or when it is attacked. They can see for up to five meters."
- enhancement_desc = "Wards can detect invisibile entities, and are more specific in relaying information about what it sees. \
- Invisible entities that are spotted by it will be decloaked."
+ name = "Summon Monitor Ward"
+ desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you when it sees entities \
+ moving around it, or when it is attacked. They can see for up to five meters. It can also see invisible entities, and \
+ forcefully decloak them if close enough."
cost = 25
obj_path = /obj/item/weapon/spell/summon/summon_ward
category = UTILITY_SPELLS
@@ -12,116 +11,10 @@
name = "summon ward"
desc = "Finally, someone you can depend on to watch your back."
cast_methods = CAST_RANGED
- summoned_mob_type = /mob/living/simple_animal/ward
+ summoned_mob_type = /mob/living/simple_mob/mechanical/ward/monitor
cooldown = 10
instability_cost = 5
energy_cost = 500
-/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_animal/ward/ward)
- ward.creator = owner
- if(check_for_scepter())
- ward.true_sight = 1
- ward.see_invisible = SEE_INVISIBLE_LEVEL_TWO
-
-/mob/living/simple_animal/ward
- name = "ward"
- desc = "It's a little flying drone that seems to be watching you..."
- icon = 'icons/mob/critter.dmi'
- icon_state = "ward"
- resistance = 5
- wander = 0
- response_help = "pets the"
- response_disarm = "swats away"
- response_harm = "punches"
- min_oxy = 0
- max_oxy = 0
- min_tox = 0
- max_tox = 0
- min_co2 = 0
- max_co2 = 0
- min_n2 = 0
- max_n2 = 0
- minbodytemp = 0
- maxbodytemp = 0
- unsuitable_atoms_damage = 0
- heat_damage_per_tick = 0
- cold_damage_per_tick = 0
-
- var/true_sight = 0 // If true, detects more than what the Technomancer normally can't.
- var/mob/living/carbon/human/creator = null
- var/list/seen_mobs = list()
-
-/mob/living/simple_animal/ward/death()
- if(creator)
- creator << "Your ward inside [get_area(src)] was killed!"
- ..()
- qdel(src)
-
-/mob/living/simple_animal/ward/proc/expire()
- if(creator && src)
- creator << "Your ward inside [get_area(src)] expired."
- qdel(src)
-
-/mob/living/simple_animal/ward/Life()
- ..()
- detect_mobs()
- update_icon()
-
-/mob/living/simple_animal/ward/proc/detect_mobs()
- var/list/things_in_sight = view(5,src)
- var/list/newly_seen_mobs = list()
- for(var/mob/living/L in things_in_sight)
- if(L == creator) // I really wish is_ally() was usable here.
- continue
-
- if(istype(L, /mob/living/simple_animal/ward))
- continue
-
- if(istype(L, /mob/living/simple_animal))
- var/mob/living/simple_animal/SA = L
- if(creator in SA.friends)
- continue
-
- if(!true_sight)
- var/turf/T = get_turf(L)
- var/light_amount = T.get_lumcount()
- if(light_amount <= 0.5)
- continue // Too dark to see.
-
- if(L.alpha <= 127)
- continue // Too transparent, as a mercy to camo lings.
-
- else
- L.break_cloak()
-
- // Warn the Technomancer when it sees a new mob.
- if(!(L in seen_mobs))
- seen_mobs.Add(L)
- newly_seen_mobs.Add(L)
- if(creator)
- if(true_sight)
- creator << "Your ward at [get_area(src)] detected [english_list(newly_seen_mobs)]."
- else
- creator << "Your ward at [get_area(src)] detected something."
-
- // Now get rid of old mobs that left vision.
- for(var/mob/living/L in seen_mobs)
- if(!(L in things_in_sight))
- seen_mobs.Remove(L)
-
-
-/mob/living/simple_animal/ward/update_icon()
- if(seen_mobs.len)
- icon_state = "ward_spotted"
- set_light(3, 3, l_color = "FF0000")
- else
- icon_state = "ward"
- set_light(3, 3, l_color = "00FF00")
- if(true_sight)
- overlays.Cut()
- var/image/I = image('icons/mob/critter.dmi',"ward_truesight")
- overlays.Add(I)
-
-/mob/living/simple_animal/ward/invisible_detect
- true_sight = 1
- see_invisible = SEE_INVISIBLE_LEVEL_TWO
+/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_mob/mechanical/ward/monitor/my_ward)
+ my_ward.owner = owner
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 798c94eb9b..9c9e1811cc 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -181,7 +181,7 @@
RefreshParts()
-/obj/machinery/sleeper/initialize()
+/obj/machinery/sleeper/Initialize()
. = ..()
update_icon()
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index caed68e7c4..cb6200dcaa 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -49,10 +49,9 @@
if(occupant)
to_chat(user, "\The [src] is already occupied!")
return
- for(var/mob/living/simple_animal/slime/M in range(1, H.affecting))
- if(M.victim == H.affecting)
- to_chat(user, "[H.affecting.name] has a slime attached to them, deal with that first.")
- return
+ if(H.affecting.has_buckled_mobs())
+ to_chat(user, span("warning", "\The [H.affecting] has other entities attached to it. Remove them first."))
+ return
var/mob/M = H.affecting
if(M.abiotic())
to_chat(user, "Subject cannot have abiotic items on.")
@@ -86,10 +85,9 @@
if(O.abiotic())
to_chat(user, "Subject cannot have abiotic items on.")
return 0
- for(var/mob/living/simple_animal/slime/M in range(1, O))
- if(M.victim == O)
- to_chat(user, "[O] has a slime attached to them, deal with that first.")
- return 0
+ if(O.has_buckled_mobs())
+ to_chat(user, span("warning", "\The [O] has other entities attached to it. Remove them first."))
+ return
if(O == user)
visible_message("[user] climbs into \the [src].")
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 2779528eb1..dfb35dcdb7 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -137,7 +137,7 @@
TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K
-/obj/machinery/alarm/initialize()
+/obj/machinery/alarm/Initialize()
. = ..()
set_frequency(frequency)
if(!master_is_operating())
@@ -895,7 +895,7 @@ FIRE ALARM
alarm()
time = 0
timing = 0
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
updateDialog()
last_process = world.timeofday
@@ -964,7 +964,7 @@ FIRE ALARM
else if(href_list["time"])
timing = text2num(href_list["time"])
last_process = world.timeofday
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
else if(href_list["tp"])
var/tp = text2num(href_list["tp"])
time += tp
@@ -1002,7 +1002,7 @@ FIRE ALARM
seclevel = newlevel
update_icon()
-/obj/machinery/firealarm/initialize()
+/obj/machinery/firealarm/Initialize()
. = ..()
if(z in using_map.contact_levels)
set_security_level(security_level? get_security_level() : "green")
diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm
index 59e9670d1f..839470f356 100644
--- a/code/game/machinery/atmo_control.dm
+++ b/code/game/machinery/atmo_control.dm
@@ -63,7 +63,7 @@
frequency = new_frequency
radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
-/obj/machinery/air_sensor/initialize()
+/obj/machinery/air_sensor/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
@@ -129,7 +129,7 @@ obj/machinery/computer/general_air_control/Destroy()
frequency = new_frequency
radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
-/obj/machinery/computer/general_air_control/initialize()
+/obj/machinery/computer/general_air_control/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
diff --git a/code/game/machinery/atmoalter/area_atmos_computer.dm b/code/game/machinery/atmoalter/area_atmos_computer.dm
index 0d0259db74..b0277785fd 100644
--- a/code/game/machinery/atmoalter/area_atmos_computer.dm
+++ b/code/game/machinery/atmoalter/area_atmos_computer.dm
@@ -14,125 +14,123 @@
//Simple variable to prevent me from doing attack_hand in both this and the child computer
var/zone = "This computer is working on a wireless range, the range is currently limited to 25 meters."
- initialize()
- . = ..()
- scanscrubbers()
+/obj/machinery/computer/area_atmos/Initialize()
+ . = ..()
+ scanscrubbers()
- attack_ai(var/mob/user as mob)
- return src.attack_hand(user)
-
- attack_hand(var/mob/user as mob)
- if(..(user))
- return
- src.add_fingerprint(usr)
- var/dat = {"
-
-
-
-
-
- Area Air Control
- [status]
- Scan
- "}
- for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in connectedscrubbers)
- dat += {"
-
-
- [scrubber.name]
- Pressure: [round(scrubber.air_contents.return_pressure(), 0.01)] kPa
- Flow Rate: [round(scrubber.last_flow_rate,0.1)] L/s
- |
-
- Turn On
- Turn Off
- Load: [round(scrubber.last_power_draw)] W
- |
-
"}
+/obj/machinery/computer/area_atmos/attack_ai(var/mob/user as mob)
+ return src.attack_hand(user)
+/obj/machinery/computer/area_atmos/attack_hand(var/mob/user as mob)
+ if(..(user))
+ return
+ src.add_fingerprint(usr)
+ var/dat = {"
+
+
+
+
+
+ Area Air Control
+ [status]
+ Scan
+ "}
+ for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in connectedscrubbers)
dat += {"
-
- [zone]
-
- "}
- user << browse("[dat]", "window=miningshuttle;size=400x400")
- status = ""
+
+
+ [scrubber.name]
+ Pressure: [round(scrubber.air_contents.return_pressure(), 0.01)] kPa
+ Flow Rate: [round(scrubber.last_flow_rate,0.1)] L/s
+ |
+
+ Turn On
+ Turn Off
+ Load: [round(scrubber.last_power_draw)] W
+ |
+
"}
- Topic(href, href_list)
- if(..())
+ dat += {"
+
+ [zone]
+
+ "}
+ user << browse("[dat]", "window=miningshuttle;size=400x400")
+ status = ""
+
+/obj/machinery/computer/area_atmos/Topic(href, href_list)
+ if(..())
+ return
+ usr.set_machine(src)
+ src.add_fingerprint(usr)
+
+
+ if(href_list["scan"])
+ scanscrubbers()
+ else if(href_list["toggle"])
+ var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber = locate(href_list["scrub"])
+
+ if(!validscrubber(scrubber))
+ spawn(20)
+ status = "ERROR: Couldn't connect to scrubber! (timeout)"
+ connectedscrubbers -= scrubber
+ src.updateUsrDialog()
return
- usr.set_machine(src)
- src.add_fingerprint(usr)
+ scrubber.on = text2num(href_list["toggle"])
+ scrubber.update_icon()
- if(href_list["scan"])
- scanscrubbers()
- else if(href_list["toggle"])
- var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber = locate(href_list["scrub"])
+/obj/machinery/computer/area_atmos/proc/validscrubber(obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber as obj)
+ if(!isobj(scrubber) || get_dist(scrubber.loc, src.loc) > src.range || scrubber.loc.z != src.loc.z)
+ return FALSE
+ return TRUE
- if(!validscrubber(scrubber))
- spawn(20)
- status = "ERROR: Couldn't connect to scrubber! (timeout)"
- connectedscrubbers -= scrubber
- src.updateUsrDialog()
- return
+/obj/machinery/computer/area_atmos/proc/scanscrubbers()
+ connectedscrubbers = new()
- scrubber.on = text2num(href_list["toggle"])
- scrubber.update_icon()
+ var/found = 0
+ for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in range(range, src.loc))
+ if(istype(scrubber))
+ found = 1
+ connectedscrubbers += scrubber
- proc/validscrubber( var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber as obj )
- if(!isobj(scrubber) || get_dist(scrubber.loc, src.loc) > src.range || scrubber.loc.z != src.loc.z)
- return 0
-
- return 1
-
- proc/scanscrubbers()
- connectedscrubbers = new()
-
- var/found = 0
- for(var/obj/machinery/portable_atmospherics/powered/scrubber/huge/scrubber in range(range, src.loc))
- if(istype(scrubber))
- found = 1
- connectedscrubbers += scrubber
-
- if(!found)
- status = "ERROR: No scrubber found!"
-
- src.updateUsrDialog()
+ if(!found)
+ status = "ERROR: No scrubber found!"
+ updateUsrDialog()
/obj/machinery/computer/area_atmos/area
zone = "This computer is working in a wired network limited to this area."
diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm
index 0b1f4c3d77..603f88e0c9 100644
--- a/code/game/machinery/atmoalter/meter.dm
+++ b/code/game/machinery/atmoalter/meter.dm
@@ -12,7 +12,7 @@
use_power = 1
idle_power_usage = 15
-/obj/machinery/meter/initialize()
+/obj/machinery/meter/Initialize()
. = ..()
if (!target)
target = select_target()
diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm
index 73f5abce4b..8778b4e5f3 100644
--- a/code/game/machinery/atmoalter/portable_atmospherics.dm
+++ b/code/game/machinery/atmoalter/portable_atmospherics.dm
@@ -26,7 +26,7 @@
QDEL_NULL(holding)
. = ..()
-/obj/machinery/portable_atmospherics/initialize()
+/obj/machinery/portable_atmospherics/Initialize()
. = ..()
spawn()
var/obj/machinery/atmospherics/portables_connector/port = locate() in loc
diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm
index 346e26370f..8c278c63fa 100644
--- a/code/game/machinery/biogenerator.dm
+++ b/code/game/machinery/biogenerator.dm
@@ -1,6 +1,6 @@
/obj/machinery/biogenerator
name = "biogenerator"
- desc = ""
+ desc = "Converts plants into biomass, which can be used for fertilizer and sort-of-synthetic products."
icon = 'icons/obj/biogenerator.dmi'
icon_state = "biogen-stand"
density = 1
@@ -50,7 +50,7 @@
return
if(istype(O, /obj/item/weapon/reagent_containers/glass))
if(beaker)
- to_chat(user, "]The [src] is already loaded.")
+ to_chat(user, "\The [src] is already loaded.")
else
user.remove_from_mob(O)
O.loc = src
@@ -104,23 +104,25 @@
if(beaker)
dat += "Activate Biogenerator!
"
dat += "Detach Container
"
- dat += "Food
"
- dat += "10 milk ([round(20/build_eff)])
"
- dat += "Slab of meat ([round(50/build_eff)])
"
- dat += "Nutrient
"
+ dat += "Food:
"
+ dat += "10 milk ([round(20/build_eff)]) | x5
"
+ dat += "10 cream ([round(20/build_eff)]) | x5
"
+ dat += "Slab of meat ([round(50/build_eff)]) | x5
"
+ dat += "Nutrient:
"
dat += "E-Z-Nutrient ([round(60/build_eff)]) | x5
"
dat += "Left 4 Zed ([round(120/build_eff)]) | x5
"
dat += "Robust Harvest ([round(150/build_eff)]) | x5
"
- dat += "Leather
"
+ dat += "Leather:
"
dat += "Wallet ([round(100/build_eff)])
"
dat += "Botanical gloves ([round(250/build_eff)])
"
+ dat += "Plant bag ([round(250/build_eff)])
"
+ dat += "Large plant bag ([round(250/build_eff)])
"
dat += "Utility belt ([round(300/build_eff)])
"
dat += "Leather Satchel ([round(400/build_eff)])
"
dat += "Cash Bag ([round(400/build_eff)])
"
dat += "Chemistry Bag ([round(400/build_eff)])
"
dat += "Workboots ([round(400/build_eff)])
"
dat += "Leather Shoes ([round(400/build_eff)])
"
-
dat += "Leather Chaps ([round(400/build_eff)])
"
dat += "Leather Coat ([round(500/build_eff)])
"
dat += "Leather Jacket ([round(500/build_eff)])
"
@@ -164,10 +166,11 @@
processing = 1
update_icon()
updateUsrDialog()
- playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
+ playsound(src.loc, 'sound/machines/blender.ogg', 40, 1)
use_power(S * 30)
sleep((S + 15) / eat_eff)
processing = 0
+ playsound(src.loc, 'sound/machines/biogenerator_end.ogg', 40, 1)
update_icon()
else
menustat = "void"
@@ -186,8 +189,20 @@
switch(item)
if("milk")
beaker.reagents.add_reagent("milk", 10)
+ if("milk5")
+ beaker.reagents.add_reagent("milk", 50)
+ if("cream")
+ beaker.reagents.add_reagent("cream", 10)
+ if("cream5")
+ beaker.reagents.add_reagent("cream", 50)
if("meat")
new/obj/item/weapon/reagent_containers/food/snacks/meat(loc)
+ if("meat5")
+ new/obj/item/weapon/reagent_containers/food/snacks/meat(loc) //This is ugly.
+ new/obj/item/weapon/reagent_containers/food/snacks/meat(loc)
+ new/obj/item/weapon/reagent_containers/food/snacks/meat(loc)
+ new/obj/item/weapon/reagent_containers/food/snacks/meat(loc)
+ new/obj/item/weapon/reagent_containers/food/snacks/meat(loc)
if("ez")
new/obj/item/weapon/reagent_containers/glass/bottle/eznutrient(loc)
if("l4z")
@@ -216,6 +231,10 @@
new/obj/item/weapon/storage/wallet(loc)
if("gloves")
new/obj/item/clothing/gloves/botanic_leather(loc)
+ if("plantbag")
+ new/obj/item/weapon/storage/bag/plants(loc)
+ if("plantbaglarge")
+ new/obj/item/weapon/storage/bag/plants/large(loc)
if("tbelt")
new/obj/item/weapon/storage/belt/utility(loc)
if("satchel")
diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm
index f66b580c52..cc982ce207 100644
--- a/code/game/machinery/bioprinter.dm
+++ b/code/game/machinery/bioprinter.dm
@@ -287,7 +287,7 @@
/obj/machinery/organ_printer/robot/dismantle()
if(stored_matter >= matter_amount_per_sheet)
- new /obj/item/stack/material/steel(get_turf(src), Floor(stored_matter/matter_amount_per_sheet))
+ new /obj/item/stack/material/steel(get_turf(src), FLOOR(stored_matter/matter_amount_per_sheet, 1))
return ..()
/obj/machinery/organ_printer/robot/print_organ(var/choice)
@@ -305,7 +305,7 @@
return
var/obj/item/stack/S = W
var/space_left = max_stored_matter - stored_matter
- var/sheets_to_take = min(S.amount, Floor(space_left/matter_amount_per_sheet))
+ var/sheets_to_take = min(S.amount, FLOOR(space_left/matter_amount_per_sheet, 1))
if(sheets_to_take <= 0)
to_chat(user, "\The [src] is too full.")
return
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 484adf6068..d484e7c882 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -91,7 +91,7 @@
kick_viewers()
update_icon()
update_coverage()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
/obj/machinery/camera/bullet_act(var/obj/item/projectile/P)
take_damage(P.get_structure_damage())
@@ -138,7 +138,7 @@
/obj/machinery/camera/attack_generic(mob/user as mob)
if(isanimal(user))
- var/mob/living/simple_animal/S = user
+ var/mob/living/simple_mob/S = user
set_status(0)
S.do_attack_animation(src)
S.setClickCooldown(user.get_attack_speed())
@@ -390,7 +390,7 @@
return 0
// Do after stuff here
- user << "You start to weld the [src].."
+ user << "You start to weld [src].."
playsound(src.loc, WT.usesound, 50, 1)
WT.eyecheck(user)
busy = 1
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 69681d1775..2dbe4e6176 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -194,7 +194,7 @@
occupant.adjustCloneLoss(-2 * heal_rate)
//Premature clones may have brain damage.
- occupant.adjustBrainLoss(-(ceil(0.5*heal_rate)))
+ occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1)))
//So clones don't die of oxyloss in a running pod.
if(occupant.reagents.get_reagent_amount("inaprovaline") < 30)
diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm
index 3826eb3c94..14ba6dd005 100644
--- a/code/game/machinery/computer/atmos_control.dm
+++ b/code/game/machinery/computer/atmos_control.dm
@@ -20,6 +20,7 @@
/obj/machinery/computer/atmoscontrol/laptop
name = "Atmospherics Laptop"
desc = "A cheap laptop."
+ icon_screen = "atmoslaptop"
icon_state = "laptop"
icon_keyboard = "laptop_key"
density = 0
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index b35f74902d..abdade7b4f 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -225,7 +225,7 @@
circuit = /obj/item/weapon/circuitboard/security/telescreen/entertainment
var/obj/item/device/radio/radio = null
-/obj/machinery/computer/security/telescreen/entertainment/initialize()
+/obj/machinery/computer/security/telescreen/entertainment/Initialize()
. = ..()
radio = new(src)
radio.listening = TRUE
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index cbf0041025..44a1f69945 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -16,7 +16,7 @@
var/loading = 0 // Nice loading text
-/obj/machinery/computer/cloning/initialize()
+/obj/machinery/computer/cloning/Initialize()
. = ..()
updatemodules()
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index 36c2a894e7..309a066404 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -15,11 +15,13 @@
var/light_power_on = 1
var/overlay_layer
+ clicksound = "keyboard"
+
/obj/machinery/computer/New()
overlay_layer = layer
..()
-/obj/machinery/computer/initialize()
+/obj/machinery/computer/Initialize()
. = ..()
power_change()
update_icon()
diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm
index 91a4a76bb8..eaea426c07 100644
--- a/code/game/machinery/computer/guestpass.dm
+++ b/code/game/machinery/computer/guestpass.dm
@@ -54,13 +54,13 @@
expired = 1
return ..()
-/obj/item/weapon/card/id/guest/initialize()
+/obj/item/weapon/card/id/guest/Initialize()
. = ..()
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
update_icon()
/obj/item/weapon/card/id/guest/Destroy()
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/weapon/card/id/guest/process()
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index 30182937d4..c153e37190 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -5,6 +5,7 @@
desc = "Used to view, edit and maintain medical records."
icon_keyboard = "med_key"
icon_screen = "medcomp"
+ density = 0 //Why does a laptop blocks peoples.
light_color = "#315ab4"
req_one_access = list(access_medical, access_forensics_lockers, access_robotics)
circuit = /obj/item/weapon/circuitboard/med_data
diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm
index 670b637152..6fce75def7 100644
--- a/code/game/machinery/computer/message.dm
+++ b/code/game/machinery/computer/message.dm
@@ -71,7 +71,7 @@
icon_screen = initial(icon_screen)
..()
-/obj/machinery/computer/message_monitor/initialize()
+/obj/machinery/computer/message_monitor/Initialize()
//Is the server isn't linked to a server, and there's a server available, default it to the first one in the list.
if(!linkedServer)
if(message_servers && message_servers.len > 0)
diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm
index 8c435f5845..8fe6ffef8a 100644
--- a/code/game/machinery/computer/prisonshuttle.dm
+++ b/code/game/machinery/computer/prisonshuttle.dm
@@ -201,7 +201,7 @@ var/prison_shuttle_timeleft = 0
for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area...
bug.gib()
- for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug...
+ for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug...
pest.gib()
start_location.move_contents_to(end_location)
diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm
index 1f7c959db5..431d6278d6 100644
--- a/code/game/machinery/computer/specops_shuttle.dm
+++ b/code/game/machinery/computer/specops_shuttle.dm
@@ -81,7 +81,7 @@ var/specops_shuttle_timeleft = 0
for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area...
bug.gib()
- for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug...
+ for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug...
pest.gib()
start_location.move_contents_to(end_location)
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index d3ab69b869..8bd2f91f89 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -17,7 +17,7 @@
monitor_type = /datum/nano_module/alarm_monitor/all
circuit = /obj/item/weapon/circuitboard/stationalert_all
-/obj/machinery/computer/station_alert/initialize()
+/obj/machinery/computer/station_alert/Initialize()
alarm_monitor = new monitor_type(src)
alarm_monitor.register_alarm(src, /obj/machinery/computer/station_alert/update_icon)
. = ..()
diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm
index ba7193b69b..e02ab68474 100644
--- a/code/game/machinery/computer/syndicate_specops_shuttle.dm
+++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm
@@ -166,7 +166,7 @@ var/syndicate_elite_shuttle_timeleft = 0
for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area...
bug.gib()
- for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug...
+ for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug...
pest.gib()
start_location.move_contents_to(end_location)
diff --git a/code/game/machinery/computer3/computers/HolodeckControl.dm b/code/game/machinery/computer3/computers/HolodeckControl.dm
index fcff13c968..da3c2416fd 100644
--- a/code/game/machinery/computer3/computers/HolodeckControl.dm
+++ b/code/game/machinery/computer3/computers/HolodeckControl.dm
@@ -207,7 +207,7 @@
for(var/obj/effect/decal/cleanable/blood/B in linkedholodeck)
qdel(B)
- for(var/mob/living/simple_animal/hostile/carp/C in linkedholodeck)
+ for(var/mob/living/simple_mob/animal/space/carp/C in linkedholodeck)
qdel(C)
holographic_items = A.copy_contents_to(linkedholodeck , 1)
@@ -228,7 +228,7 @@
T.temperature = 5000
T.hotspot_expose(50000,50000,1)
if(L.name=="Holocarp Spawn")
- new /mob/living/simple_animal/hostile/carp(L.loc)
+ new /mob/living/simple_mob/animal/space/carp(L.loc)
/datum/file/program/holodeck/proc/emergencyShutdown()
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index 20a54046a6..809ada22c8 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -30,7 +30,7 @@
icon_state = "base"
initialize_directions = dir
-/obj/machinery/atmospherics/unary/cryo_cell/initialize()
+/obj/machinery/atmospherics/unary/cryo_cell/Initialize()
. = ..()
var/image/tank = image(icon,"tank")
tank.alpha = 200
@@ -199,14 +199,13 @@
return
if(occupant)
to_chat(user,"\The [src] is already occupied by [occupant].")
- for(var/mob/living/simple_animal/slime/M in range(1,grab.affecting))
- if(M.victim == grab.affecting)
- to_chat(usr, "[grab.affecting.name] will not fit into the cryo because they have a slime latched onto their head.")
- return
+ if(grab.affecting.has_buckled_mobs())
+ to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first."))
+ return
var/mob/M = grab.affecting
qdel(grab)
put_mob(M)
-
+
return
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance
@@ -349,14 +348,14 @@
set name = "Move Inside"
set category = "Object"
set src in oview(1)
- for(var/mob/living/simple_animal/slime/M in range(1,usr))
- if(M.victim == usr)
- to_chat(usr, "You're too busy getting your life sucked out of you.")
+ if(isliving(usr))
+ var/mob/living/L = usr
+ if(L.has_buckled_mobs())
+ to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
return
- if(usr.stat != 0)
- return
- put_mob(usr)
- return
+ if(L.stat != CONSCIOUS)
+ return
+ put_mob(L)
/atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform)
return return_air()
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 6e74466be3..f67ecff2cd 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -299,7 +299,7 @@
occupant.resting = 1
return ..()
-/obj/machinery/cryopod/initialize()
+/obj/machinery/cryopod/Initialize()
. = ..()
find_control_computer()
@@ -532,9 +532,10 @@
to_chat(usr, "\The [src] is in use.")
return
- for(var/mob/living/simple_animal/slime/M in range(1,usr))
- if(M.victim == usr)
- to_chat(usr, "You're too busy getting your life sucked out of you.")
+ if(isliving(usr))
+ var/mob/living/L = usr
+ if(L.has_buckled_mobs())
+ to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
return
visible_message("[usr] [on_enter_visible_message] [src].", 3)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 15bbc512ab..961d0fd6aa 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -38,11 +38,12 @@
var/bolt_up_sound = 'sound/machines/boltsup.ogg'
var/bolt_down_sound = 'sound/machines/boltsdown.ogg'
-/obj/machinery/door/airlock/attack_generic(var/mob/user, var/damage)
+/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage)
if(stat & (BROKEN|NOPOWER))
- if(damage >= 10)
+ if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
if(src.locked || src.welded)
visible_message("\The [user] begins breaking into \the [src] internals!")
+ user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything.
if(do_after(user,10 SECONDS,src))
src.locked = 0
src.welded = 0
@@ -50,6 +51,7 @@
open(1)
if(prob(25))
src.shock(user, 100)
+ user.set_AI_busy(FALSE)
else if(src.density)
visible_message("\The [user] forces \the [src] open!")
open(1)
@@ -476,9 +478,6 @@ About the new airlock wires panel:
return
..(user)
-/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user as mob)
- ..(user)
-
/obj/machinery/door/airlock/proc/isElectrified()
if(src.electrified_until != 0)
return 1
@@ -1173,7 +1172,7 @@ About the new airlock wires panel:
else
wires = new/datum/wires/airlock(src)
-/obj/machinery/door/airlock/initialize()
+/obj/machinery/door/airlock/Initialize()
if(src.closeOtherId != null)
for (var/obj/machinery/door/airlock/A in machines)
if(A.closeOtherId == src.closeOtherId && A != src)
@@ -1226,3 +1225,24 @@ About the new airlock wires panel:
src.open()
src.lock()
return
+
+
+/obj/machinery/door/airlock/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode)
+ switch(passed_mode)
+ if(RCD_DECONSTRUCT)
+ // Old RCD code made it cost 10 units to decon an airlock.
+ // Now the new one costs ten "sheets".
+ return list(
+ RCD_VALUE_MODE = RCD_DECONSTRUCT,
+ RCD_VALUE_DELAY = 5 SECONDS,
+ RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10
+ )
+ return FALSE
+
+/obj/machinery/door/airlock/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode)
+ switch(passed_mode)
+ if(RCD_DECONSTRUCT)
+ to_chat(user, span("notice", "You deconstruct \the [src]."))
+ qdel(src)
+ return TRUE
+ return FALSE
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index 295a7ff24f..cbd0875cd3 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -129,7 +129,7 @@ obj/machinery/door/airlock/proc/set_frequency(new_frequency)
radio_connection = radio_controller.add_object(src, frequency, RADIO_AIRLOCK)
-obj/machinery/door/airlock/initialize()
+obj/machinery/door/airlock/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
@@ -203,7 +203,7 @@ obj/machinery/airlock_sensor/proc/set_frequency(new_frequency)
frequency = new_frequency
radio_connection = radio_controller.add_object(src, frequency, RADIO_AIRLOCK)
-obj/machinery/airlock_sensor/initialize()
+obj/machinery/airlock_sensor/Initialize()
. = ..()
set_frequency(frequency)
@@ -276,7 +276,7 @@ obj/machinery/access_button/proc/set_frequency(new_frequency)
radio_connection = radio_controller.add_object(src, frequency, RADIO_AIRLOCK)
-obj/machinery/access_button/initialize()
+obj/machinery/access_button/Initialize()
. = ..()
set_frequency(frequency)
diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm
index 1c384a651d..f7dda48feb 100644
--- a/code/game/machinery/doors/alarmlock.dm
+++ b/code/game/machinery/doors/alarmlock.dm
@@ -18,7 +18,7 @@
radio_controller.remove_object(src,air_frequency)
..()
-/obj/machinery/door/airlock/alarmlock/initialize()
+/obj/machinery/door/airlock/alarmlock/Initialize()
. = ..()
radio_controller.remove_object(src, air_frequency)
air_connection = radio_controller.add_object(src, air_frequency, RADIO_TO_AIRALARM)
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index f4926c2aa8..ab8819f8da 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -32,7 +32,7 @@
//turning this off prevents awkward zone geometry in places like medbay lobby, for example.
block_air_zones = 0
-/obj/machinery/door/blast/initialize()
+/obj/machinery/door/blast/Initialize()
. = ..()
implicit_material = get_material_by_name("plasteel")
@@ -151,7 +151,7 @@
return
else if(istype(C, /obj/item/stack/material) && C.get_material_name() == "plasteel") // Repairing.
- var/amt = Ceiling((maxhealth - health)/150)
+ var/amt = CEILING((maxhealth - health)/150, 1)
if(!amt)
to_chat(user, "\The [src] is already fully repaired.")
return
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index fe1d726d50..0935dfa724 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -31,7 +31,7 @@
maptext_height = 26
maptext_width = 32
-/obj/machinery/door_timer/initialize()
+/obj/machinery/door_timer/Initialize()
..()
//Doors need to go first, and can't rely on init order, so come back to me.
return INITIALIZE_HINT_LATELOAD
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index cf7b26f8a5..a2beb60d77 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -39,8 +39,8 @@
/obj/machinery/door/attack_generic(var/mob/user, var/damage)
if(isanimal(user))
- var/mob/living/simple_animal/S = user
- if(damage >= 10)
+ var/mob/living/simple_mob/S = user
+ if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
visible_message("\The [user] smashes into the [src]!")
playsound(src, S.attack_sound, 75, 1)
take_damage(damage)
@@ -202,80 +202,83 @@
/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob)
src.add_fingerprint(user)
- if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name())
- if(stat & BROKEN)
- user << "It looks like \the [src] is pretty busted. It's going to need more than just patching up now."
- return
- if(health >= maxhealth)
- user << "Nothing to fix!"
- return
- if(!density)
- user << "\The [src] must be closed before you can repair it."
- return
+ if(istype(I))
+ if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name())
+ if(stat & BROKEN)
+ user << "It looks like \the [src] is pretty busted. It's going to need more than just patching up now."
+ return
+ if(health >= maxhealth)
+ user << "Nothing to fix!"
+ return
+ if(!density)
+ user << "\The [src] must be closed before you can repair it."
+ return
- //figure out how much metal we need
- var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT
- amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc?
+ //figure out how much metal we need
+ var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT
+ amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc?
- var/obj/item/stack/stack = I
- var/transfer
- if (repairing)
- transfer = stack.transfer_to(repairing, amount_needed - repairing.amount)
- if (!transfer)
- user << "You must weld or remove \the [repairing] from \the [src] before you can add anything else."
- else
- repairing = stack.split(amount_needed)
+ var/obj/item/stack/stack = I
+ var/transfer
if (repairing)
- repairing.loc = src
- transfer = repairing.amount
+ transfer = stack.transfer_to(repairing, amount_needed - repairing.amount)
+ if (!transfer)
+ user << "You must weld or remove \the [repairing] from \the [src] before you can add anything else."
+ else
+ repairing = stack.split(amount_needed)
+ if (repairing)
+ repairing.loc = src
+ transfer = repairing.amount
- if (transfer)
- user << "You fit [transfer] [stack.singular_name]\s to damaged and broken parts on \the [src]."
+ if (transfer)
+ user << "You fit [transfer] [stack.singular_name]\s to damaged and broken parts on \the [src]."
- return
-
- if(repairing && istype(I, /obj/item/weapon/weldingtool))
- if(!density)
- user << "\The [src] must be closed before you can repair it."
return
- var/obj/item/weapon/weldingtool/welder = I
- if(welder.remove_fuel(0,user))
- user << "You start to fix dents and weld \the [repairing] into place."
- playsound(src, welder.usesound, 50, 1)
- if(do_after(user, (5 * repairing.amount) * welder.toolspeed) && welder && welder.isOn())
- user << "You finish repairing the damage to \the [src]."
- health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth)
- update_icon()
- qdel(repairing)
- repairing = null
+ if(repairing && istype(I, /obj/item/weapon/weldingtool))
+ if(!density)
+ user << "\The [src] must be closed before you can repair it."
+ return
+
+ var/obj/item/weapon/weldingtool/welder = I
+ if(welder.remove_fuel(0,user))
+ user << "You start to fix dents and weld \the [repairing] into place."
+ playsound(src, welder.usesound, 50, 1)
+ if(do_after(user, (5 * repairing.amount) * welder.toolspeed) && welder && welder.isOn())
+ user << "You finish repairing the damage to \the [src]."
+ health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth)
+ update_icon()
+ qdel(repairing)
+ repairing = null
+ return
+
+ if(repairing && I.is_crowbar())
+ user << "You remove \the [repairing]."
+ playsound(src, I.usesound, 100, 1)
+ repairing.loc = user.loc
+ repairing = null
+ return
+
+ //psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them.
+ if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card))
+ var/obj/item/weapon/W = I
+ user.setClickCooldown(user.get_attack_speed(W))
+ if(W.damtype == BRUTE || W.damtype == BURN)
+ user.do_attack_animation(src)
+ if(W.force < min_force)
+ user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.")
+ else
+ user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!")
+ playsound(src.loc, hitsound, 100, 1)
+ take_damage(W.force)
+ return
+
+ if(src.operating > 0 || isrobot(user))
+ return //borgs can't attack doors open because it conflicts with their AI-like interaction with them.
+
+ if(src.operating)
return
- if(repairing && I.is_crowbar())
- user << "You remove \the [repairing]."
- playsound(src, I.usesound, 100, 1)
- repairing.loc = user.loc
- repairing = null
- return
-
- //psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them.
- if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card))
- var/obj/item/weapon/W = I
- user.setClickCooldown(user.get_attack_speed(W))
- if(W.damtype == BRUTE || W.damtype == BURN)
- user.do_attack_animation(src)
- if(W.force < min_force)
- user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.")
- else
- user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!")
- playsound(src.loc, hitsound, 100, 1)
- take_damage(W.force)
- return
-
- if(src.operating > 0 || isrobot(user)) return //borgs can't attack doors open because it conflicts with their AI-like interaction with them.
-
- if(src.operating) return
-
if(src.allowed(user) && operable())
if(src.density)
open()
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index fb7609ee05..ac5246d2da 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -216,22 +216,26 @@
return
..()
-/obj/machinery/door/firedoor/attack_generic(var/mob/user, var/damage)
+/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage)
if(stat & (BROKEN|NOPOWER))
- if(damage >= 10)
+ if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
var/time_to_force = (2 + (2 * blocked)) * 5
if(src.density)
visible_message("\The [user] starts forcing \the [src] open!")
+ user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything.
if(do_after(user, time_to_force, src))
visible_message("\The [user] forces \the [src] open!")
src.blocked = 0
open(1)
+ user.set_AI_busy(FALSE)
else
time_to_force = (time_to_force / 2)
visible_message("\The [user] starts forcing \the [src] closed!")
+ user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything.
if(do_after(user, time_to_force, src))
visible_message("\The [user] forces \the [src] closed!")
close(1)
+ user.set_AI_busy(FALSE)
else
visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].")
return
diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm
index 6bf5fe0553..11893fa5ca 100644
--- a/code/game/machinery/embedded_controller/airlock_controllers.dm
+++ b/code/game/machinery/embedded_controller/airlock_controllers.dm
@@ -13,7 +13,7 @@
var/tag_secure = 0
var/cycle_to_external_air = 0
-/obj/machinery/embedded_controller/radio/airlock/initialize()
+/obj/machinery/embedded_controller/radio/airlock/Initialize()
. = ..()
program = new/datum/computer/file/embedded_program/airlock(src)
diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller.dm b/code/game/machinery/embedded_controller/airlock_docking_controller.dm
index 86885ebd53..fa7d5dfa04 100644
--- a/code/game/machinery/embedded_controller/airlock_docking_controller.dm
+++ b/code/game/machinery/embedded_controller/airlock_docking_controller.dm
@@ -5,7 +5,7 @@
var/datum/computer/file/embedded_program/docking/airlock/docking_program
tag_secure = 1
-/obj/machinery/embedded_controller/radio/airlock/docking_port/initialize()
+/obj/machinery/embedded_controller/radio/airlock/docking_port/Initialize()
. = ..()
airlock_program = new/datum/computer/file/embedded_program/airlock/docking(src)
docking_program = new/datum/computer/file/embedded_program/docking/airlock(src, airlock_program)
diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm
index 7c35568e51..bdce39de14 100644
--- a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm
+++ b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm
@@ -9,7 +9,7 @@
var/datum/computer/file/embedded_program/docking/multi/docking_program
-/obj/machinery/embedded_controller/radio/docking_port_multi/initialize()
+/obj/machinery/embedded_controller/radio/docking_port_multi/Initialize()
. = ..()
docking_program = new/datum/computer/file/embedded_program/docking/multi(src)
program = docking_program
@@ -55,7 +55,7 @@
var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program
tag_secure = 1
-/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/initialize()
+/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Initialize()
. = ..()
airlock_program = new/datum/computer/file/embedded_program/airlock/multi_docking(src)
program = airlock_program
diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm
index 6833498b5f..17bf13181a 100644
--- a/code/game/machinery/embedded_controller/embedded_controller_base.dm
+++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm
@@ -57,7 +57,7 @@ obj/machinery/embedded_controller/radio/Destroy()
var/datum/radio_frequency/radio_connection
unacidable = 1
-/obj/machinery/embedded_controller/radio/initialize()
+/obj/machinery/embedded_controller/radio/Initialize()
. = ..()
set_frequency(frequency)
diff --git a/code/game/machinery/embedded_controller/simple_docking_controller.dm b/code/game/machinery/embedded_controller/simple_docking_controller.dm
index 9242afd762..8e5c941bbf 100644
--- a/code/game/machinery/embedded_controller/simple_docking_controller.dm
+++ b/code/game/machinery/embedded_controller/simple_docking_controller.dm
@@ -4,7 +4,7 @@
var/tag_door
var/datum/computer/file/embedded_program/docking/simple/docking_program
-/obj/machinery/embedded_controller/radio/simple_docking_controller/initialize()
+/obj/machinery/embedded_controller/radio/simple_docking_controller/Initialize()
. = ..()
docking_program = new/datum/computer/file/embedded_program/docking/simple(src)
program = docking_program
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index c103f1f39e..0729121195 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -112,9 +112,13 @@ Class Procs:
var/uid
var/panel_open = 0
var/global/gl_uid = 1
+ var/clicksound // sound played on succesful interface. Just put it in the list of vars at the start.
+ var/clickvol = 40 // volume
var/interact_offline = 0 // Can the machine be interacted with while de-powered.
var/obj/item/weapon/circuitboard/circuit = null
+ var/speed_process = FALSE //If false, SSmachines. If true, SSfastprocess.
+
/obj/machinery/New(l, d=0)
..(l)
if(d)
@@ -122,13 +126,19 @@ Class Procs:
if(circuit)
circuit = new circuit(src)
-/obj/machinery/initialize()
+/obj/machinery/Initialize()
. = ..()
global.machines += src
- START_MACHINE_PROCESSING(src)
+ if(!speed_process)
+ START_MACHINE_PROCESSING(src)
+ else
+ START_PROCESSING(SSfastprocess, src)
/obj/machinery/Destroy()
- STOP_MACHINE_PROCESSING(src)
+ if(!speed_process)
+ STOP_MACHINE_PROCESSING(src)
+ else
+ STOP_PROCESSING(SSfastprocess, src)
global.machines -= src
if(component_parts)
for(var/atom/A in component_parts)
@@ -151,8 +161,6 @@ Class Procs:
if(!(use_power || idle_power_usage || active_power_usage))
return PROCESS_KILL
- return
-
/obj/machinery/emp_act(severity)
if(use_power && stat == 0)
use_power(7500/severity)
@@ -228,6 +236,7 @@ Class Procs:
return attack_hand(user)
/obj/machinery/attack_hand(mob/user as mob)
+
if(inoperable(MAINT))
return 1
if(user.lying || user.stat)
@@ -244,6 +253,9 @@ Class Procs:
to_chat(user, "You momentarily forget how to use [src].")
return 1
+ if(clicksound && istype(user, /mob/living/carbon))
+ playsound(src, clicksound, clickvol)
+
add_fingerprint(user)
return ..()
diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm
index fd6a1048c7..0720b03b26 100644
--- a/code/game/machinery/oxygen_pump.dm
+++ b/code/game/machinery/oxygen_pump.dm
@@ -22,7 +22,7 @@
idle_power_usage = 10
active_power_usage = 120 // No idea what the realistic amount would be.
-/obj/machinery/oxygen_pump/initialize()
+/obj/machinery/oxygen_pump/Initialize()
. = ..()
tank = new spawn_type (src)
contained = new mask_type (src)
@@ -33,7 +33,7 @@
if(breather.internals)
breather.internals.icon_state = "internal0"
breather.remove_from_mob(contained)
- visible_message("The mask rapidly retracts just before /the [src] is destroyed!")
+ visible_message("\The [contained] rapidly retracts just before /the [src] is destroyed!")
breather = null
QDEL_NULL(tank)
@@ -45,11 +45,11 @@
if(istype(target) && CanMouseDrop(target))
if(!can_apply_to_target(target, usr)) // There is no point in attempting to apply a mask if it's impossible.
return
- usr.visible_message("\The [usr] begins placing the mask onto [target]..")
+ usr.visible_message("\The [usr] begins placing \the [contained] onto [target].")
if(!do_mob(usr, target, 25) || !can_apply_to_target(target, usr))
return
// place mask and add fingerprints
- usr.visible_message("\The [usr] has placed \the mask on [target]'s mouth.")
+ usr.visible_message("\The [usr] has placed \the [contained] on [target]'s mouth.")
attach_mask(target)
src.add_fingerprint(usr)
@@ -69,7 +69,7 @@
tank.forceMove(src)
breather.remove_from_mob(contained)
contained.forceMove(src)
- src.visible_message("\The [user] makes \The [contained] rapidly retracts back into \the [src]!")
+ src.visible_message("\The [user] makes \the [contained] rapidly retract back into \the [src]!")
if(breather.internals)
breather.internals.icon_state = "internal0"
breather = null
@@ -112,7 +112,7 @@
to_chat(user, "There is no tank in \the [src].")
return
if(stat & MAINT)
- to_chat(user, "Please close \the maintenance hatch first.")
+ to_chat(user, "Please close the maintenance hatch first.")
return
if(!Adjacent(target))
to_chat(user, "Please stay close to \the [src].")
@@ -123,18 +123,15 @@
return
//Checking if breather is still valid
if(target == breather && target.wear_mask != contained)
- to_chat(user, "\The [target] is not using the supplied mask.")
+ to_chat(user, "\The [target] is not using the supplied [contained].")
return
return 1
/obj/machinery/oxygen_pump/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_screwdriver())
stat ^= MAINT
- user.visible_message("\The [user] [stat & MAINT ? "opens" : "closes"] \the [src].", "You [stat & MAINT ? "open" : "close"] \the [src].")
- if(stat & MAINT)
- icon_state = icon_state_open
- if(!stat)
- icon_state = icon_state_closed
+ user.visible_message("\The [user] [(stat & MAINT) ? "opens" : "closes"] \the [src].", "You [(stat & MAINT) ? "open" : "close"] \the [src].")
+ icon_state = (stat & MAINT) ? icon_state_open : icon_state_closed
//TO-DO: Open icon
if(istype(W, /obj/item/weapon/tank) && (stat & MAINT))
if(tank)
@@ -151,7 +148,7 @@
/obj/machinery/oxygen_pump/examine(var/mob/user)
. = ..()
if(tank)
- to_chat(user, "The meter shows [round(tank.air_contents.return_pressure())]")
+ to_chat(user, "The meter shows [round(tank.air_contents.return_pressure())] kPa.")
else
to_chat(user, "It is missing a tank!")
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index 0482e2f242..eb58a49959 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -38,7 +38,7 @@ Buildable meters
* @param loc Location
* @pipe_type
*/
-/obj/item/pipe/initialize(var/mapload, var/_pipe_type, var/_dir, var/obj/machinery/atmospherics/make_from)
+/obj/item/pipe/Initialize(var/mapload, var/_pipe_type, var/_dir, var/obj/machinery/atmospherics/make_from)
if(make_from)
make_from_existing(make_from)
else
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index b33e5584f3..3ee9a72b82 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -406,16 +406,16 @@ var/list/turret_icons
attacked = 0
..()
-/obj/machinery/porta_turret/attack_generic(mob/user as mob, var/damage)
- if(isanimal(user))
- var/mob/living/simple_animal/S = user
- if(damage >= 10)
+/obj/machinery/porta_turret/attack_generic(mob/living/L, damage)
+ if(isanimal(L))
+ var/mob/living/simple_mob/S = L
+ if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly.
visible_message("\The [S] [pick(S.attacktext)] \the [src]!")
take_damage(incoming_damage)
S.do_attack_animation(src)
return 1
- visible_message("\The [user] bonks \the [src]'s casing!")
+ visible_message("\The [L] bonks \the [src]'s casing!")
return ..()
/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user)
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 4c447e91f3..fa808ea051 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -132,7 +132,7 @@
if("cellremove")
if(panel_open && cell && !usr.get_active_hand())
- usr.visible_message("\The usr] removes \the [cell] from \the [src].", "You remove \the [cell] from \the [src].")
+ usr.visible_message("\The [usr] removes \the [cell] from \the [src].", "You remove \the [cell] from \the [src].")
cell.update_icon()
usr.put_in_hands(cell)
cell.add_fingerprint(usr)
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index 9a02fddfd2..acf90a0b4d 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -60,7 +60,7 @@
return
// register for radio system
-/obj/machinery/status_display/initialize()
+/obj/machinery/status_display/Initialize()
. = ..()
if(radio_controller)
radio_controller.add_object(src, frequency)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 5cb6b0ba06..de35da8a52 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -643,6 +643,20 @@
departments = list("Mercenary", "Charring")
can_repair = 1
+/obj/machinery/suit_cycler/exploration
+ name = "Explorer suit cycler"
+ model_text = "Exploration"
+ departments = list("Exploration","Old Exploration")
+
+/obj/machinery/suit_cycler/exploreration/Initialize()
+ species -= SPECIES_TESHARI
+ return ..()
+
+/obj/machinery/suit_cycler/pilot
+ name = "Pilot suit cycler"
+ model_text = "Pilot"
+ departments = list("Pilot Blue","Pilot")
+
/obj/machinery/suit_cycler/attack_ai(mob/user as mob)
return attack_hand(user)
@@ -1106,6 +1120,51 @@
suit.icon_state = "rig-firebug"
suit.item_state_slots[slot_r_hand_str] = "rig-firebug"
suit.item_state_slots[slot_l_hand_str] = "rig-firebug"
+ if("Exploration")
+ if(helmet)
+ helmet.name = "exploration voidsuit helmet"
+ helmet.icon_state = "helm_explorer"
+ helmet.item_state = "helm_explorer"
+ if(suit)
+ suit.name = "exploration voidsuit"
+ suit.icon_state = "void_explorer"
+ suit.item_state = "void_explorer"
+ suit.item_state_slots[slot_r_hand_str] = "wiz_voidsuit"
+ suit.item_state_slots[slot_l_hand_str] = "wiz_voidsuit"
+ if("Old Exploration")
+ if(helmet)
+ helmet.name = "exploration voidsuit helmet"
+ helmet.icon_state = "helm_explorer2"
+ helmet.item_state = "helm_explorer2"
+ if(suit)
+ suit.name = "exploration voidsuit"
+ suit.icon_state = "void_explorer2"
+ suit.item_state = "void_explorer2"
+ suit.item_state_slots[slot_r_hand_str] = "wiz_voidsuit"
+ suit.item_state_slots[slot_l_hand_str] = "wiz_voidsuit"
+ if("Pilot")
+ if(helmet)
+ helmet.name = "pilot voidsuit helmet"
+ helmet.icon_state = "rig0_pilot"
+ helmet.item_state = "pilot_helm"
+ if(suit)
+ suit.name = "pilot voidsuit"
+ suit.icon_state = "rig-pilot"
+ suit.item_state = "rig-pilot"
+ suit.item_state_slots[slot_r_hand_str] = "sec_voidsuitTG"
+ suit.item_state_slots[slot_l_hand_str] = "sec_voidsuitTG"
+ if("Pilot Blue")
+ if(helmet)
+ helmet.name = "pilot voidsuit helmet"
+ helmet.icon_state = "rig0_pilot2"
+ helmet.item_state = "pilot_helm2"
+ if(suit)
+ suit.name = "pilot voidsuit"
+ suit.icon_state = "rig-pilot2"
+ suit.item_state = "rig-pilot2"
+ suit.item_state_slots[slot_r_hand_str] = "sec_voidsuitTG"
+ suit.item_state_slots[slot_l_hand_str] = "sec_voidsuitTG"
+
if(helmet) helmet.name = "refitted [helmet.name]"
if(suit) suit.name = "refitted [suit.name]"
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index 8f576b5d00..5abd57e8cf 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -122,7 +122,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
var/turf/position = get_turf(src)
listening_level = position.z
-/obj/machinery/telecomms/initialize()
+/obj/machinery/telecomms/Initialize()
if(autolinkers.len)
// Links nearby machines
if(!long_range_link)
@@ -317,7 +317,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
netspeed = 40
var/list/telecomms_map
-/obj/machinery/telecomms/hub/initialize()
+/obj/machinery/telecomms/hub/Initialize()
. = ..()
LAZYINITLIST(telecomms_map)
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index be31a5aa56..1d3556b45e 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -17,7 +17,7 @@
underlays += image('icons/obj/stationobjs.dmi', icon_state = "telecomp-wires")
return
-/obj/machinery/computer/teleporter/initialize()
+/obj/machinery/computer/teleporter/Initialize()
. = ..()
var/obj/machinery/teleport/station/station
var/obj/machinery/teleport/hub/hub
@@ -69,7 +69,7 @@
for(var/obj/machinery/teleport/hub/H in range(1))
var/amount = rand(2,5)
for(var/i=0;iYou enable leg actuators overload.")
src.log_message("Toggled leg actuators overload.")
+ playsound(src, 'sound/mecha/mechanical_toggle.ogg', 50, 1)
return
/obj/mecha/combat/gygax/dyndomove(direction)
diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm
index 3236600309..27bdf905a9 100644
--- a/code/game/mecha/combat/marauder.dm
+++ b/code/game/mecha/combat/marauder.dm
@@ -22,6 +22,7 @@
internal_damage_threshold = 25
force = 45
max_equip = 4
+ mech_faction = MECH_FACTION_NT
max_hull_equip = 3
max_weapon_equip = 3
@@ -42,6 +43,7 @@
force = 55
max_equip = 5
+
/obj/mecha/combat/marauder/mauler
desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model."
name = "Mauler"
@@ -49,6 +51,7 @@
initial_icon = "mauler"
operation_req_access = list(access_syndicate)
wreckage = /obj/effect/decal/mecha_wreckage/mauler
+ mech_faction = MECH_FACTION_SYNDI
/obj/mecha/combat/marauder/New()
..()
diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm
index fec8c18e1d..825dfde5f5 100644
--- a/code/game/mecha/equipment/mecha_equipment.dm
+++ b/code/game/mecha/equipment/mecha_equipment.dm
@@ -20,10 +20,13 @@
var/required_type = /obj/mecha //may be either a type or a list of allowed types
var/equip_type = null //mechaequip2
var/allow_duplicate = FALSE
+ var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed.
/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1)
sleep(equip_cooldown)
set_ready_state(1)
+ if(ready_sound) //Kind of like the kinetic accelerator.
+ playsound(loc, ready_sound, 50, 1, -1)
if(target && chassis)
return 1
return 0
@@ -69,10 +72,22 @@
src.update_chassis_page()
chassis.occupant_message("The [src] is destroyed!")
chassis.log_append_to_last("[src] is destroyed.",1)
- if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))
- chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50)
- else
- chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50)
+ if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))//Gun
+ switch(chassis.mech_faction)
+ if(MECH_FACTION_NT)
+ src.chassis.occupant << sound('sound/mecha/weapdestrnano.ogg',volume=70)
+ if(MECH_FACTION_SYNDI)
+ src.chassis.occupant << sound('sound/mecha/weapdestrsyndi.ogg',volume=60)
+ else
+ src.chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50)
+ else //Not a gun
+ switch(chassis.mech_faction)
+ if(MECH_FACTION_NT)
+ src.chassis.occupant << sound('sound/mecha/critdestrnano.ogg',volume=70)
+ if(MECH_FACTION_SYNDI)
+ src.chassis.occupant << sound('sound/mecha/critdestrsyndi.ogg',volume=70)
+ else
+ src.chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50)
spawn
qdel(src)
return
diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm
index 92a250ddfa..dcca44fe68 100644
--- a/code/game/mecha/equipment/tools/medical_tools.dm
+++ b/code/game/mecha/equipment/tools/medical_tools.dm
@@ -40,10 +40,9 @@
if(occupant)
occupant_message("The sleeper is already occupied")
return
- for(var/mob/living/simple_animal/slime/M in range(1,target))
- if(M.victim == target)
- occupant_message("[target] will not fit into the sleeper because they have a slime latched onto their head.")
- return
+ if(target.has_buckled_mobs())
+ occupant_message(span("warning", "\The [target] has other entities attached to it. Remove them first."))
+ return
occupant_message("You start putting [target] into [src].")
chassis.visible_message("[chassis] starts putting [target] into the [src].")
var/C = chassis.loc
diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm
index dd4472282c..cf54e0fc3c 100644
--- a/code/game/mecha/equipment/tools/tools.dm
+++ b/code/game/mecha/equipment/tools/tools.dm
@@ -128,7 +128,7 @@
desc = "This is an upgraded version of the drill that'll pierce the heavens! (Can be attached to: Combat and Engineering Exosuits)"
icon_state = "mecha_diamond_drill"
origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3)
- equip_cooldown = 20
+ equip_cooldown = 10
force = 15
/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/action(atom/target)
@@ -244,100 +244,43 @@
equip_cooldown = 10
energy_drain = 250
range = MELEE|RANGED
- var/mode = 0 //0 - deconstruct, 1 - wall or floor, 2 - airlock.
- var/disabled = 0 //malf
-
equip_type = EQUIP_SPECIAL
+ var/obj/item/weapon/rcd/electric/mounted/mecha/my_rcd = null
+
+/obj/item/mecha_parts/mecha_equipment/tool/rcd/Initialize()
+ my_rcd = new(src)
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/tool/rcd/Destroy()
+ QDEL_NULL(my_rcd)
+ return ..()
/obj/item/mecha_parts/mecha_equipment/tool/rcd/action(atom/target)
- if(istype(target,/area/shuttle)||istype(target, /turf/space/transit))//>implying these are ever made -Sieve
- disabled = 1
- else
- disabled = 0
- if(!istype(target, /turf) && !istype(target, /obj/machinery/door/airlock))
- target = get_turf(target)
- if(!action_checks(target) || disabled || get_dist(chassis, target)>3) return
- playsound(chassis, 'sound/machines/click.ogg', 50, 1)
- //meh
- switch(mode)
- if(0)
- if (istype(target, /turf/simulated/wall))
- occupant_message("Deconstructing [target]...")
- set_ready_state(0)
- if(do_after_cooldown(target))
- if(disabled) return
- chassis.spark_system.start()
- target:ChangeTurf(/turf/simulated/floor/plating)
- playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
- chassis.use_power(energy_drain)
- else if (istype(target, /turf/simulated/floor))
- occupant_message("Deconstructing [target]...")
- set_ready_state(0)
- if(do_after_cooldown(target))
- if(disabled) return
- chassis.spark_system.start()
- target:ChangeTurf(get_base_turf_by_area(target))
- playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
- chassis.use_power(energy_drain)
- else if (istype(target, /obj/machinery/door/airlock))
- occupant_message("Deconstructing [target]...")
- set_ready_state(0)
- if(do_after_cooldown(target))
- if(disabled) return
- chassis.spark_system.start()
- qdel(target)
- playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
- chassis.use_power(energy_drain)
- if(1)
- if(istype(target, /turf/space) || istype(target,get_base_turf_by_area(target)))
- occupant_message("Building Floor...")
- set_ready_state(0)
- if(do_after_cooldown(target))
- if(disabled) return
- target:ChangeTurf(/turf/simulated/floor/plating)
- playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
- chassis.spark_system.start()
- chassis.use_power(energy_drain*2)
- else if(istype(target, /turf/simulated/floor))
- occupant_message("Building Wall...")
- set_ready_state(0)
- if(do_after_cooldown(target))
- if(disabled) return
- target:ChangeTurf(/turf/simulated/wall)
- playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
- chassis.spark_system.start()
- chassis.use_power(energy_drain*2)
- if(2)
- if(istype(target, /turf/simulated/floor))
- occupant_message("Building Airlock...")
- set_ready_state(0)
- if(do_after_cooldown(target))
- if(disabled) return
- chassis.spark_system.start()
- var/obj/machinery/door/airlock/T = new /obj/machinery/door/airlock(target)
- T.autoclose = 1
- playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
- playsound(target, 'sound/effects/sparks2.ogg', 50, 1)
- chassis.use_power(energy_drain*2)
- return
+ if(!action_checks(target) || get_dist(chassis, target) > 3)
+ return FALSE
+
+ my_rcd.use_rcd(target, chassis.occupant)
/obj/item/mecha_parts/mecha_equipment/tool/rcd/Topic(href,href_list)
..()
if(href_list["mode"])
- mode = text2num(href_list["mode"])
- switch(mode)
- if(0)
- occupant_message("Switched RCD to Deconstruct.")
- if(1)
- occupant_message("Switched RCD to Construct.")
- if(2)
- occupant_message("Switched RCD to Construct Airlock.")
- return
-
+ my_rcd.mode_index = text2num(href_list["mode"])
+ occupant_message("RCD reconfigured to '[my_rcd.modes[my_rcd.mode_index]]'.")
+/*
/obj/item/mecha_parts/mecha_equipment/tool/rcd/get_equip_info()
return "[..()] \[D|C|A\]"
+*/
+/obj/item/mecha_parts/mecha_equipment/tool/rcd/get_equip_info()
+ var/list/content = list(..()) // This is all for one line, in the interest of string tree conservation.
+ var/i = 1
+ content += "
"
+ for(var/mode in my_rcd.modes)
+ content += " [mode]"
+ if(i < my_rcd.modes.len)
+ content += "
"
+ i++
-
+ return content.Join()
/obj/item/mecha_parts/mecha_equipment/teleporter
@@ -1260,9 +1203,10 @@
usr << "Kinda hard to climb in while handcuffed don't you think?"
return
- for(var/mob/living/simple_animal/slime/M in range(1,usr))
- if(M.victim == usr)
- usr << "You're too busy getting your life sucked out of you."
+ if(isliving(usr))
+ var/mob/living/L = usr
+ if(L.has_buckled_mobs())
+ to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
return
//search for a valid passenger compartment
@@ -1386,4 +1330,4 @@
/obj/item/mecha_parts/mecha_equipment/tool/jetpack/do_after_cooldown()
sleep(equip_cooldown)
wait = 0
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 4d2d26e34c..1916ff3611 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -38,7 +38,7 @@
files = new /datum/research(src) //Setup the research data holder.
return
-/obj/machinery/mecha_part_fabricator/initialize()
+/obj/machinery/mecha_part_fabricator/Initialize()
update_categories()
. = ..()
diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm
index ca8aabf769..89ca7dca84 100644
--- a/code/game/mecha/mech_prosthetics.dm
+++ b/code/game/mecha/mech_prosthetics.dm
@@ -39,7 +39,7 @@
files = new /datum/research(src) //Setup the research data holder.
return
-/obj/machinery/pros_fabricator/initialize()
+/obj/machinery/pros_fabricator/Initialize()
. = ..()
manufacturer = basic_robolimb.company
update_categories()
diff --git a/code/game/mecha/mech_sensor.dm b/code/game/mecha/mech_sensor.dm
index 5173f182ce..3ac0361e48 100644
--- a/code/game/mecha/mech_sensor.dm
+++ b/code/game/mecha/mech_sensor.dm
@@ -45,8 +45,8 @@
var/obj/mecha/R = O
if(R && R.occupant)
R.occupant << block_message
- else if(istype(O, /obj/vehicle/train/cargo/engine))
- var/obj/vehicle/train/cargo/engine/E = O
+ else if(istype(O, /obj/vehicle/train/engine))
+ var/obj/vehicle/train/engine/E = O
if(E && E.load && E.is_train_head())
E.load << block_message
@@ -69,7 +69,7 @@
else
icon_state = "airlock_sensor_off"
-/obj/machinery/mech_sensor/initialize()
+/obj/machinery/mech_sensor/Initialize()
. = ..()
set_frequency(frequency)
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 394c0fde9e..9b17cb8613 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -7,6 +7,11 @@
#define MELEE 1
#define RANGED 2
+
+#define MECH_FACTION_NT "nano"
+#define MECH_FACTION_SYNDI "syndi"
+#define MECH_FACTION_NONE "none"
+
/obj/mecha
name = "Mecha"
desc = "Exosuit"
@@ -41,6 +46,10 @@
var/lights_power = 6
var/force = 0
+ var/mech_faction = null
+ var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once.
+
+
//inner atmos
var/use_internal_tank = 0
var/internal_tank_valve = ONE_ATMOSPHERE
@@ -500,7 +509,7 @@
internal_damage |= int_dam_flag
pr_internal_damage.start()
log_append_to_last("Internal damage of type [int_dam_flag].",1)
- occupant << sound('sound/machines/warning-buzzer.ogg',wait=0)
+ occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding.
return
/obj/mecha/proc/clearInternalDamage(int_dam_flag)
@@ -1008,6 +1017,7 @@
if(network && !(internal_tank.return_air() in network.gases))
network.gases += internal_tank.return_air()
network.update = 1
+ playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1)
log_message("Connected to gas port.")
return 1
@@ -1021,6 +1031,7 @@
connected_port.connected_device = null
connected_port = null
+ playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1)
src.log_message("Disconnected from gas port.")
return 1
@@ -1078,6 +1089,7 @@
else set_light(light_range - lights_power)
src.occupant_message("Toggled lights [lights?"on":"off"].")
log_message("Toggled lights [lights?"on":"off"].")
+ playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1)
return
@@ -1142,10 +1154,12 @@
to_chat(usr,"Access denied")
src.log_append_to_last("Permission denied.")
return
- for(var/mob/living/simple_animal/slime/M in range(1,usr))
- if(M.victim == usr)
- to_chat(usr,"You're too busy getting your life sucked out of you.")
+ if(isliving(usr))
+ var/mob/living/L = usr
+ if(L.has_buckled_mobs())
+ to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
return
+
// usr << "You start climbing into [src.name]"
visible_message("\The [usr] starts to climb into [src.name]")
@@ -1176,8 +1190,22 @@
src.icon_state = src.reset_icon()
set_dir(dir_in)
playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
- if(!hasInternalDamage())
- src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
+ if(!hasInternalDamage()) //Otherwise it's not nominal!
+ switch(mech_faction)
+ if(MECH_FACTION_NT)//The good guys category
+ if(firstactivation)//First time = long activation sound
+ firstactivation = 1
+ src.occupant << sound('sound/mecha/LongNanoActivation.ogg',volume=50)
+ else
+ src.occupant << sound('sound/mecha/nominalnano.ogg',volume=50)
+ if(MECH_FACTION_SYNDI)//Bad guys
+ if(firstactivation)
+ firstactivation = 1
+ src.occupant << sound('sound/mecha/LongSyndiActivation.ogg',volume=50)
+ else
+ src.occupant << sound('sound/mecha/nominalsyndi.ogg',volume=50)
+ else//Everyone else gets the normal noise
+ src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
return 1
else
return 0
diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm
index 73cee1223b..bc20f25b2e 100644
--- a/code/game/mecha/mecha_wreckage.dm
+++ b/code/game/mecha/mecha_wreckage.dm
@@ -99,6 +99,14 @@
name = "Dark Gygax wreckage"
icon_state = "darkgygax-broken"
+/obj/effect/decal/mecha_wreckage/gygax/adv
+ name = "Advanced Dark Gygax wreckage"
+ icon_state = "darkgygax_adv-broken"
+
+/obj/effect/decal/mecha_wreckage/gygax/medgax
+ name = "Medgax wreckage"
+ icon_state = "medgax-broken"
+
/obj/effect/decal/mecha_wreckage/marauder
name = "Marauder wreckage"
icon_state = "marauder-broken"
@@ -198,6 +206,9 @@
parts -= part
return
+/obj/effect/decal/mecha_wreckage/odysseus/murdysseus
+ icon_state = "murdysseus-broken"
+
/obj/effect/decal/mecha_wreckage/hoverpod
name = "Hover pod wreckage"
icon_state = "engineering_pod-broken"
diff --git a/code/game/mecha/medical/medical.dm b/code/game/mecha/medical/medical.dm
index adb75800ea..90ab62681a 100644
--- a/code/game/mecha/medical/medical.dm
+++ b/code/game/mecha/medical/medical.dm
@@ -7,7 +7,7 @@
cargo_capacity = 1
-/obj/mecha/medical/initialize()
+/obj/mecha/medical/Initialize()
. = ..()
var/turf/T = get_turf(src)
if(isPlayerLevel(T.z))
diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm
index 8a1242d1a2..f7e886dd62 100644
--- a/code/game/mecha/medical/odysseus.dm
+++ b/code/game/mecha/medical/odysseus.dm
@@ -109,7 +109,7 @@
else if(foundVirus)
holder.icon_state = "hudill"
else if(patient.has_brain_worms())
- var/mob/living/simple_animal/borer/B = patient.has_brain_worms()
+ var/mob/living/simple_mob/animal/borer/B = patient.has_brain_worms()
if(B.controlling)
holder.icon_state = "hudbrainworm"
else
diff --git a/code/game/mecha/working/working.dm b/code/game/mecha/working/working.dm
index dbdf68ce5b..f2149b1cdd 100644
--- a/code/game/mecha/working/working.dm
+++ b/code/game/mecha/working/working.dm
@@ -6,7 +6,7 @@
max_universal_equip = 1
max_special_equip = 1
-/obj/mecha/working/initialize()
+/obj/mecha/working/Initialize()
. = ..()
var/turf/T = get_turf(src)
if(isPlayerLevel(T.z))
diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm
index 436ebabcc9..43b42cbac9 100644
--- a/code/game/objects/effects/chem/chemsmoke.dm
+++ b/code/game/objects/effects/chem/chemsmoke.dm
@@ -130,9 +130,9 @@
var/offset = 0
var/points = round((radius * 2 * M_PI) / arcLength)
- var/angle = round(ToDegrees(arcLength / radius), 1)
+ var/angle = round(TODEGREES(arcLength / radius), 1)
- if(!IsInteger(radius))
+ if(!ISINTEGER(radius))
offset = 45 //degrees
for(var/j = 0, j < points, j++)
diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm
index 03f743db6b..4455f049ca 100644
--- a/code/game/objects/effects/chem/foam.dm
+++ b/code/game/objects/effects/chem/foam.dm
@@ -24,7 +24,7 @@
process()
checkReagents()
spawn(120)
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
sleep(30)
if(metal)
var/obj/structure/foamedmetal/M = new(src.loc)
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index 69d875d977..95f5e72faa 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -33,11 +33,11 @@ var/global/list/image/splatter_cache=list()
if(invisibility != 100)
invisibility = 100
amount = 0
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
..(ignore=1)
/obj/effect/decal/cleanable/blood/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/decal/cleanable/blood/New()
@@ -53,7 +53,7 @@ var/global/list/image/splatter_cache=list()
blood_DNA |= B.blood_DNA.Copy()
qdel(B)
drytime = world.time + DRYING_TIME * (amount+1)
- processing_objects += src
+ START_PROCESSING(SSobj, src)
/obj/effect/decal/cleanable/blood/process()
if(world.time > drytime)
@@ -115,7 +115,7 @@ var/global/list/image/splatter_cache=list()
desc = drydesc
color = adjust_brightness(color, -50)
amount = 0
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
/obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user)
..()
diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm
index 0d7370d269..c98cacb785 100644
--- a/code/game/objects/effects/decals/contraband.dm
+++ b/code/game/objects/effects/decals/contraband.dm
@@ -109,7 +109,7 @@
pixel_x = -32
pixel_y = 0
-/obj/structure/sign/poster/initialize()
+/obj/structure/sign/poster/Initialize()
. = ..()
if (poster_type)
var/path = text2path(poster_type)
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index e92268f4da..f21d6443fb 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -103,9 +103,9 @@ steam.start() -- spawns the effect
if (istype(T, /turf))
T.hotspot_expose(1000,100)
-/obj/effect/effect/sparks/initialize()
+/obj/effect/effect/sparks/Initialize()
. = ..()
- schedule_task_in(5 SECONDS, /proc/qdel, list(src))
+ QDEL_IN(src, 5 SECONDS)
/obj/effect/effect/sparks/Destroy()
var/turf/T = src.loc
@@ -225,15 +225,16 @@ steam.start() -- spawns the effect
/obj/effect/effect/smoke/bad/Move()
..()
- for(var/mob/living/carbon/M in get_turf(src))
- affect(M)
+ for(var/mob/living/L in get_turf(src))
+ affect(L)
-/obj/effect/effect/smoke/bad/affect(var/mob/living/carbon/M)
+/obj/effect/effect/smoke/bad/affect(var/mob/living/L)
if (!..())
return 0
- M.adjustOxyLoss(1)
- if(prob(25))
- M.emote("cough")
+ if(L.needs_to_breathe())
+ L.adjustOxyLoss(1)
+ if(prob(25))
+ L.emote("cough")
/* Not feasile until a later date
/obj/effect/effect/smoke/bad/Crossed(atom/movable/M as mob|obj)
@@ -251,6 +252,72 @@ steam.start() -- spawns the effect
projectiles -= proj
*/
+/////////////////////////////////////////////
+// 'Elemental' smoke
+/////////////////////////////////////////////
+
+/obj/effect/effect/smoke/elemental
+ name = "cloud"
+ desc = "A cloud of some kind that seems really generic and boring."
+ opacity = FALSE
+ var/strength = 5 // How much damage to do inside each affect()
+
+/obj/effect/effect/smoke/elemental/Initialize()
+ START_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/effect/effect/smoke/elemental/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/effect/effect/smoke/elemental/Move()
+ ..()
+ for(var/mob/living/L in range(1, src))
+ affect(L)
+
+/obj/effect/effect/smoke/elemental/process()
+ for(var/mob/living/L in range(1, src))
+ affect(L)
+
+
+/obj/effect/effect/smoke/elemental/fire
+ name = "burning cloud"
+ desc = "A cloud of something that is on fire."
+ color = "#FF9933"
+ light_color = "#FF0000"
+ light_range = 2
+ light_power = 5
+
+/obj/effect/effect/smoke/elemental/fire/affect(mob/living/L)
+ L.inflict_heat_damage(strength)
+ L.add_modifier(/datum/modifier/fire, 6 SECONDS) // Around 15 damage per stack.
+
+/obj/effect/effect/smoke/elemental/frost
+ name = "freezing cloud"
+ desc = "A cloud filled with brutally cold mist."
+ color = "#00CCFF"
+
+/obj/effect/effect/smoke/elemental/frost/affect(mob/living/L)
+ L.inflict_cold_damage(strength)
+
+/obj/effect/effect/smoke/elemental/shock
+ name = "charged cloud"
+ desc = "A cloud charged with electricity."
+ color = "#4D4D4D"
+
+/obj/effect/effect/smoke/elemental/shock/affect(mob/living/L)
+ L.inflict_shock_damage(strength)
+
+/obj/effect/effect/smoke/elemental/mist
+ name = "misty cloud"
+ desc = "A cloud filled with water vapor."
+ color = "#CCFFFF"
+ alpha = 128
+ strength = 1
+
+/obj/effect/effect/smoke/elemental/mist/affect(mob/living/L)
+ L.water_act(strength)
+
/////////////////////////////////////////////
// Smoke spread
/////////////////////////////////////////////
@@ -282,7 +349,8 @@ steam.start() -- spawns the effect
src.location = get_turf(holder)
var/obj/effect/effect/smoke/smoke = new smoke_type(src.location)
src.total_smoke++
- smoke.color = I
+ if(I)
+ smoke.color = I
var/direction = src.direction
if(!direction)
if(src.cardinals)
@@ -296,10 +364,21 @@ steam.start() -- spawns the effect
if (smoke) qdel(smoke)
src.total_smoke--
-
/datum/effect/effect/system/smoke_spread/bad
smoke_type = /obj/effect/effect/smoke/bad
+/datum/effect/effect/system/smoke_spread/fire
+ smoke_type = /obj/effect/effect/smoke/elemental/fire
+
+/datum/effect/effect/system/smoke_spread/frost
+ smoke_type = /obj/effect/effect/smoke/elemental/frost
+
+/datum/effect/effect/system/smoke_spread/shock
+ smoke_type = /obj/effect/effect/smoke/elemental/shock
+
+/datum/effect/effect/system/smoke_spread/mist
+ smoke_type = /obj/effect/effect/smoke/elemental/mist
+
/////////////////////////////////////////////
//////// Attach an Ion trail to any object, that spawns when it moves (like for the jetpack)
/// just pass in the object to attach it to in set_up
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 50230eba17..193d24d4ab 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -83,7 +83,7 @@
/obj/effect/landmark/proc/delete()
delete_me = 1
-/obj/effect/landmark/initialize()
+/obj/effect/landmark/Initialize()
. = ..()
if(delete_me)
return INITIALIZE_HINT_QDEL
diff --git a/code/game/objects/effects/map_effects/beam_point.dm b/code/game/objects/effects/map_effects/beam_point.dm
new file mode 100644
index 0000000000..524686874c
--- /dev/null
+++ b/code/game/objects/effects/map_effects/beam_point.dm
@@ -0,0 +1,192 @@
+GLOBAL_LIST_EMPTY(all_beam_points)
+
+// Creates and manages a beam attached to itself and another beam_point.
+// You can do cool things with these such as moving the beam_point to move the beam, turning them on and off on a timer, triggered by external input, and more.
+/obj/effect/map_effect/beam_point
+ name = "beam point"
+ icon_state = "beam_point"
+
+ // General variables.
+ var/list/my_beams = list() // Instances of beams. Deleting one will kill the beam.
+ var/id = "A" // Two beam_points must share the same ID to be connected to each other.
+ var/max_beams = 10 // How many concurrent beams to seperate beam_points to have at once. Set to zero to only act as targets for other beam_points.
+ var/seek_range = 7 // How far to look for an end beam_point when not having a beam. Defaults to screen height/width. Make sure this is below beam_max_distance.
+
+ // Controls how and when the beam is created.
+ var/make_beams_on_init = FALSE
+ var/use_timer = FALSE // Sadly not the /tg/ timers.
+ var/list/on_duration = list(2 SECONDS, 2 SECONDS, 2 SECONDS) // How long the beam should stay on for, if use_timer is true. Alternates between each duration in the list.
+ var/list/off_duration = list(3 SECONDS, 0.5 SECOND, 0.5 SECOND) // How long it should stay off for. List length is not needed to be the same as on_duration.
+ var/timer_on_index = 1 // Index to use for on_duration list.
+ var/timer_off_index = 1// Ditto, for off_duration list.
+ var/initial_delay = 0 // How long to wait before first turning on the beam, to sync beam times or create a specific pattern.
+ var/beam_creation_sound = null // Optional sound played when one or more beams are created.
+ var/beam_destruction_sound = null // Optional sound played when a beam is destroyed.
+
+ // Beam datum arguments.
+ var/beam_icon = 'icons/effects/beam.dmi' // Icon file to use for beam visuals.
+ var/beam_icon_state = "b_beam" // Icon state to use for visuals.
+ var/beam_time = INFINITY // How long the beam lasts. By default it will last forever until destroyed.
+ var/beam_max_distance = 10 // If the beam is farther than this, it will be destroyed. Make sure it's higher than seek_range.
+ var/beam_type = /obj/effect/ebeam // The type of beam. Default has no special properties. Some others may do things like hurt things touching it.
+ var/beam_sleep_time = 3 // How often the beam updates visually. Suggested to leave this alone, 3 is already fast.
+
+/obj/effect/map_effect/beam_point/Initialize()
+ GLOB.all_beam_points += src
+ if(make_beams_on_init)
+ create_beams()
+ if(use_timer)
+ spawn(initial_delay)
+ handle_beam_timer()
+ return ..()
+
+/obj/effect/map_effect/beam_point/Destroy()
+ destroy_all_beams()
+ use_timer = FALSE
+ GLOB.all_beam_points -= src
+ return ..()
+
+// This is the top level proc to make the magic happen.
+/obj/effect/map_effect/beam_point/proc/create_beams()
+ if(my_beams.len >= max_beams)
+ return
+ var/beams_to_fill = max_beams - my_beams.len
+ for(var/i = 1 to beams_to_fill)
+ var/obj/effect/map_effect/beam_point/point = seek_beam_point()
+ if(!point)
+ break // No more points could be found, no point checking repeatively.
+ build_beam(point)
+
+// Finds a suitable beam point.
+/obj/effect/map_effect/beam_point/proc/seek_beam_point()
+ for(var/obj/effect/map_effect/beam_point/point in GLOB.all_beam_points)
+ if(id != point.id)
+ continue // Not linked together by ID.
+ if(has_active_beam(point))
+ continue // Already got one.
+ if(point.z != src.z)
+ continue // Not on same z-level. get_dist() ignores z-levels by design according to docs.
+ if(get_dist(src, point) > seek_range)
+ continue // Too far.
+ return point
+
+// Checks if the two points have an active beam between them.
+// Used to make sure two points don't have more than one beam.
+/obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them)
+ // First, check our beams.
+ for(var/datum/beam/B in my_beams)
+ if(B.target == them)
+ return TRUE
+ if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not.
+ return TRUE
+
+ // Now check theirs, to see if they have a beam on us.
+ for(var/datum/beam/B in them.my_beams)
+ if(B.target == src)
+ return TRUE
+ if(B.origin == src) // Same story as above.
+ return TRUE
+
+ return FALSE
+
+/obj/effect/map_effect/beam_point/proc/build_beam(var/atom/beam_target)
+ if(!beam_target)
+ log_debug("[src] ([src.type] \[[x],[y],[z]\]) failed to build its beam due to not having a target.")
+ return FALSE
+
+ var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time)
+ my_beams += new_beam
+ if(beam_creation_sound)
+ playsound(src, beam_creation_sound, 70, 1)
+
+ return TRUE
+
+/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B)
+ if(!B)
+ log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam that does not exist.")
+ return FALSE
+
+ if(!(B in my_beams))
+ log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam it did not own.")
+ return FALSE
+
+ my_beams -= B
+ qdel(B)
+ if(beam_destruction_sound)
+ playsound(src, beam_destruction_sound, 70, 1)
+
+ return TRUE
+
+/obj/effect/map_effect/beam_point/proc/destroy_all_beams()
+ for(var/datum/beam/B in my_beams)
+ destroy_beam(B)
+ return TRUE
+
+// This code makes me sad.
+/obj/effect/map_effect/beam_point/proc/handle_beam_timer()
+ if(!use_timer || QDELETED(src))
+ return
+
+ if(my_beams.len) // Currently on.
+ destroy_all_beams()
+ color = "#FF0000"
+
+ timer_off_index++
+ if(timer_off_index > off_duration.len)
+ timer_off_index = 1
+
+ spawn(off_duration[timer_off_index])
+ .()
+
+ else // Currently off.
+ // If nobody's around, keep the beams off to avoid wasteful beam process(), if they have one.
+ if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk))
+ spawn(retry_delay)
+ .()
+ return
+
+ create_beams()
+ color = "#00FF00"
+
+ timer_on_index++
+ if(timer_on_index > on_duration.len)
+ timer_on_index = 1
+
+ spawn(on_duration[timer_on_index])
+ .()
+
+
+
+// Subtypes to use in maps and adminbuse.
+// Remember, beam_points ONLY connect to other beam_points with the same id variable.
+
+// Creates the beam when instantiated and stays on until told otherwise.
+/obj/effect/map_effect/beam_point/instant
+ make_beams_on_init = TRUE
+
+/obj/effect/map_effect/beam_point/instant/electric
+ beam_icon_state = "nzcrentrs_power"
+ beam_type = /obj/effect/ebeam/reactive/electric
+ beam_creation_sound = 'sound/effects/lightningshock.ogg'
+ beam_destruction_sound = "sparks"
+
+// Turns on and off on a timer.
+/obj/effect/map_effect/beam_point/timer
+ use_timer = TRUE
+
+// Shocks people who touch the beam while it's on. Flicks on and off on a specific pattern.
+/obj/effect/map_effect/beam_point/timer/electric
+ beam_icon_state = "nzcrentrs_power"
+ beam_type = /obj/effect/ebeam/reactive/electric
+ beam_creation_sound = 'sound/effects/lightningshock.ogg'
+ beam_destruction_sound = "sparks"
+ seek_range = 3
+
+// Is only a target for other beams to connect to.
+/obj/effect/map_effect/beam_point/end
+ max_beams = 0
+
+// Can only have one beam.
+/obj/effect/map_effect/beam_point/mono
+ make_beams_on_init = TRUE
+ max_beams = 1
diff --git a/code/game/objects/effects/map_effects/effect_emitter.dm b/code/game/objects/effects/map_effects/effect_emitter.dm
new file mode 100644
index 0000000000..441ba957dd
--- /dev/null
+++ b/code/game/objects/effects/map_effects/effect_emitter.dm
@@ -0,0 +1,78 @@
+// Creates effects like smoke clouds every so often.
+/obj/effect/map_effect/interval/effect_emitter
+ var/datum/effect/effect/system/effect_system = null
+ var/effect_system_type = null // Which effect system to attach.
+
+ var/effect_amount = 10 // How many effect objects to create on each interval. Note that there's a hard cap on certain effect_systems.
+ var/effect_cardinals_only = FALSE // If true, effects only move in cardinal directions.
+ var/effect_forced_dir = null // If set, effects emitted will always move in this direction.
+
+/obj/effect/map_effect/interval/effect_emitter/Initialize()
+ effect_system = new effect_system_type()
+ effect_system.attach(src)
+ configure_effects()
+ return ..()
+
+/obj/effect/map_effect/interval/effect_emitter/interval/Destroy()
+ QDEL_NULL(effect_system)
+ return ..()
+
+/obj/effect/map_effect/interval/effect_emitter/proc/configure_effects()
+ effect_system.set_up(effect_amount, effect_cardinals_only, usr.loc, effect_forced_dir)
+
+/obj/effect/map_effect/interval/effect_emitter/trigger()
+ configure_effects() // We do this every interval in case it changes.
+ effect_system.start()
+ ..()
+
+
+// Creates smoke clouds every so often.
+/obj/effect/map_effect/interval/effect_emitter/smoke
+ name = "smoke emitter"
+ icon_state = "smoke_emitter"
+ effect_system_type = /datum/effect/effect/system/smoke_spread
+
+ interval_lower_bound = 1 SECOND
+ interval_upper_bound = 1 SECOND
+ effect_amount = 2
+
+/obj/effect/map_effect/interval/effect_emitter/smoke/bad
+ name = "bad smoke emitter"
+ effect_system_type = /datum/effect/effect/system/smoke_spread/bad
+
+/obj/effect/map_effect/interval/effect_emitter/smoke/fire
+ name = "fire smoke emitter"
+ effect_system_type = /datum/effect/effect/system/smoke_spread/fire
+
+/obj/effect/map_effect/interval/effect_emitter/smoke/frost
+ name = "frost smoke emitter"
+ effect_system_type = /datum/effect/effect/system/smoke_spread/frost
+
+/obj/effect/map_effect/interval/effect_emitter/smoke/shock
+ name = "shock smoke emitter"
+ effect_system_type = /datum/effect/effect/system/smoke_spread/shock
+
+/obj/effect/map_effect/interval/effect_emitter/smoke/mist
+ name = "mist smoke emitter"
+ effect_system_type = /datum/effect/effect/system/smoke_spread/mist
+
+
+// Makes sparks.
+/obj/effect/map_effect/interval/effect_emitter/sparks
+ name = "spark emitter"
+ icon_state = "spark_emitter"
+ effect_system_type = /datum/effect/effect/system/spark_spread
+
+ interval_lower_bound = 3 SECONDS
+ interval_upper_bound = 7 SECONDS
+
+/obj/effect/map_effect/interval/effect_emitter/sparks/frequent
+ effect_amount = 4 // Otherwise it caps out fast.
+ interval_lower_bound = 1
+ interval_upper_bound = 3 SECONDS
+
+// Makes ""steam"" that looks like fire extinguisher water except it does nothing.
+/obj/effect/map_effect/interval/effect_emitter/steam
+ name = "steam emitter"
+ icon_state = "smoke_emitter"
+ effect_system_type = /datum/effect/effect/system/steam_spread
diff --git a/code/game/objects/effects/map_effects/map_effects.dm b/code/game/objects/effects/map_effects/map_effects.dm
new file mode 100644
index 0000000000..5cd79f1b0c
--- /dev/null
+++ b/code/game/objects/effects/map_effects/map_effects.dm
@@ -0,0 +1,71 @@
+// These are objects you can use inside special maps (like PoIs), or for adminbuse.
+// Players cannot see or interact with these.
+/obj/effect/map_effect
+ anchored = TRUE
+ invisibility = 99 // So a badmin can go view these by changing their see_invisible.
+ icon = 'icons/effects/map_effects.dmi'
+
+ // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects.
+ var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it.
+ var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run.
+ var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement.
+ var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well.
+ var/retry_delay = 3 SECONDS // How long until we check for players again.
+
+/obj/effect/map_effect/ex_act()
+ return
+
+/obj/effect/map_effect/singularity_pull()
+ return
+
+/obj/effect/map_effect/singularity_act()
+ return
+
+// Base type for effects that run on variable intervals.
+/obj/effect/map_effect/interval
+ var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger.
+ var/interval_upper_bound = 5 SECONDS // Higher number for above.
+ var/halt = FALSE // Set to true to stop the loop when it reaches the next iteration.
+
+/obj/effect/map_effect/interval/Initialize()
+ handle_interval_delay()
+ return ..()
+
+/obj/effect/map_effect/interval/Destroy()
+ halt = TRUE // Shouldn't need it to GC but just in case.
+ return ..()
+
+// Override this for the specific thing to do. Be sure to call parent to keep looping.
+/obj/effect/map_effect/interval/proc/trigger()
+ handle_interval_delay()
+
+// Handles the delay and making sure it doesn't run when it would be bad.
+/obj/effect/map_effect/interval/proc/handle_interval_delay()
+ // Check to see if we're useful first.
+ if(halt)
+ return // Do not pass .(), do not recursively collect 200 thaler.
+
+ if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk))
+ spawn(retry_delay) // Maybe someday we'll have fancy TG timers/schedulers.
+ if(!QDELETED(src))
+ .()
+
+ var/next_interval = rand(interval_lower_bound, interval_upper_bound)
+ spawn(next_interval)
+ if(!QDELETED(src))
+ trigger()
+
+// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it.
+/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE)
+ if(!proximity_to)
+ return FALSE
+
+ for(var/thing in player_list)
+ var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways.
+ if(ignore_ghosts && isobserver(M))
+ continue
+ if(ignore_afk && M.client && M.client.is_afk(5 MINUTES))
+ continue
+ if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius)
+ return TRUE
+ return FALSE
\ No newline at end of file
diff --git a/code/game/objects/effects/map_effects/perma_light.dm b/code/game/objects/effects/map_effects/perma_light.dm
new file mode 100644
index 0000000000..3a82dc7e69
--- /dev/null
+++ b/code/game/objects/effects/map_effects/perma_light.dm
@@ -0,0 +1,9 @@
+// Emits light forever with magic. Useful for mood lighting in Points of Interest.
+// Be sure to check how it looks ingame, and fiddle with the settings until it looks right.
+/obj/effect/map_effect/perma_light
+ name = "permanent light"
+ icon_state = "permalight"
+
+ light_range = 3
+ light_power = 1
+ light_color = "#FFFFFF"
\ No newline at end of file
diff --git a/code/game/objects/effects/map_effects/radiation_emitter.dm b/code/game/objects/effects/map_effects/radiation_emitter.dm
new file mode 100644
index 0000000000..367c27445d
--- /dev/null
+++ b/code/game/objects/effects/map_effects/radiation_emitter.dm
@@ -0,0 +1,19 @@
+// Constantly emites radiation from the tile it's placed on.
+/obj/effect/map_effect/radiation_emitter
+ name = "radiation emitter"
+ icon_state = "radiation_emitter"
+ var/radiation_power = 30 // Bigger numbers means more radiation.
+
+/obj/effect/map_effect/radiation_emitter/Initialize()
+ START_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/effect/map_effect/radiation_emitter/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/effect/map_effect/radiation_emitter/process()
+ radiation_repository.radiate(src, radiation_power)
+
+/obj/effect/map_effect/radiation_emitter/strong
+ radiation_power = 100
\ No newline at end of file
diff --git a/code/game/objects/effects/map_effects/screen_shaker.dm b/code/game/objects/effects/map_effects/screen_shaker.dm
new file mode 100644
index 0000000000..29568242e5
--- /dev/null
+++ b/code/game/objects/effects/map_effects/screen_shaker.dm
@@ -0,0 +1,18 @@
+// Makes the screen shake for nearby players every so often.
+/obj/effect/map_effect/interval/screen_shaker
+ name = "screen shaker"
+ icon_state = "screen_shaker"
+
+ interval_lower_bound = 1 SECOND
+ interval_upper_bound = 2 SECONDS
+
+ var/shake_radius = 7 // How far the shaking effect extends to. By default it is one screen length.
+ var/shake_duration = 2 // How long the shaking lasts.
+ var/shake_strength = 1 // How much it shakes.
+
+/obj/effect/map_effect/interval/screen_shaker/trigger()
+ for(var/A in player_list)
+ var/mob/M = A
+ if(M.z == src.z && get_dist(src, M) <= shake_radius)
+ shake_camera(M, shake_duration, shake_strength)
+ ..()
\ No newline at end of file
diff --git a/code/game/objects/effects/map_effects/sound_emitter.dm b/code/game/objects/effects/map_effects/sound_emitter.dm
new file mode 100644
index 0000000000..4f30a3fdc6
--- /dev/null
+++ b/code/game/objects/effects/map_effects/sound_emitter.dm
@@ -0,0 +1,117 @@
+// Plays a sound at its location every so often.
+/obj/effect/map_effect/interval/sound_emitter
+ name = "sound emitter"
+ icon_state = "sound_emitter"
+ var/list/sounds_to_play = list(null) // List containing sound files or strings of sound groups.
+ // A sound or string is picked randomly each run.
+
+ var/sound_volume = 50 // How loud the sound is. 0 is silent, and 100 is loudest. Please be reasonable with the volume.
+ // Note that things like vacuum may affect the volume heard by other mobs.
+
+ var/sound_frequency_variance = TRUE // If the sound will sound somewhat different each time.
+ // If a specific frequency is desired, sound_frequency must also be set.
+
+ var/sound_extra_range = 0 // Set to make sounds heard from farther away than normal.
+
+ var/sound_fallout = 0 // Within the 'fallout distance', the sound stays at the same volume, otherwise it attenuates.
+ // Higher numbers make the sound fade out more slowly with distance.
+
+ var/sound_global = FALSE // If true, sounds will not be distorted due to the current area's 'sound environment'.
+ // It DOES NOT make the sound have a constant volume or z-level wide range, despite the misleading name.
+
+ var/sound_frequency = null // Sets a specific custom frequency. sound_frequency_variance must be true as well.
+ // If sound_frequency is null, but sound_frequency_variance is true, a semi-random frequency will be chosen to the sound each time.
+
+ var/sound_channel = 0 // BYOND allows a sound to play in 1 through 1024 sound channels.
+ // 0 will have BYOND give it the lowest available channel, it is not recommended to change this without a good reason.
+
+ var/sound_pressure_affected = TRUE // If false, people in low pressure or vacuum will hear the sound.
+
+ var/sound_ignore_walls = TRUE // If false, walls will completely muffle the sound.
+
+ var/sound_preference = null // Player preference to check before playing this sound to them, if any.
+
+/obj/effect/map_effect/interval/sound_emitter/trigger()
+ playsound(
+ src,
+ pick(sounds_to_play),
+ sound_volume,
+ sound_frequency_variance,
+ sound_extra_range,
+ sound_fallout,
+ sound_global,
+ sound_frequency,
+ sound_channel,
+ sound_pressure_affected,
+ sound_ignore_walls,
+ sound_preference
+ )
+ ..()
+
+/obj/effect/map_effect/interval/sound_emitter/thunder
+ sounds_to_play = list("thunder")
+ interval_lower_bound = 10 SECONDS
+ interval_upper_bound = 15 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/geiger
+ sounds_to_play = list("geiger")
+ interval_lower_bound = 2 SECONDS
+ interval_upper_bound = 2 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/geiger_weak
+ sounds_to_play = list("geiger_weak")
+ interval_lower_bound = 1 SECOND
+ interval_upper_bound = 1 SECOND
+
+/obj/effect/map_effect/interval/sound_emitter/punching
+ sounds_to_play = list("punch")
+ interval_lower_bound = 5
+ interval_upper_bound = 1 SECOND
+
+/obj/effect/map_effect/interval/sound_emitter/explosions
+ sounds_to_play = list("explosion")
+ interval_lower_bound = 5 SECONDS
+ interval_upper_bound = 10 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/distant_explosions
+ sounds_to_play = list('sound/effects/explosionfar.ogg')
+ interval_lower_bound = 5 SECONDS
+ interval_upper_bound = 10 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/ballistic_gunfight
+ sounds_to_play = list(
+ 'sound/weapons/gunshot.ogg',
+ 'sound/weapons/deagle.ogg',
+ 'sound/weapons/rifleshot.ogg',
+ 'sound/weapons/sniper.ogg',
+ 'sound/weapons/shotgun.ogg',
+ 'sound/weapons/gunshot3.ogg',
+ 'sound/weapons/machinegun.ogg'
+ )
+ interval_lower_bound = 5
+ interval_upper_bound = 2 SECONDS
+
+/obj/effect/map_effect/interval/sound_emitter/energy_gunfight
+ sounds_to_play = list(
+ 'sound/weapons/Taser.ogg',
+ 'sound/weapons/laser.ogg',
+ 'sound/weapons/eLuger.ogg',
+ 'sound/weapons/laser3.ogg',
+ 'sound/weapons/pulse.ogg',
+ 'sound/weapons/gauss_shoot.ogg',
+ 'sound/weapons/emitter.ogg'
+ )
+ interval_lower_bound = 5
+ interval_upper_bound = 2 SECONDS
+
+
+// I'm not sorry.
+/obj/effect/map_effect/interval/sound_emitter/clownsteps
+ sounds_to_play = list("clownstep")
+ interval_lower_bound = 5
+ interval_upper_bound = 1 SECOND
+
+/obj/effect/map_effect/interval/sound_emitter/bikehorns
+ sounds_to_play = list('sound/items/bikehorn.ogg')
+ interval_lower_bound = 5
+ interval_upper_bound = 1 SECOND
diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm
index d7db6f2a0c..5fca6fbecf 100644
--- a/code/game/objects/effects/misc.dm
+++ b/code/game/objects/effects/misc.dm
@@ -34,7 +34,7 @@
pixel_x = -32
pixel_y = -32
-/obj/effect/temporary_effect/cleave_attack/initialize() // Makes the slash fade smoothly. When completely transparent it should qdel itself.
+/obj/effect/temporary_effect/cleave_attack/Initialize() // Makes the slash fade smoothly. When completely transparent it should qdel itself.
. = ..()
animate(src, alpha = 0, time = time_to_die - 1)
@@ -44,7 +44,7 @@
icon_state = "shuttle_warning_still"
time_to_die = 4.9 SECONDS
-/obj/effect/temporary_effect/shuttle_landing/initialize()
+/obj/effect/temporary_effect/shuttle_landing/Initialize()
flick("shuttle_warning", src) // flick() forces the animation to always begin at the start.
. = ..()
@@ -60,7 +60,7 @@
time_to_die = 1 SECOND
pixel_x = -32
-/obj/effect/temporary_effect/lightning_strike/initialize()
+/obj/effect/temporary_effect/lightning_strike/Initialize()
icon_state += "[rand(1,2)]" // To have two variants of lightning sprites.
animate(src, alpha = 0, time = time_to_die - 1)
. = ..()
\ No newline at end of file
diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm
index 9f4f0fa118..056d9f7295 100644
--- a/code/game/objects/effects/overlays.dm
+++ b/code/game/objects/effects/overlays.dm
@@ -94,3 +94,18 @@
icon_state = "snowwall"
plane = MOB_PLANE
layer = ABOVE_MOB_LAYER
+
+/obj/effect/overlay/holographic
+ mouse_opacity = FALSE
+ anchored = TRUE
+ plane = ABOVE_PLANE
+
+// Similar to the tesla ball but doesn't actually do anything and is purely visual.
+/obj/effect/overlay/energy_ball
+ name = "energy ball"
+ desc = "An energy ball."
+ icon = 'icons/obj/tesla_engine/energy_ball.dmi'
+ icon_state = "energy_ball"
+ plane = PLANE_LIGHTING_ABOVE
+ pixel_x = -32
+ pixel_y = -32
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 7beb1e6329..e331dbcb22 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -56,13 +56,15 @@
/obj/effect/spider/stickyweb
icon_state = "stickyweb1"
- New()
- if(prob(50))
- icon_state = "stickyweb2"
+
+/obj/effect/spider/stickyweb/Initialize()
+ if(prob(50))
+ icon_state = "stickyweb2"
+ return ..()
/obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0)) return 1
- if(istype(mover, /mob/living/simple_animal/hostile/giant_spider))
+ if(istype(mover, /mob/living/simple_mob/animal/giant_spider))
return 1
else if(istype(mover, /mob/living))
if(prob(50))
@@ -80,17 +82,19 @@
var/spiders_min = 6
var/spiders_max = 24
var/spider_type = /obj/effect/spider/spiderling
- New()
- pixel_x = rand(3,-3)
- pixel_y = rand(3,-3)
- processing_objects |= src
+
+/obj/effect/spider/eggcluster/Initialize()
+ pixel_x = rand(3,-3)
+ pixel_y = rand(3,-3)
+ START_PROCESSING(SSobj, src)
+ return ..()
/obj/effect/spider/eggcluster/New(var/location, var/atom/parent)
get_light_and_color(parent)
..()
/obj/effect/spider/eggcluster/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
if(istype(loc, /obj/item/organ/external))
var/obj/item/organ/external/O = loc
O.implants -= src
@@ -129,15 +133,15 @@
var/amount_grown = -1
var/obj/machinery/atmospherics/unary/vent_pump/entry_vent
var/travelling_in_vent = 0
- var/list/grow_as = list(/mob/living/simple_animal/hostile/giant_spider, /mob/living/simple_animal/hostile/giant_spider/nurse, /mob/living/simple_animal/hostile/giant_spider/hunter)
+ var/list/grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/nurse, /mob/living/simple_mob/animal/giant_spider/hunter)
/obj/effect/spider/spiderling/frost
- grow_as = list(/mob/living/simple_animal/hostile/giant_spider/frost)
+ grow_as = list(/mob/living/simple_mob/animal/giant_spider/frost)
/obj/effect/spider/spiderling/New(var/location, var/atom/parent)
pixel_x = rand(6,-6)
pixel_y = rand(6,-6)
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
//50% chance to grow up
if(prob(50))
amount_grown = 1
@@ -145,7 +149,7 @@
..()
/obj/effect/spider/spiderling/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
walk(src, 0) // Because we might have called walk_to, we must stop the walk loop or BYOND keeps an internal reference to us forever.
return ..()
diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm
index febfff6d0f..2ede69f232 100644
--- a/code/game/objects/effects/step_triggers.dm
+++ b/code/game/objects/effects/step_triggers.dm
@@ -128,7 +128,7 @@
var/obj/effect/landmark/the_landmark = null
var/landmark_id = null
-/obj/effect/step_trigger/teleporter/landmark/initialize()
+/obj/effect/step_trigger/teleporter/landmark/Initialize()
. = ..()
for(var/obj/effect/landmark/teleport_mark/mark in tele_landmarks)
if(mark.landmark_id == landmark_id)
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
new file mode 100644
index 0000000000..8a9220e87f
--- /dev/null
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -0,0 +1,46 @@
+/obj/effect/temp_visual/decoy
+ desc = "It's a decoy!"
+ duration = 15
+
+/obj/effect/temp_visual/decoy/Initialize(mapload, atom/mimiced_atom, var/customappearance)
+ . = ..()
+ alpha = initial(alpha)
+ if(mimiced_atom)
+ name = mimiced_atom.name
+ appearance = mimiced_atom.appearance
+ set_dir(mimiced_atom.dir)
+ mouse_opacity = 0
+ if(customappearance)
+ appearance = customappearance
+
+/obj/effect/temp_visual/decoy/fading/Initialize(mapload, atom/mimiced_atom)
+ . = ..()
+ animate(src, alpha = 0, time = duration)
+
+/obj/effect/temp_visual/decoy/fading/fivesecond
+ duration = 50
+
+/obj/effect/temp_visual/small_smoke
+ icon_state = "smoke"
+ duration = 50
+
+// VOREStation Add - Used by Kinetic Accelerator
+/obj/effect/temp_visual/kinetic_blast
+ name = "kinetic explosion"
+ icon = 'icons/obj/projectiles.dmi'
+ icon_state = "kinetic_blast"
+ layer = ABOVE_MOB_LAYER
+ duration = 4
+
+/obj/effect/temp_visual/explosion
+ name = "explosion"
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "explosion"
+ pixel_x = -32
+ pixel_y = -32
+ duration = 8
+
+/obj/effect/temp_visual/explosion/fast
+ icon_state = "explosionfast"
+ duration = 4
+// VOREStation Add End
diff --git a/code/game/objects/effects/temporary_visuals/temproary_visual.dm b/code/game/objects/effects/temporary_visuals/temproary_visual.dm
new file mode 100644
index 0000000000..c5038f1b28
--- /dev/null
+++ b/code/game/objects/effects/temporary_visuals/temproary_visual.dm
@@ -0,0 +1,36 @@
+//temporary visual effects
+/obj/effect/temp_visual
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "nothing"
+ anchored = TRUE
+ layer = ABOVE_MOB_LAYER
+ mouse_opacity = 0
+ var/duration = 10 //in deciseconds
+ var/randomdir = TRUE
+
+/obj/effect/temp_visual/Initialize()
+ . = ..()
+ if(randomdir)
+ set_dir(pick(cardinal))
+
+ spawn(duration)
+ qdel(src)
+
+/obj/effect/temp_visual/singularity_act()
+ return
+
+/obj/effect/temp_visual/singularity_pull()
+ return
+
+/obj/effect/temp_visual/ex_act()
+ return
+
+/*
+/obj/effect/temp_visual/dir_setting
+ randomdir = FALSE
+
+/obj/effect/temp_visual/dir_setting/Initialize(mapload, set_dir)
+ if(set_dir)
+ setDir(set_dir)
+ . = ..()
+*/ //More tg stuff that might be useful later
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 4a80379c16..be332a47e5 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -111,6 +111,13 @@
src.loc = null
return ..()
+// Check if target is reasonable for us to operate on.
+/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
+ if(((src in target) && !target_self) || ((!istype(target.loc, /turf)) && (!istype(target, /turf)) && (not_inside)))
+ return FALSE
+ else
+ return TRUE
+
/obj/item/proc/update_twohanding()
update_held_icon()
@@ -200,6 +207,9 @@
/obj/item/attack_hand(mob/living/user as mob)
if (!user) return
+ if(anchored)
+ to_chat(user, span("notice", "\The [src] won't budge, you can't pick it up!"))
+ return
if (hasorgans(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/temp = H.organs_by_name["r_hand"]
@@ -619,7 +629,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
var/cannotzoom
- if(usr.stat || !(istype(usr,/mob/living/carbon/human)))
+ if((usr.stat && !zoom) || !(istype(usr,/mob/living/carbon/human)))
usr << "You are unable to focus through the [devicename]"
cannotzoom = 1
else if(!zoom && global_hud.darkMask[1] in usr.client.screen)
@@ -629,39 +639,46 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
usr << "You are too distracted to look through the [devicename], perhaps if it was in your active hand this might work better"
cannotzoom = 1
+ //We checked above if they are a human and returned already if they weren't.
+ var/mob/living/carbon/human/H = usr
+
if(!zoom && !cannotzoom)
- if(usr.hud_used.hud_shown)
- usr.toggle_zoom_hud() // If the user has already limited their HUD this avoids them having a HUD when they zoom in
- usr.client.view = viewsize
+ if(H.hud_used.hud_shown)
+ H.toggle_zoom_hud() // If the user has already limited their HUD this avoids them having a HUD when they zoom in
+ H.client.view = viewsize
zoom = 1
var/tilesize = 32
var/viewoffset = tilesize * tileoffset
- switch(usr.dir)
+ switch(H.dir)
if (NORTH)
- usr.client.pixel_x = 0
- usr.client.pixel_y = viewoffset
+ H.client.pixel_x = 0
+ H.client.pixel_y = viewoffset
if (SOUTH)
- usr.client.pixel_x = 0
- usr.client.pixel_y = -viewoffset
+ H.client.pixel_x = 0
+ H.client.pixel_y = -viewoffset
if (EAST)
- usr.client.pixel_x = viewoffset
- usr.client.pixel_y = 0
+ H.client.pixel_x = viewoffset
+ H.client.pixel_y = 0
if (WEST)
- usr.client.pixel_x = -viewoffset
- usr.client.pixel_y = 0
+ H.client.pixel_x = -viewoffset
+ H.client.pixel_y = 0
- usr.visible_message("[usr] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].")
+ H.visible_message("[usr] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].")
+ H.looking_elsewhere = TRUE
+ H.handle_vision()
else
- usr.client.view = world.view
- if(!usr.hud_used.hud_shown)
- usr.toggle_zoom_hud()
+ H.client.view = world.view
+ if(!H.hud_used.hud_shown)
+ H.toggle_zoom_hud()
zoom = 0
- usr.client.pixel_x = 0
- usr.client.pixel_y = 0
+ H.client.pixel_x = 0
+ H.client.pixel_y = 0
+ H.looking_elsewhere = FALSE
+ H.handle_vision()
if(!cannotzoom)
usr.visible_message("[zoomdevicename ? "[usr] looks up from the [src.name]" : "[usr] lowers the [src.name]"].")
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index cf91bbef3b..8f6533a702 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -108,7 +108,7 @@ var/list/civilian_cartridges = list(
icon_state = "cart-s"
access_security = 1
-/obj/item/weapon/cartridge/security/initialize()
+/obj/item/weapon/cartridge/security/Initialize()
radio = new /obj/item/radio/integrated/beepsky(src)
. = ..()
@@ -164,7 +164,7 @@ var/list/civilian_cartridges = list(
access_reagent_scanner = 1
access_atmos = 1
-/obj/item/weapon/cartridge/signal/initialize()
+/obj/item/weapon/cartridge/signal/Initialize()
radio = new /obj/item/radio/integrated/signal(src)
. = ..()
@@ -198,7 +198,7 @@ var/list/civilian_cartridges = list(
access_status_display = 1
access_security = 1
-/obj/item/weapon/cartridge/hos/initialize()
+/obj/item/weapon/cartridge/hos/Initialize()
radio = new /obj/item/radio/integrated/beepsky(src)
. = ..()
@@ -223,7 +223,7 @@ var/list/civilian_cartridges = list(
access_reagent_scanner = 1
access_atmos = 1
-/obj/item/weapon/cartridge/rd/initialize()
+/obj/item/weapon/cartridge/rd/Initialize()
radio = new /obj/item/radio/integrated/signal(src)
. = ..()
diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm
index 9de35c2582..a5a76dd993 100644
--- a/code/game/objects/items/devices/PDA/radio.dm
+++ b/code/game/objects/items/devices/PDA/radio.dm
@@ -116,39 +116,37 @@
var/last_transmission
var/datum/radio_frequency/radio_connection
- initialize()
- if(!radio_controller)
- return
-
- if (src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ)
- src.frequency = sanitize_frequency(src.frequency)
-
- set_frequency(frequency)
-
- proc/set_frequency(new_frequency)
- radio_controller.remove_object(src, frequency)
- frequency = new_frequency
- radio_connection = radio_controller.add_object(src, frequency)
-
- proc/send_signal(message="ACTIVATE")
-
- if(last_transmission && world.time < (last_transmission + 5))
- return
- last_transmission = world.time
-
- var/time = time2text(world.realtime,"hh:mm:ss")
- var/turf/T = get_turf(src)
- lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")
-
- var/datum/signal/signal = new
- signal.source = src
- signal.encryption = code
- signal.data["message"] = message
-
- radio_connection.post_signal(src, signal)
-
+/obj/item/radio/integrated/signal/Initialize()
+ if(!radio_controller)
return
+ if (src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ)
+ src.frequency = sanitize_frequency(src.frequency)
+
+ set_frequency(frequency)
+
+/obj/item/radio/integrated/signal/proc/set_frequency(new_frequency)
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ radio_connection = radio_controller.add_object(src, frequency)
+
+/obj/item/radio/integrated/signal/proc/send_signal(message="ACTIVATE")
+
+ if(last_transmission && world.time < (last_transmission + 5))
+ return
+ last_transmission = world.time
+
+ var/time = time2text(world.realtime,"hh:mm:ss")
+ var/turf/T = get_turf(src)
+ lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")
+
+ var/datum/signal/signal = new
+ signal.source = src
+ signal.encryption = code
+ signal.data["message"] = message
+
+ radio_connection.post_signal(src, signal)
+
/obj/item/radio/integrated/signal/Destroy()
if(radio_controller)
radio_controller.remove_object(src, frequency)
diff --git a/code/game/objects/items/devices/ai_detector.dm b/code/game/objects/items/devices/ai_detector.dm
index 7fa6f74521..fde112706f 100644
--- a/code/game/objects/items/devices/ai_detector.dm
+++ b/code/game/objects/items/devices/ai_detector.dm
@@ -17,11 +17,11 @@
// It's really really unlikely for the view range to change. But why not be futureproof anyways?
range_alert = world.view
range_warning = world.view * 2
- processing_objects += src
+ START_PROCESSING(SSobj, src)
..()
/obj/item/device/multitool/ai_detector/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/device/multitool/ai_detector/process()
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index c2af338521..d700008220 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -107,7 +107,7 @@
return 0
if(!user.IsAdvancedToolUser() && isanimal(user))
- var/mob/living/simple_animal/S = user
+ var/mob/living/simple_mob/S = user
if(!S.IsHumanoidToolUser(src))
return 0
diff --git a/code/game/objects/items/devices/communicator/cartridge.dm b/code/game/objects/items/devices/communicator/cartridge.dm
index 6b29f1121e..b5a46b4ebe 100644
--- a/code/game/objects/items/devices/communicator/cartridge.dm
+++ b/code/game/objects/items/devices/communicator/cartridge.dm
@@ -379,7 +379,7 @@
..()
internal_devices |= new /obj/item/device/halogen_counter(src)
-/obj/item/weapon/commcard/engineering/initialize()
+/obj/item/weapon/commcard/engineering/Initialize()
internal_data["grid_sensors"] = find_powernet_sensors()
internal_data["powernet_target"] = ""
@@ -616,7 +616,7 @@
internal_data["stat_display_active2"] = null
internal_data["stat_display_special"] = null
-/obj/item/weapon/commcard/head/initialize()
+/obj/item/weapon/commcard/head/Initialize()
// Have to register the commcard with the Radio controller to receive updates to the status displays
radio_controller.add_object(src, 1435)
..()
@@ -793,7 +793,7 @@
internal_devices |= new /obj.item/device/analyzer(src)
internal_devices |= new /obj/item/device/halogen_counter(src)
-/obj/item/weapon/commcard/head/ce/initialize()
+/obj/item/weapon/commcard/head/ce/Initialize()
internal_data["grid_sensors"] = find_powernet_sensors()
internal_data["powernet_target"] = ""
@@ -904,7 +904,7 @@
list("name" = "Shuttle Blast Door Control", "template" = "merc_blast_door_control.tmpl")
)
-/obj/item/weapon/commcard/mercenary/initialize()
+/obj/item/weapon/commcard/mercenary/Initialize()
internal_data["shuttle_door_code"] = "smindicate" // Copied from PDA code
internal_data["shuttle_doors"] = find_blast_doors()
diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm
index ec9c09c161..6324af7fb5 100644
--- a/code/game/objects/items/devices/communicator/communicator.dm
+++ b/code/game/objects/items/devices/communicator/communicator.dm
@@ -82,7 +82,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
all_communicators += src
all_communicators = sortAtom(all_communicators)
node = get_exonet_node()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
camera = new(src)
camera.name = "[src] #[rand(100,999)]"
camera.c_tag = camera.name
@@ -324,7 +324,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
//Clean up references that might point at us
all_communicators -= src
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
listening_objects.Remove(src)
QDEL_NULL(camera)
QDEL_NULL(exonet)
diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm
index 1ddfa04bf0..7f0b8b6dac 100644
--- a/code/game/objects/items/devices/defib.dm
+++ b/code/game/objects/items/devices/defib.dm
@@ -53,7 +53,7 @@
else
new_overlays += "[initial(icon_state)]-powered"
- var/ratio = Ceiling(bcell.percent()/25) * 25
+ var/ratio = CEILING(bcell.percent()/25, 1) * 25
new_overlays += "[initial(icon_state)]-charge[ratio]"
else
new_overlays += "[initial(icon_state)]-nocell"
@@ -604,7 +604,7 @@
/obj/item/weapon/shockpaddles/standalone/Destroy()
. = ..()
if(fail_counter)
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
/obj/item/weapon/shockpaddles/standalone/check_charge(var/charge_amt)
return 1
@@ -617,7 +617,7 @@
if(fail_counter > 0)
radiation_repository.radiate(src, fail_counter--)
else
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
/obj/item/weapon/shockpaddles/standalone/emp_act(severity)
..()
@@ -632,7 +632,7 @@
to_chat(loc, "\The [src] feel pleasantly warm.")
if(new_fail && !fail_counter)
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
fail_counter = new_fail
/* From the Bay port, this doesn't seem to have a sprite.
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 44d3c98c8c..4543a6d726 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -19,13 +19,13 @@
var/power_usage
var/power_use = 1
-/obj/item/device/flashlight/initialize()
+/obj/item/device/flashlight/Initialize()
. = ..()
update_icon()
/obj/item/device/flashlight/New()
if(power_use)
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
if(cell_type)
cell = new cell_type(src)
@@ -38,7 +38,7 @@
/obj/item/device/flashlight/Destroy()
if(power_use)
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/device/flashlight/get_cell()
@@ -68,6 +68,7 @@
cell.charge = 0
visible_message("\The [src] flickers before going dull.")
set_light(0)
+ playsound(src.loc, 'sound/effects/sparks3.ogg', 10, 1, -3) //Small cue that your light went dull in your pocket.
on = 0
update_icon()
@@ -180,6 +181,7 @@
user.put_in_hands(cell)
cell = null
user << "You remove the cell from the [src]."
+ playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
on = 0
update_icon()
return
@@ -228,6 +230,7 @@
W.loc = src
cell = W
user << "You install a cell in \the [src]."
+ playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
update_icon()
else
user << "\The [src] already has a cell."
@@ -349,7 +352,7 @@
turn_off()
if(!fuel)
src.icon_state = "[initial(icon_state)]-empty"
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
/obj/item/device/flashlight/flare/proc/turn_off()
on = 0
@@ -372,14 +375,14 @@
user.visible_message("[user] activates the flare.", "You pull the cord on the flare, activating it!")
src.force = on_damage
src.damtype = "fire"
- processing_objects += src
+ START_PROCESSING(SSobj, src)
/obj/item/device/flashlight/flare/proc/ignite() //Used for flare launchers.
on = !on
update_icon()
force = on_damage
damtype = "fire"
- processing_objects += src
+ START_PROCESSING(SSobj, src)
return 1
//Glowsticks
@@ -406,7 +409,7 @@
turn_off()
if(!fuel)
src.icon_state = "[initial(icon_state)]-empty"
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
/obj/item/device/flashlight/glowstick/proc/turn_off()
on = 0
@@ -423,7 +426,7 @@
. = ..()
if(.)
user.visible_message("[user] cracks and shakes the glowstick.", "You crack and shake the glowstick, turning it on!")
- processing_objects += src
+ START_PROCESSING(SSobj, src)
/obj/item/device/flashlight/glowstick/red
name = "red glowstick"
diff --git a/code/game/objects/items/devices/geiger.dm b/code/game/objects/items/devices/geiger.dm
index dbac918609..9ab63afce7 100644
--- a/code/game/objects/items/devices/geiger.dm
+++ b/code/game/objects/items/devices/geiger.dm
@@ -17,10 +17,10 @@
var/radiation_count = 0
/obj/item/device/geiger/New()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
/obj/item/device/geiger/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/device/geiger/process()
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index c62cb541ce..839dc7a2dc 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -17,7 +17,7 @@ var/list/GPS_list = list()
var/hide_signal = FALSE // If true, signal is not visible to other GPS devices.
var/can_hide_signal = FALSE // If it can toggle the above var.
-/obj/item/device/gps/initialize()
+/obj/item/device/gps/Initialize()
. = ..()
GPS_list += src
name = "global positioning system ([gps_tag])"
diff --git a/code/game/objects/items/devices/hacktool.dm b/code/game/objects/items/devices/hacktool.dm
index b5d6439da3..fb3d8d9bcc 100644
--- a/code/game/objects/items/devices/hacktool.dm
+++ b/code/game/objects/items/devices/hacktool.dm
@@ -62,7 +62,7 @@
if(hack_result && in_hack_mode)
user << "Your hacking attempt was succesful!"
- user.playsound_local(get_turf(src), 'sound/piano/A#6.ogg', 50)
+ user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50)
else
user << "Your hacking attempt failed!"
return 0
diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm
index 343a58ce1a..ee77a47de9 100644
--- a/code/game/objects/items/devices/laserpointer.dm
+++ b/code/game/objects/items/devices/laserpointer.dm
@@ -166,7 +166,7 @@
outmsg = "You missed the lens of [C] with [src]."
//cats!
- for(var/mob/living/simple_animal/cat/C in viewers(1,targloc))
+ for(var/mob/living/simple_mob/animal/passive/cat/C in viewers(1,targloc))
if (!(C.stat || C.buckled))
if(prob(50) && !(C.client))
C.visible_message("[C] pounces on the light!", "You pounce on the light!")
@@ -200,7 +200,7 @@
if(energy <= max_energy)
if(!recharging)
recharging = 1
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
if(energy <= 0)
to_chat(user, "You've overused the battery of [src], now it needs time to recharge!")
recharge_locked = 1
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index 21d855c674..e6385c751f 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -18,20 +18,51 @@
matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 20)
+ var/mode_index = 1
+ var/toolmode = MULTITOOL_MODE_STANDARD
+ var/list/modes = list(MULTITOOL_MODE_STANDARD, MULTITOOL_MODE_INTCIRCUITS)
+
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage
var/obj/machinery/clonepod/connecting //same for cryopod linkage
var/obj/machinery/connectable //Used to connect machinery.
+ var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool.
toolspeed = 1
-/obj/item/device/multitool/attack_self(mob/user)
- var/clear = alert("Do you want to clear the buffers on the [src]?",, "Yes", "No",)
- if(clear == "Yes")
- buffer = null
- connecting = null
- connectable = null
+/obj/item/device/multitool/attack_self(mob/living/user)
+ var/choice = alert("What do you want to do with \the [src]?","Multitool Menu", "Switch Mode", "Clear Buffers", "Cancel")
+ switch(choice)
+ if("Cancel")
+ to_chat(user,"You lower \the [src].")
+ return
+ if("Clear Buffers")
+ to_chat(user,"You clear \the [src]'s memory.")
+ buffer = null
+ connecting = null
+ connectable = null
+ weakref_wiring = null
+ accepting_refs = 0
+ if(toolmode == MULTITOOL_MODE_INTCIRCUITS)
+ accepting_refs = 1
+ if("Switch Mode")
+ mode_switch(user)
+
+ update_icon()
+
+ return ..()
+
+/obj/item/device/multitool/proc/mode_switch(mob/living/user)
+ if(++mode_index > modes.len) mode_index = 1
+
else
- ..()
+ mode_index++
+
+ toolmode = modes[mode_index]
+ to_chat(user,"\The [src] is now set to [toolmode].")
+
+ accepting_refs = (toolmode == MULTITOOL_MODE_INTCIRCUITS)
+
+ return
/obj/item/device/multitool/cyborg
name = "multitool"
@@ -44,4 +75,4 @@
icon = 'icons/obj/abductor.dmi'
icon_state = "multitool"
toolspeed = 0.1
- origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5)
\ No newline at end of file
+ origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5)
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index 6ea73e8c78..26daccfc92 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -25,7 +25,7 @@
var/obj/structure/cable/attached // the attached cable
/obj/item/device/powersink/Destroy()
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
STOP_PROCESSING_POWER_OBJECT(src)
..()
@@ -49,7 +49,7 @@
return
else
if (mode == 2)
- processing_objects.Remove(src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite
+ STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite
STOP_PROCESSING_POWER_OBJECT(src)
anchored = 0
mode = 0
@@ -73,14 +73,14 @@
src.visible_message("[user] activates [src]!")
mode = 2
icon_state = "powersink1"
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
START_PROCESSING_POWER_OBJECT(src)
if(2) //This switch option wasn't originally included. It exists now. --NeoFite
src.visible_message("[user] deactivates [src]!")
mode = 1
set_light(0)
icon_state = "powersink0"
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
STOP_PROCESSING_POWER_OBJECT(src)
/obj/item/device/powersink/pwr_drain()
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index a67a2bef7b..51d7e0a4b6 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -92,7 +92,7 @@
syndie = 1
ks1type = /obj/item/device/encryptionkey/raider
-/obj/item/device/radio/headset/raider/initialize()
+/obj/item/device/radio/headset/raider/Initialize()
. = ..()
set_frequency(RAID_FREQ)
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index 364323d183..4a919a1c83 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -53,13 +53,13 @@
/obj/item/device/radio/intercom/omni
name = "global announcer"
-/obj/item/device/radio/intercom/omni/initialize()
+/obj/item/device/radio/intercom/omni/Initialize()
channels = radiochannels.Copy()
return ..()
/obj/item/device/radio/intercom/New()
..()
- processing_objects += src
+ START_PROCESSING(SSobj, src)
circuit = new circuit(src)
/obj/item/device/radio/intercom/department/medbay/New()
@@ -103,7 +103,7 @@
internal_channels[num2text(RAID_FREQ)] = list(access_syndicate)
/obj/item/device/radio/intercom/Destroy()
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/device/radio/intercom/attack_ai(mob/user as mob)
diff --git a/code/game/objects/items/devices/radio/jammer.dm b/code/game/objects/items/devices/radio/jammer.dm
index 9ae7b9fd4e..a1dc09db67 100644
--- a/code/game/objects/items/devices/radio/jammer.dm
+++ b/code/game/objects/items/devices/radio/jammer.dm
@@ -44,7 +44,7 @@ var/global/list/active_radio_jammers = list()
/obj/item/device/radio_jammer/proc/turn_off(mob/user)
if(user)
to_chat(user,"\The [src] deactivates.")
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
active_radio_jammers -= src
on = FALSE
update_icon()
@@ -52,7 +52,7 @@ var/global/list/active_radio_jammers = list()
/obj/item/device/radio_jammer/proc/turn_on(mob/user)
if(user)
to_chat(user,"\The [src] is now active.")
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
active_radio_jammers += src
on = TRUE
update_icon()
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index aec26c8c54..40f87365fe 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -78,7 +78,7 @@ var/global/list/default_medbay_channels = list(
return ..()
-/obj/item/device/radio/initialize()
+/obj/item/device/radio/Initialize()
. = ..()
if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ)
frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
@@ -769,6 +769,7 @@ var/global/list/default_medbay_channels = list(
icon_state = "red_phone"
listening = 1
name = "phone"
+ anchored = FALSE
/obj/item/device/radio/phone/medbay
frequency = MED_I_FREQ
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index feed24510b..97d352c3d7 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -451,15 +451,15 @@ HALOGEN COUNTER - Radcount on mobs
matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 20)
/obj/item/device/slime_scanner/attack(mob/living/M as mob, mob/living/user as mob)
- if(!isslime(M))
- to_chat(user, "This device can only scan slimes!")
+ if(!istype(M, /mob/living/simple_mob/slime/xenobio))
+ to_chat(user, "This device can only scan lab-grown slimes!")
return
- var/mob/living/simple_animal/slime/S = M
+ var/mob/living/simple_mob/slime/xenobio/S = M
user.show_message("Slime scan results:
[S.slime_color] [S.is_adult ? "adult" : "baby"] slime
Health: [S.health]
Mutation Probability: [S.mutation_chance]")
var/list/mutations = list()
for(var/potential_color in S.slime_mutation)
- var/mob/living/simple_animal/slime/slime = potential_color
+ var/mob/living/simple_mob/slime/xenobio/slime = potential_color
mutations.Add(initial(slime.slime_color))
user.show_message("Potental to mutate into [english_list(mutations)] colors.
Extract potential: [S.cores]
Nutrition: [S.nutrition]/[S.get_max_nutrition()]")
@@ -469,12 +469,14 @@ HALOGEN COUNTER - Radcount on mobs
user.show_message("Warning: Subject is hungry.")
user.show_message("Electric change strength: [S.power_charge]")
- if(S.resentment)
- user.show_message("Warning: Subject is harboring resentment.")
- if(S.docile)
+ if(S.has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = S.ai_holder
+ if(AI.resentment)
+ user.show_message("Warning: Subject is harboring resentment.")
+ if(AI.rabid)
+ user.show_message("Subject is enraged and extremely dangerous!")
+ if(S.harmless)
user.show_message("Subject has been pacified.")
- if(S.rabid)
- user.show_message("Subject is enraged and extremely dangerous!")
if(S.unity)
user.show_message("Subject is friendly to other slime colors.")
diff --git a/code/game/objects/items/devices/suit_cooling.dm b/code/game/objects/items/devices/suit_cooling.dm
index 6efc045785..5a6d5872c4 100644
--- a/code/game/objects/items/devices/suit_cooling.dm
+++ b/code/game/objects/items/devices/suit_cooling.dm
@@ -30,7 +30,7 @@
toggle(usr)
/obj/item/device/suit_cooling_unit/New()
- processing_objects |= src
+ START_PROCESSING(SSobj, src)
cell = new/obj/item/weapon/cell/high() //comes not with the crappy default power cell - because this is dedicated EVA equipment
cell.loc = src
diff --git a/code/game/objects/items/devices/t_scanner.dm b/code/game/objects/items/devices/t_scanner.dm
index 5610828bfe..0709429ac1 100644
--- a/code/game/objects/items/devices/t_scanner.dm
+++ b/code/game/objects/items/devices/t_scanner.dm
@@ -28,10 +28,10 @@
/obj/item/device/t_scanner/proc/set_active(var/active)
on = active
if(on)
- processing_objects.Add(src)
+ START_PROCESSING(SSobj, src)
flicker = 0
else
- processing_objects.Remove(src)
+ STOP_PROCESSING(SSobj, src)
set_user_client(null)
update_icon()
diff --git a/code/game/objects/items/devices/text_to_speech.dm b/code/game/objects/items/devices/text_to_speech.dm
index bc0c9d5304..47dfc256fc 100644
--- a/code/game/objects/items/devices/text_to_speech.dm
+++ b/code/game/objects/items/devices/text_to_speech.dm
@@ -1,7 +1,7 @@
/obj/item/device/text_to_speech
name = "TTS device"
desc = "A device that speaks an inputted message. Given to crew which can not speak properly or at all."
- icon = 'icons/obj/electronic_assemblies.dmi'
+ icon = 'icons/obj/integrated_electronics/electronic_setups.dmi'
icon_state = "setup_small"
w_class = ITEMSIZE_SMALL
var/named
diff --git a/code/game/objects/items/devices/tvcamera.dm b/code/game/objects/items/devices/tvcamera.dm
index be2b4c18e7..3fdaa5e79f 100644
--- a/code/game/objects/items/devices/tvcamera.dm
+++ b/code/game/objects/items/devices/tvcamera.dm
@@ -26,7 +26,7 @@
to_chat(usr, "Video feed is [camera.status ? "on" : "off"]")
to_chat(usr, "Audio feed is [radio.broadcasting ? "on" : "off"]")
-/obj/item/device/tvcamera/initialize()
+/obj/item/device/tvcamera/Initialize()
. = ..()
camera = new(src)
camera.c_tag = channel
diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm
index 2778f46b03..0bdb5a6b72 100644
--- a/code/game/objects/items/devices/uplink.dm
+++ b/code/game/objects/items/devices/uplink.dm
@@ -28,11 +28,11 @@
uses = owner.tcrystals
else
uses = telecrystals
- processing_objects += src
+ START_PROCESSING(SSobj, src)
/obj/item/device/uplink/Destroy()
world_uplinks -= src
- processing_objects -= src
+ STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/device/uplink/get_item_cost(var/item_type, var/item_cost)
diff --git a/code/game/objects/items/devices/violin.dm b/code/game/objects/items/devices/violin.dm
index 683c74bd02..5dc215cf91 100644
--- a/code/game/objects/items/devices/violin.dm
+++ b/code/game/objects/items/devices/violin.dm
@@ -1,392 +1,41 @@
//copy pasta of the space piano, don't hurt me -Pete
-
-/obj/item/device/violin
- name = "space violin"
- desc = "A wooden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
+/obj/item/device/instrument
+ name = "generic instrument"
+ var/datum/song/handheld/song
+ var/instrumentId = "generic"
+ var/instrumentExt = "mid"
icon = 'icons/obj/musician.dmi'
- icon_state = "violin"
force = 10
- var/datum/song/song
- var/playing = 0
- var/help = 0
- var/edit = 1
- var/repeat = 0
-/obj/item/device/violin/proc/playnote(var/note as text)
- //world << "Note: [note]"
- var/soundfile
- /*BYOND loads resource files at compile time if they are ''. This means you can't really manipulate them dynamically.
- Tried doing it dynamically at first but its more trouble than its worth. Would have saved many lines tho.*/
- switch(note)
- if("Cn1") soundfile = 'sound/violin/Cn1.mid'
- if("C#1") soundfile = 'sound/violin/C#1.mid'
- if("Db1") soundfile = 'sound/violin/Db1.mid'
- if("Dn1") soundfile = 'sound/violin/Dn1.mid'
- if("D#1") soundfile = 'sound/violin/D#1.mid'
- if("Eb1") soundfile = 'sound/violin/Eb1.mid'
- if("En1") soundfile = 'sound/violin/En1.mid'
- if("E#1") soundfile = 'sound/violin/E#1.mid'
- if("Fb1") soundfile = 'sound/violin/Fb1.mid'
- if("Fn1") soundfile = 'sound/violin/Fn1.mid'
- if("F#1") soundfile = 'sound/violin/F#1.mid'
- if("Gb1") soundfile = 'sound/violin/Gb1.mid'
- if("Gn1") soundfile = 'sound/violin/Gn1.mid'
- if("G#1") soundfile = 'sound/violin/G#1.mid'
- if("Ab1") soundfile = 'sound/violin/Ab1.mid'
- if("An1") soundfile = 'sound/violin/An1.mid'
- if("A#1") soundfile = 'sound/violin/A#1.mid'
- if("Bb1") soundfile = 'sound/violin/Bb1.mid'
- if("Bn1") soundfile = 'sound/violin/Bn1.mid'
- if("B#1") soundfile = 'sound/violin/B#1.mid'
- if("Cb2") soundfile = 'sound/violin/Cb2.mid'
- if("Cn2") soundfile = 'sound/violin/Cn2.mid'
- if("C#2") soundfile = 'sound/violin/C#2.mid'
- if("Db2") soundfile = 'sound/violin/Db2.mid'
- if("Dn2") soundfile = 'sound/violin/Dn2.mid'
- if("D#2") soundfile = 'sound/violin/D#2.mid'
- if("Eb2") soundfile = 'sound/violin/Eb2.mid'
- if("En2") soundfile = 'sound/violin/En2.mid'
- if("E#2") soundfile = 'sound/violin/E#2.mid'
- if("Fb2") soundfile = 'sound/violin/Fb2.mid'
- if("Fn2") soundfile = 'sound/violin/Fn2.mid'
- if("F#2") soundfile = 'sound/violin/F#2.mid'
- if("Gb2") soundfile = 'sound/violin/Gb2.mid'
- if("Gn2") soundfile = 'sound/violin/Gn2.mid'
- if("G#2") soundfile = 'sound/violin/G#2.mid'
- if("Ab2") soundfile = 'sound/violin/Ab2.mid'
- if("An2") soundfile = 'sound/violin/An2.mid'
- if("A#2") soundfile = 'sound/violin/A#2.mid'
- if("Bb2") soundfile = 'sound/violin/Bb2.mid'
- if("Bn2") soundfile = 'sound/violin/Bn2.mid'
- if("B#2") soundfile = 'sound/violin/B#2.mid'
- if("Cb3") soundfile = 'sound/violin/Cb3.mid'
- if("Cn3") soundfile = 'sound/violin/Cn3.mid'
- if("C#3") soundfile = 'sound/violin/C#3.mid'
- if("Db3") soundfile = 'sound/violin/Db3.mid'
- if("Dn3") soundfile = 'sound/violin/Dn3.mid'
- if("D#3") soundfile = 'sound/violin/D#3.mid'
- if("Eb3") soundfile = 'sound/violin/Eb3.mid'
- if("En3") soundfile = 'sound/violin/En3.mid'
- if("E#3") soundfile = 'sound/violin/E#3.mid'
- if("Fb3") soundfile = 'sound/violin/Fb3.mid'
- if("Fn3") soundfile = 'sound/violin/Fn3.mid'
- if("F#3") soundfile = 'sound/violin/F#3.mid'
- if("Gb3") soundfile = 'sound/violin/Gb3.mid'
- if("Gn3") soundfile = 'sound/violin/Gn3.mid'
- if("G#3") soundfile = 'sound/violin/G#3.mid'
- if("Ab3") soundfile = 'sound/violin/Ab3.mid'
- if("An3") soundfile = 'sound/violin/An3.mid'
- if("A#3") soundfile = 'sound/violin/A#3.mid'
- if("Bb3") soundfile = 'sound/violin/Bb3.mid'
- if("Bn3") soundfile = 'sound/violin/Bn3.mid'
- if("B#3") soundfile = 'sound/violin/B#3.mid'
- if("Cb4") soundfile = 'sound/violin/Cb4.mid'
- if("Cn4") soundfile = 'sound/violin/Cn4.mid'
- if("C#4") soundfile = 'sound/violin/C#4.mid'
- if("Db4") soundfile = 'sound/violin/Db4.mid'
- if("Dn4") soundfile = 'sound/violin/Dn4.mid'
- if("D#4") soundfile = 'sound/violin/D#4.mid'
- if("Eb4") soundfile = 'sound/violin/Eb4.mid'
- if("En4") soundfile = 'sound/violin/En4.mid'
- if("E#4") soundfile = 'sound/violin/E#4.mid'
- if("Fb4") soundfile = 'sound/violin/Fb4.mid'
- if("Fn4") soundfile = 'sound/violin/Fn4.mid'
- if("F#4") soundfile = 'sound/violin/F#4.mid'
- if("Gb4") soundfile = 'sound/violin/Gb4.mid'
- if("Gn4") soundfile = 'sound/violin/Gn4.mid'
- if("G#4") soundfile = 'sound/violin/G#4.mid'
- if("Ab4") soundfile = 'sound/violin/Ab4.mid'
- if("An4") soundfile = 'sound/violin/An4.mid'
- if("A#4") soundfile = 'sound/violin/A#4.mid'
- if("Bb4") soundfile = 'sound/violin/Bb4.mid'
- if("Bn4") soundfile = 'sound/violin/Bn4.mid'
- if("B#4") soundfile = 'sound/violin/B#4.mid'
- if("Cb5") soundfile = 'sound/violin/Cb5.mid'
- if("Cn5") soundfile = 'sound/violin/Cn5.mid'
- if("C#5") soundfile = 'sound/violin/C#5.mid'
- if("Db5") soundfile = 'sound/violin/Db5.mid'
- if("Dn5") soundfile = 'sound/violin/Dn5.mid'
- if("D#5") soundfile = 'sound/violin/D#5.mid'
- if("Eb5") soundfile = 'sound/violin/Eb5.mid'
- if("En5") soundfile = 'sound/violin/En5.mid'
- if("E#5") soundfile = 'sound/violin/E#5.mid'
- if("Fb5") soundfile = 'sound/violin/Fb5.mid'
- if("Fn5") soundfile = 'sound/violin/Fn5.mid'
- if("F#5") soundfile = 'sound/violin/F#5.mid'
- if("Gb5") soundfile = 'sound/violin/Gb5.mid'
- if("Gn5") soundfile = 'sound/violin/Gn5.mid'
- if("G#5") soundfile = 'sound/violin/G#5.mid'
- if("Ab5") soundfile = 'sound/violin/Ab5.mid'
- if("An5") soundfile = 'sound/violin/An5.mid'
- if("A#5") soundfile = 'sound/violin/A#5.mid'
- if("Bb5") soundfile = 'sound/violin/Bb5.mid'
- if("Bn5") soundfile = 'sound/violin/Bn5.mid'
- if("B#5") soundfile = 'sound/violin/B#5.mid'
- if("Cb6") soundfile = 'sound/violin/Cb6.mid'
- if("Cn6") soundfile = 'sound/violin/Cn6.mid'
- if("C#6") soundfile = 'sound/violin/C#6.mid'
- if("Db6") soundfile = 'sound/violin/Db6.mid'
- if("Dn6") soundfile = 'sound/violin/Dn6.mid'
- if("D#6") soundfile = 'sound/violin/D#6.mid'
- if("Eb6") soundfile = 'sound/violin/Eb6.mid'
- if("En6") soundfile = 'sound/violin/En6.mid'
- if("E#6") soundfile = 'sound/violin/E#6.mid'
- if("Fb6") soundfile = 'sound/violin/Fb6.mid'
- if("Fn6") soundfile = 'sound/violin/Fn6.mid'
- if("F#6") soundfile = 'sound/violin/F#6.mid'
- if("Gb6") soundfile = 'sound/violin/Gb6.mid'
- if("Gn6") soundfile = 'sound/violin/Gn6.mid'
- if("G#6") soundfile = 'sound/violin/G#6.mid'
- if("Ab6") soundfile = 'sound/violin/Ab6.mid'
- if("An6") soundfile = 'sound/violin/An6.mid'
- if("A#6") soundfile = 'sound/violin/A#6.mid'
- if("Bb6") soundfile = 'sound/violin/Bb6.mid'
- if("Bn6") soundfile = 'sound/violin/Bn6.mid'
- if("B#6") soundfile = 'sound/violin/B#6.mid'
- if("Cb7") soundfile = 'sound/violin/Cb7.mid'
- if("Cn7") soundfile = 'sound/violin/Cn7.mid'
- if("C#7") soundfile = 'sound/violin/C#7.mid'
- if("Db7") soundfile = 'sound/violin/Db7.mid'
- if("Dn7") soundfile = 'sound/violin/Dn7.mid'
- if("D#7") soundfile = 'sound/violin/D#7.mid'
- if("Eb7") soundfile = 'sound/violin/Eb7.mid'
- if("En7") soundfile = 'sound/violin/En7.mid'
- if("E#7") soundfile = 'sound/violin/E#7.mid'
- if("Fb7") soundfile = 'sound/violin/Fb7.mid'
- if("Fn7") soundfile = 'sound/violin/Fn7.mid'
- if("F#7") soundfile = 'sound/violin/F#7.mid'
- if("Gb7") soundfile = 'sound/violin/Gb7.mid'
- if("Gn7") soundfile = 'sound/violin/Gn7.mid'
- if("G#7") soundfile = 'sound/violin/G#7.mid'
- if("Ab7") soundfile = 'sound/violin/Ab7.mid'
- if("An7") soundfile = 'sound/violin/An7.mid'
- if("A#7") soundfile = 'sound/violin/A#7.mid'
- if("Bb7") soundfile = 'sound/violin/Bb7.mid'
- if("Bn7") soundfile = 'sound/violin/Bn7.mid'
- if("B#7") soundfile = 'sound/violin/B#7.mid'
- if("Cb8") soundfile = 'sound/violin/Cb8.mid'
- if("Cn8") soundfile = 'sound/violin/Cn8.mid'
- if("C#8") soundfile = 'sound/violin/C#8.mid'
- if("Db8") soundfile = 'sound/violin/Db8.mid'
- if("Dn8") soundfile = 'sound/violin/Dn8.mid'
- if("D#8") soundfile = 'sound/violin/D#8.mid'
- if("Eb8") soundfile = 'sound/violin/Eb8.mid'
- if("En8") soundfile = 'sound/violin/En8.mid'
- if("E#8") soundfile = 'sound/violin/E#8.mid'
- if("Fb8") soundfile = 'sound/violin/Fb8.mid'
- if("Fn8") soundfile = 'sound/violin/Fn8.mid'
- if("F#8") soundfile = 'sound/violin/F#8.mid'
- if("Gb8") soundfile = 'sound/violin/Gb8.mid'
- if("Gn8") soundfile = 'sound/violin/Gn8.mid'
- if("G#8") soundfile = 'sound/violin/G#8.mid'
- if("Ab8") soundfile = 'sound/violin/Ab8.mid'
- if("An8") soundfile = 'sound/violin/An8.mid'
- if("A#8") soundfile = 'sound/violin/A#8.mid'
- if("Bb8") soundfile = 'sound/violin/Bb8.mid'
- if("Bn8") soundfile = 'sound/violin/Bn8.mid'
- if("B#8") soundfile = 'sound/violin/B#8.mid'
- if("Cb9") soundfile = 'sound/violin/Cb9.mid'
- if("Cn9") soundfile = 'sound/violin/Cn9.mid'
- else return
+/obj/item/device/instrument/New()
+ ..()
+ song = new(instrumentId, src)
+ song.instrumentExt = instrumentExt
- hearers(15, get_turf(src)) << sound(soundfile)
+/obj/item/device/instrument/Destroy()
+ qdel(song)
+ song = null
+ ..()
-/obj/item/device/violin/proc/playsong()
- do
- var/cur_oct[7]
- var/cur_acc[7]
- for(var/i = 1 to 7)
- cur_oct[i] = "3"
- cur_acc[i] = "n"
+/obj/item/device/instrument/attack_self(mob/user as mob)
+ if(!user.IsAdvancedToolUser())
+ to_chat(user, "You don't have the dexterity to do this!")
+ return 1
+ interact(user)
- for(var/line in song.lines)
- //world << line
- for(var/beat in splittext(lowertext(line), ","))
- //world << "beat: [beat]"
- var/list/notes = splittext(beat, "/")
- for(var/note in splittext(notes[1], "-"))
- //world << "note: [note]"
- if(!playing || !isliving(loc))//If the violin is playing, or isn't held by a person
- playing = 0
- return
- if(lentext(note) == 0)
- continue
- //world << "Parse: [copytext(note,1,2)]"
- var/cur_note = text2ascii(note) - 96
- if(cur_note < 1 || cur_note > 7)
- continue
- for(var/i=2 to lentext(note))
- var/ni = copytext(note,i,i+1)
- if(!text2num(ni))
- if(ni == "#" || ni == "b" || ni == "n")
- cur_acc[cur_note] = ni
- else if(ni == "s")
- cur_acc[cur_note] = "#" // so shift is never required
- else
- cur_oct[cur_note] = ni
- playnote(uppertext(copytext(note,1,2)) + cur_acc[cur_note] + cur_oct[cur_note])
- if(notes.len >= 2 && text2num(notes[2]))
- sleep(song.tempo / text2num(notes[2]))
- else
- sleep(song.tempo)
- if(repeat > 0)
- repeat-- //Infinite loops are baaaad.
- while(repeat > 0)
- playing = 0
-
-/obj/item/device/violin/attack_self(mob/user as mob)
- if(!isliving(user) || user.stat || user.restrained() || user.lying) return
- user.set_machine(src)
-
- var/dat = "Violin"
-
- if(song)
- if(song.lines.len > 0 && !(playing))
- dat += "Play Song
"
- dat += "Repeat Song: [repeat] times.
"
- if(playing)
- dat += "Stop Playing
"
- dat += "Repeats left: [repeat].
"
- if(!edit)
- dat += "Show Editor
"
- else
- dat += "Hide Editor
"
- dat += "Start a New Song
"
- dat += "Import a Song
"
- if(song)
- var/calctempo = (10/song.tempo)*60
- dat += "Tempo : -- [calctempo] BPM ++
"
- var/linecount = 0
- for(var/line in song.lines)
- linecount += 1
- dat += "Line [linecount]: [line] Delete Line Modify Line
"
- dat += "Add Line
"
- if(help)
- dat += "Hide Help
"
- dat += {"
- Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-).
- Every note in a chord will play together, with chord timed by the tempo.
-
- Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
- By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
- Example: C,D,E,F,G,A,B will play a C major scale.
- After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
- Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
- A pause may be denoted by an empty chord: C,E,,C,G
- To make a chord be a different time, end it with /x, where the chord length will be length
- defined by tempo / x: C,G/2,E/4
- Combined, an example is: E-E4/4,/2,G#/8,B/8,E3-E4/4
-
- Lines may be up to 50 characters.
- A song may only contain up to 50 lines.
- "}
- else
- dat += "Show Help
"
- dat += "