diff --git a/.travis.yml b/.travis.yml
index 04817d797a..428ab53b86 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@ sudo: false
env:
BYOND_MAJOR="512"
- BYOND_MINOR="1392"
+ BYOND_MINOR="1403"
MACRO_COUNT=4
cache:
@@ -37,9 +37,14 @@ script:
- (num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ])
- source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup
- python tools/TagMatcher/tag-matcher.py ../..
- - echo "#define UNIT_TEST 1" > code/_unit_tests.dm
+ #First compile is to ensure maps are valid.
+ - echo "#define MAP_TEST 1" > code/_map_tests.dm
- cp config/example/* config/
- DreamMaker vorestation.dme
- - travis_wait DreamDaemon vorestation.dmb -invisible -trusted -core 2>&1 | tee log.txt
+ - echo "#define MAP_TEST 0" > code/_map_tests.dm
+ #Second compile is for the unit tests. Compiling a second time to exclude the validated maps is actually faster than waiting for startup with them compiled.
+ - echo "#define UNIT_TEST 1" > code/_unit_tests.dm
+ - DreamMaker vorestation.dme
+ - DreamDaemon vorestation.dmb -invisible -trusted -core 2>&1 | tee log.txt
- grep "All Unit Tests Passed" log.txt
diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm
index ee24252a0c..0502f2b8f8 100644
--- a/code/ATMOSPHERICS/atmospherics.dm
+++ b/code/ATMOSPHERICS/atmospherics.dm
@@ -10,9 +10,6 @@ Pipelines + Other Objects -> Pipe network
*/
/obj/machinery/atmospherics
-
- auto_init = 0
-
anchored = 1
idle_power_usage = 0
active_power_usage = 0
@@ -33,6 +30,7 @@ Pipelines + Other Objects -> Pipe network
var/obj/machinery/atmospherics/node2
/obj/machinery/atmospherics/New()
+ ..()
if(!icon_manager)
icon_manager = new()
@@ -42,7 +40,19 @@ Pipelines + Other Objects -> Pipe network
if(!pipe_color_check(pipe_color))
pipe_color = null
- ..()
+ init_dir()
+
+// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes.
+/obj/machinery/atmospherics/proc/init_dir()
+ return
+
+// Initializes nodes by looking at neighboring atmospherics machinery to connect to.
+// When we're being constructed at runtime, atmos_init() is called by the construction code.
+// When dynamically loading a map atmos_init is called by the maploader (initTemplateBounds proc)
+// But during initial world creation its called by the master_controller.
+// TODO - Consolidate these different ways of being called once SSatoms is created.
+/obj/machinery/atmospherics/proc/atmos_init()
+ return
/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob)
if(istype(A, /obj/item/device/pipe_painter))
diff --git a/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm b/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm
index 5e6dcb95cb..1429b9b30a 100644
--- a/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm
@@ -9,12 +9,13 @@
circuit = /obj/item/weapon/circuitboard/algae_farm
anchored = 1
density = 1
- use_power = 2
+ power_channel = EQUIP
+ use_power = 1
idle_power_usage = 100 // Minimal lights to keep algae alive
active_power_usage = 5000 // Powerful grow lights to stimulate oxygen production
//power_rating = 7500 //7500 W ~ 10 HP
- var/list/stored_material = list(MATERIAL_ALGAE = 10000, MATERIAL_CARBON = 0)
+ var/list/stored_material = list(MATERIAL_ALGAE = 0, MATERIAL_CARBON = 0)
// Capacity increases with matter bin quality
var/list/storage_capacity = list(MATERIAL_ALGAE = 10000, MATERIAL_CARBON = 10000)
// Speed at which we convert CO2 to O2. Increases with manipulator quality
@@ -32,12 +33,15 @@
var/const/input_gas = "carbon_dioxide"
var/const/output_gas = "oxygen"
+/obj/machinery/atmospherics/binary/algae_farm/filled
+ stored_material = list(MATERIAL_ALGAE = 10000, MATERIAL_CARBON = 0)
+
/obj/machinery/atmospherics/binary/algae_farm/New()
..()
desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]."
default_apply_parts()
update_icon()
- // TODO - Make these in acutal icon states so its not silly like this
+ // TODO - Make these in actual icon states so its not silly like this
var/image/I = image(icon = icon, icon_state = "algae-pipe-overlay", dir = dir)
I.color = PIPE_COLOR_BLUE
overlays += I
@@ -54,18 +58,28 @@
recent_moles_transferred = 0
if(inoperable() || use_power < 2)
+ ui_error = null
+ update_icon()
+ if(use_power == 1)
+ last_power_draw = idle_power_usage
+ else
+ last_power_draw = 0
return 0
+ last_power_draw = active_power_usage
+
// STEP 1 - Check material resources
if(stored_material[MATERIAL_ALGAE] < algae_per_mole)
ui_error = "Insufficient [material_display_name(MATERIAL_ALGAE)] to process."
+ update_icon()
return
if(stored_material[MATERIAL_CARBON] + carbon_per_mole > storage_capacity[MATERIAL_CARBON])
ui_error = "[material_display_name(MATERIAL_CARBON)] output storage is full."
+ update_icon()
return
var/moles_to_convert = min(moles_per_tick,\
stored_material[MATERIAL_ALGAE] * algae_per_mole,\
- storage_capacity[MATERIAL_CARBON] - stored_material[MATERIAL_CARBON] * carbon_per_mole)
+ storage_capacity[MATERIAL_CARBON] - stored_material[MATERIAL_CARBON])
// STEP 2 - Take the CO2 out of the input!
var/power_draw = scrub_gas(src, list(input_gas), air1, internal, moles_to_convert)
@@ -79,6 +93,7 @@
var/co2_moles = internal.gas[input_gas]
if(co2_moles < MINIMUM_MOLES_TO_FILTER)
ui_error = "Insufficient [gas_data.name[input_gas]] to process."
+ update_icon()
return
// STEP 4 - Consume the resources
@@ -98,7 +113,7 @@
update_icon()
/obj/machinery/atmospherics/binary/algae_farm/update_icon()
- if(inoperable() || !anchored)
+ if(inoperable() || !anchored || use_power < 2)
icon_state = "algae-off"
else if(recent_moles_transferred >= moles_per_tick)
icon_state = "algae-full"
@@ -109,21 +124,47 @@
return 1
/obj/machinery/atmospherics/binary/algae_farm/attackby(obj/item/weapon/W as obj, mob/user as mob)
- src.add_fingerprint(user)
+ add_fingerprint(user)
if(default_deconstruction_screwdriver(user, W))
return
if(default_deconstruction_crowbar(user, W))
return
+ if(default_part_replacement(user, W))
+ return
if(try_load_materials(user, W))
return
else
- user << "You cannot insert this item into \the [src]!"
+ to_chat(user, "You cannot insert this item into \the [src]!")
return
/obj/machinery/atmospherics/binary/algae_farm/attack_hand(mob/user)
- if(..()) return 1
+ if(..())
+ return 1
ui_interact(user)
+/obj/machinery/atmospherics/binary/algae_farm/RefreshParts()
+ ..()
+
+ var/cap_rating = 0
+ var/bin_rating = 0
+ var/manip_rating = 0
+
+ for(var/obj/item/weapon/stock_parts/P in component_parts)
+ if(istype(P, /obj/item/weapon/stock_parts/capacitor))
+ cap_rating += P.rating
+ if(istype(P, /obj/item/weapon/stock_parts/matter_bin))
+ bin_rating += P.rating
+ if(istype(P, /obj/item/weapon/stock_parts/manipulator))
+ manip_rating += P.rating
+
+ power_per_mole = round(initial(power_per_mole) / cap_rating)
+
+ var/storage = 5000 * (bin_rating**2)/2
+ for(var/mat in storage_capacity)
+ storage_capacity[mat] = storage
+
+ moles_per_tick = initial(moles_per_tick) + (manip_rating**2 - 1)
+
/obj/machinery/atmospherics/binary/algae_farm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/state = default_state)
var/data[0]
data["panelOpen"] = panel_open
@@ -217,7 +258,7 @@
if(!istype(S))
return 0
if(!(S.material.name in stored_material))
- user << "\The [src] doesn't accept [material_display_name(S.material)]!"
+ to_chat(user, "\The [src] doesn't accept [material_display_name(S.material)]!")
return 1
var/max_res_amount = storage_capacity[S.material.name]
if(stored_material[S.material.name] + S.perunit <= max_res_amount)
@@ -229,7 +270,7 @@
user.visible_message("\The [user] inserts [S.name] into \the [src].", "You insert [count] [S.name] into \the [src].")
updateUsrDialog()
else
- user << "\The [src] cannot hold more [S.name]."
+ to_chat(user, "\The [src] cannot hold more [S.name].")
return 1
/material/algae
@@ -248,6 +289,9 @@
color = "#557722"
default_type = MATERIAL_ALGAE
+/obj/item/stack/material/algae/ten
+ amount = 10
+
/material/carbon
name = MATERIAL_CARBON
stack_type = /obj/item/stack/material/carbon
diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
index 4bbbf02b16..6e6662f39c 100644
--- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
@@ -57,24 +57,20 @@
node1 = null
node2 = null
-/obj/machinery/atmospherics/binary/initialize()
+/obj/machinery/atmospherics/binary/atmos_init()
if(node1 && node2)
return
- init_dir()
-
var/node2_connect = dir
var/node1_connect = turn(dir, 180)
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm
index 09335cdf33..68ce37d088 100644
--- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm
@@ -101,13 +101,13 @@
else if(dir & (EAST|WEST))
initialize_directions = EAST|WEST
- initialize()
+ atmos_init()
build_network()
if (node1)
- node1.initialize()
+ node1.atmos_init()
node1.build_network()
if (node2)
- node2.initialize()
+ node2.atmos_init()
node2.build_network()
else
if(node1)
diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
index 66468a516c..5c5ff50299 100644
--- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
@@ -47,6 +47,10 @@
air2.volume = ATMOS_DEFAULT_VOLUME_PUMP
icon = null
+/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume
name = "Large Dual Port Air Vent"
diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
index ceb192f9b0..4c1863b133 100644
--- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
@@ -29,6 +29,10 @@
air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5
air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5
+/obj/machinery/atmospherics/binary/passive_gate/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/binary/passive_gate/update_icon()
icon_state = (unlocked && flowing)? "on" : "off"
@@ -166,7 +170,7 @@
return
src.add_fingerprint(usr)
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
usr.set_machine(src)
ui_interact(user)
@@ -240,14 +244,14 @@
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (unlocked)
- user << "You cannot unwrench \the [src], turn it off first."
+ to_chat(user, "You cannot unwrench \the [src], turn it off first.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm
index 1b576e1042..b8c7883796 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm
@@ -87,7 +87,7 @@
if(istype(W, /obj/item/weapon/wrench))
anchored = !anchored
playsound(src, W.usesound, 50, 1)
- user << "You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor."
+ to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.")
if(anchored)
if(dir & (NORTH|SOUTH))
@@ -95,13 +95,13 @@
else if(dir & (EAST|WEST))
initialize_directions = NORTH|SOUTH
- initialize()
+ atmos_init()
build_network()
if (node1)
- node1.initialize()
+ node1.atmos_init()
node1.build_network()
if (node2)
- node2.initialize()
+ node2.atmos_init()
node2.build_network()
else
if(node1)
@@ -153,7 +153,7 @@
return null
- initialize()
+ atmos_init()
if(node1 && node2) return
var/node2_connect = turn(dir, -90)
@@ -260,7 +260,7 @@
anchored = !anchored
playsound(src, W.usesound, 50, 1)
turbine = null
- user << "You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor."
+ to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.")
updateConnection()
else
..()
diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm
index 97abccdfad..cddd9f9527 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm
@@ -39,6 +39,10 @@ Thus, the two variables affect pump operation are set in New():
air1.volume = ATMOS_DEFAULT_VOLUME_PUMP
air2.volume = ATMOS_DEFAULT_VOLUME_PUMP
+/obj/machinery/atmospherics/binary/pump/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/binary/pump/on
icon_state = "map_on"
use_power = 1
@@ -183,7 +187,7 @@ Thus, the two variables affect pump operation are set in New():
return
src.add_fingerprint(usr)
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
usr.set_machine(src)
ui_interact(user)
@@ -219,14 +223,14 @@ Thus, the two variables affect pump operation are set in New():
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (!(stat & NOPOWER) && use_power)
- user << "You cannot unwrench this [src], turn it off first."
+ to_chat(user, "You cannot unwrench this [src], turn it off first.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench this [src], it too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
index a191370924..fc25eae3b7 100644
--- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
@@ -40,10 +40,10 @@
/datum/omni_port/proc/connect()
if(node)
return
- master.initialize()
+ master.atmos_init()
master.build_network()
if(node)
- node.initialize()
+ node.atmos_init()
node.build_network()
/datum/omni_port/proc/disconnect()
diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
index c2e1e34f2a..b1131c9d4a 100644
--- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
@@ -86,7 +86,7 @@
to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.")
add_fingerprint(user)
return 1
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
playsound(src, W.usesound, 50, 1)
if(do_after(user, 40 * W.toolspeed))
user.visible_message( \
@@ -245,14 +245,13 @@
qdel(P.network)
P.node = null
- ..()
+ . = ..()
-/obj/machinery/atmospherics/omni/initialize()
+/obj/machinery/atmospherics/omni/atmos_init()
for(var/datum/omni_port/P in ports)
if(P.node || P.mode == 0)
continue
for(var/obj/machinery/atmospherics/target in get_step(src, P.dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
P.node = target
diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm
index c1949f8a39..6911288c93 100644
--- a/code/ATMOSPHERICS/components/portables_connector.dm
+++ b/code/ATMOSPHERICS/components/portables_connector.dm
@@ -21,10 +21,6 @@
/obj/machinery/atmospherics/portables_connector/init_dir()
initialize_directions = dir
-/obj/machinery/atmospherics/portables_connector/New()
- init_dir()
- ..()
-
/obj/machinery/atmospherics/portables_connector/update_icon()
icon_state = "connector"
@@ -74,16 +70,13 @@
node = null
-/obj/machinery/atmospherics/portables_connector/initialize()
+/obj/machinery/atmospherics/portables_connector/atmos_init()
if(node)
return
- init_dir()
-
var/node_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
@@ -138,7 +131,7 @@
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (connected_device)
- user << "You cannot unwrench \the [src], dettach \the [connected_device] first."
+ to_chat(user, "You cannot unwrench \the [src], dettach \the [connected_device] first.")
return 1
if (locate(/obj/machinery/portable_atmospherics, src.loc))
return 1
@@ -147,7 +140,7 @@
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
index e9fe37f8a1..951e5a03de 100755
--- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
@@ -54,6 +54,10 @@
air2.volume = ATMOS_DEFAULT_VOLUME_FILTER
air3.volume = ATMOS_DEFAULT_VOLUME_FILTER
+/obj/machinery/atmospherics/trinary/atmos_filter/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/trinary/atmos_filter/update_icon()
if(istype(src, /obj/machinery/atmospherics/trinary/atmos_filter/m_filter))
icon_state = "m"
@@ -136,7 +140,7 @@
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
@@ -151,7 +155,7 @@
return
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
var/dat
@@ -247,9 +251,7 @@ obj/machinery/atmospherics/trinary/atmos_filter/m_filter/init_dir()
if(WEST)
initialize_directions = WEST|SOUTH|EAST
-/obj/machinery/atmospherics/trinary/atmos_filter/m_filter/initialize()
- set_frequency(frequency)
-
+/obj/machinery/atmospherics/trinary/atmos_filter/m_filter/atmos_init()
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -180)
diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
index e8a3c1be74..5739a57895 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
@@ -111,7 +111,7 @@
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
@@ -125,7 +125,7 @@
return
src.add_fingerprint(usr)
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
usr.set_machine(src)
var/dat = {"Power: [use_power?"On":"Off"]
@@ -192,7 +192,7 @@ obj/machinery/atmospherics/trinary/mixer/t_mixer/init_dir()
if(WEST)
initialize_directions = WEST|NORTH|SOUTH
-obj/machinery/atmospherics/trinary/mixer/t_mixer/initialize()
+obj/machinery/atmospherics/trinary/mixer/t_mixer/atmos_init()
..()
if(node1 && node2 && node3) return
@@ -237,7 +237,7 @@ obj/machinery/atmospherics/trinary/mixer/m_mixer/init_dir()
if(WEST)
initialize_directions = WEST|SOUTH|EAST
-obj/machinery/atmospherics/trinary/mixer/m_mixer/initialize()
+obj/machinery/atmospherics/trinary/mixer/m_mixer/atmos_init()
..()
if(node1 && node2 && node3) return
diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
index 34e6d480a8..807523fe7a 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
@@ -15,7 +15,6 @@
/obj/machinery/atmospherics/trinary/New()
..()
- init_dir()
air1 = new
air2 = new
@@ -71,31 +70,26 @@
node2 = null
node3 = null
-/obj/machinery/atmospherics/trinary/initialize()
+/obj/machinery/atmospherics/trinary/atmos_init()
if(node1 && node2 && node3)
return
- init_dir()
-
var/node1_connect = turn(dir, -180)
var/node2_connect = turn(dir, -90)
var/node3_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index 0c841a4ad3..09d2d5e424 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -46,10 +46,6 @@
/obj/machinery/atmospherics/tvalve/hide(var/i)
update_underlays()
-/obj/machinery/atmospherics/tvalve/New()
- init_dir()
- ..()
-
/obj/machinery/atmospherics/tvalve/init_dir()
switch(dir)
if(NORTH)
@@ -180,35 +176,29 @@
/obj/machinery/atmospherics/tvalve/process()
..()
. = PROCESS_KILL
- //machines.Remove(src)
return
-/obj/machinery/atmospherics/tvalve/initialize()
+/obj/machinery/atmospherics/tvalve/atmos_init()
var/node1_dir
var/node2_dir
var/node3_dir
- init_dir()
-
node1_dir = turn(dir, 180)
node2_dir = turn(dir, -90)
node3_dir = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
@@ -287,6 +277,10 @@
var/id = null
var/datum/radio_frequency/radio_connection
+/obj/machinery/atmospherics/tvalve/digital/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/tvalve/digital/bypass
icon_state = "map_tvalve1"
state = 1
@@ -309,7 +303,7 @@
if(!powered())
return
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
..()
@@ -351,14 +345,14 @@
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (istype(src, /obj/machinery/atmospherics/tvalve/digital))
- user << "You cannot unwrench \the [src], it's too complicated."
+ to_chat(user, "You cannot unwrench \the [src], it's too complicated.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
@@ -385,7 +379,7 @@
if(WEST)
initialize_directions = EAST|WEST|SOUTH
-/obj/machinery/atmospherics/tvalve/mirrored/initialize()
+/obj/machinery/atmospherics/tvalve/mirrored/atmos_init()
var/node1_dir
var/node2_dir
var/node3_dir
@@ -425,6 +419,10 @@
var/id = null
var/datum/radio_frequency/radio_connection
+/obj/machinery/atmospherics/tvalve/mirrored/digital/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/tvalve/mirrored/digital/bypass
icon_state = "map_tvalvem1"
state = 1
@@ -447,7 +445,7 @@
if(!powered())
return
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
..()
diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm
index c3b0000572..5759c39dcf 100644
--- a/code/ATMOSPHERICS/components/unary/cold_sink.dm
+++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm
@@ -23,7 +23,6 @@
/obj/machinery/atmospherics/unary/freezer/New()
..()
- initialize_directions = dir
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
@@ -32,7 +31,7 @@
component_parts += new /obj/item/stack/cable_coil(src, 2)
RefreshParts()
-/obj/machinery/atmospherics/unary/freezer/initialize()
+/obj/machinery/atmospherics/unary/freezer/atmos_init()
if(node)
return
diff --git a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm
index 3413aa04de..8a7fba153a 100644
--- a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm
+++ b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm
@@ -18,7 +18,7 @@
return
- initialize()
+ atmos_init()
if(!partner)
var/partner_connect = turn(dir,180)
@@ -70,14 +70,14 @@
return ..()
var/turf/T = src.loc
if (level==1 && isturf(T) && !T.is_plating())
- user << "You must remove the plating first."
+ to_chat(user, "You must remove the plating first.")
return 1
if (!can_unwrench())
- user << "You cannot unwrench \the [src], it is too exerted due to internal pressure."
+ to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm
index ddb2bf9a67..a1e933b96a 100644
--- a/code/ATMOSPHERICS/components/unary/heat_source.dm
+++ b/code/ATMOSPHERICS/components/unary/heat_source.dm
@@ -23,7 +23,6 @@
/obj/machinery/atmospherics/unary/heater/New()
..()
- initialize_directions = dir
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
@@ -32,7 +31,7 @@
RefreshParts()
-/obj/machinery/atmospherics/unary/heater/initialize()
+/obj/machinery/atmospherics/unary/heater/atmos_init()
if(node)
return
diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
index 3596d0e8cf..84780da425 100644
--- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm
+++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm
@@ -28,6 +28,10 @@
..()
air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more.
+/obj/machinery/atmospherics/unary/outlet_injector/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/unary/outlet_injector/update_icon()
if(!powered())
icon_state = "off"
diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm
index 327112d918..77b135cdc5 100644
--- a/code/ATMOSPHERICS/components/unary/unary_base.dm
+++ b/code/ATMOSPHERICS/components/unary/unary_base.dm
@@ -13,7 +13,6 @@
/obj/machinery/atmospherics/unary/New()
..()
- init_dir()
air_contents = new
air_contents.volume = 200
@@ -42,16 +41,13 @@
node = null
-/obj/machinery/atmospherics/unary/initialize()
+/obj/machinery/atmospherics/unary/atmos_init()
if(node)
return
- init_dir()
-
var/node_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm
index 1b271d1e4a..b5449fe737 100644
--- a/code/ATMOSPHERICS/components/unary/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm
@@ -106,8 +106,6 @@
/obj/machinery/atmospherics/unary/vent_pump/update_icon(var/safety = 0)
if(!check_icon_cache())
return
- if (!node)
- use_power = 0
overlays.Cut()
@@ -122,10 +120,10 @@
if(welded)
vent_icon += "weld"
- else if(!powered())
+ else if(!use_power || !node || (stat & (NOPOWER|BROKEN)))
vent_icon += "off"
else
- vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]"
+ vent_icon += "[pump_direction ? "out" : "in"]"
overlays += icon_manager.get_atmos_icon("device", , , vent_icon)
@@ -256,7 +254,7 @@
return 1
-/obj/machinery/atmospherics/unary/vent_pump/initialize()
+/obj/machinery/atmospherics/unary/vent_pump/atmos_init()
..()
//some vents work his own special way
@@ -357,7 +355,7 @@
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if (WT.remove_fuel(0,user))
- user << "Now welding the vent."
+ to_chat(user, "Now welding the vent.")
if(do_after(user, 20 * WT.toolspeed))
if(!src || !WT.isOn()) return
playsound(src.loc, WT.usesound, 50, 1)
@@ -370,9 +368,9 @@
welded = 0
update_icon()
else
- user << "The welding tool needs to be on to start this task."
+ to_chat(user, "The welding tool needs to be on to start this task.")
else
- user << "You need more welding fuel to complete this task."
+ to_chat(user, "You need more welding fuel to complete this task.")
return 1
else
..()
@@ -395,18 +393,18 @@
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (!(stat & NOPOWER) && use_power)
- user << "You cannot unwrench \the [src], turn it off first."
+ to_chat(user, "You cannot unwrench \the [src], turn it off first.")
return 1
var/turf/T = src.loc
if (node && node.level==1 && isturf(T) && !T.is_plating())
- user << "You must remove the plating first."
+ to_chat(user, "You must remove the plating first.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
index 8bb67bcc19..ad78f5a1f4 100644
--- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
@@ -119,7 +119,7 @@
return 1
-/obj/machinery/atmospherics/unary/vent_scrubber/initialize()
+/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init()
..()
radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null
radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null
@@ -266,18 +266,18 @@
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (!(stat & NOPOWER) && use_power)
- user << "You cannot unwrench \the [src], turn it off first."
+ to_chat(user, "You cannot unwrench \the [src], turn it off first.")
return 1
var/turf/T = src.loc
if (node && node.level==1 && isturf(T) && !T.is_plating())
- user << "You must remove the plating first."
+ to_chat(user, "You must remove the plating first.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm
index 9aaffa671e..3cee872f8b 100644
--- a/code/ATMOSPHERICS/components/valve.dm
+++ b/code/ATMOSPHERICS/components/valve.dm
@@ -140,8 +140,7 @@
return
-/obj/machinery/atmospherics/valve/initialize()
- init_dir()
+/obj/machinery/atmospherics/valve/atmos_init()
normalize_dir()
var/node1_dir
@@ -155,13 +154,11 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -232,6 +229,10 @@
var/id = null
var/datum/radio_frequency/radio_connection
+/obj/machinery/atmospherics/valve/digital/Destroy()
+ unregister_radio(src, frequency)
+ . = ..()
+
/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob)
return src.attack_hand(user)
@@ -239,7 +240,7 @@
if(!powered())
return
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
..()
@@ -292,14 +293,14 @@
if (!istype(W, /obj/item/weapon/wrench))
return ..()
if (istype(src, /obj/machinery/atmospherics/valve/digital) && !src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm
index 8441461342..74134e6ff2 100644
--- a/code/ATMOSPHERICS/datum_pipe_network.dm
+++ b/code/ATMOSPHERICS/datum_pipe_network.dm
@@ -1,6 +1,6 @@
-var/global/list/datum/pipe_network/pipe_networks = list()
+var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SSmachines
-datum/pipe_network
+/datum/pipe_network
var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network
var/volume = 0 //caches the total volume for atmos machines to use in gas calculations
@@ -11,13 +11,8 @@ datum/pipe_network
var/update = 1
//var/datum/gas_mixture/air_transient = null
- New()
- //air_transient = new()
-
- ..()
-
Destroy()
- pipe_networks -= src
+ STOP_PROCESSING_PIPENET(src)
for(var/datum/pipeline/line_member in line_members)
line_member.network = null
for(var/obj/machinery/atmospherics/normal_member in normal_members)
@@ -41,13 +36,14 @@ datum/pipe_network
if(!start_normal)
qdel(src)
+ return
start_normal.network_expand(src, reference)
update_network_gases()
if((normal_members.len>0)||(line_members.len>0))
- pipe_networks += src
+ START_PROCESSING_PIPENET(src)
else
qdel(src)
diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm
index 10cd0bec93..74cef7a172 100644
--- a/code/ATMOSPHERICS/datum_pipeline.dm
+++ b/code/ATMOSPHERICS/datum_pipeline.dm
@@ -16,7 +16,8 @@ datum/pipeline
temporarily_store_air()
for(var/obj/machinery/atmospherics/pipe/P in members)
P.parent = null
-
+ members = null
+ edges = null
. = ..()
proc/process()//This use to be called called from the pipe networks
diff --git a/code/ATMOSPHERICS/he_pipes.dm b/code/ATMOSPHERICS/he_pipes.dm
index 602edfcc6d..bff838cfdd 100644
--- a/code/ATMOSPHERICS/he_pipes.dm
+++ b/code/ATMOSPHERICS/he_pipes.dm
@@ -19,7 +19,6 @@
// BubbleWrap
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/New()
..()
- init_dir()
// BubbleWrap END
color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default.
@@ -27,8 +26,7 @@
..()
initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe
-/obj/machinery/atmospherics/pipe/simple/heat_exchanging/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/atmos_init()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -41,12 +39,10 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir))
- target.init_dir()
if(target.initialize_directions_he & get_dir(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir))
- target.init_dir()
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
@@ -75,20 +71,23 @@
else if(istype(loc, /turf/space/))
parent.radiate_heat_to_space(surface, 1)
- if(buckled_mob)
- var/hc = pipe_air.heat_capacity()
- var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500)
- pipe_air.temperature = avg_temp
- buckled_mob.bodytemperature = avg_temp
+ if(has_buckled_mobs())
+ for(var/M in buckled_mobs)
+ var/mob/living/L = M
- var/heat_limit = 1000
+ var/hc = pipe_air.heat_capacity()
+ var/avg_temp = (pipe_air.temperature * hc + L.bodytemperature * 3500) / (hc + 3500)
+ pipe_air.temperature = avg_temp
+ L.bodytemperature = avg_temp
- var/mob/living/carbon/human/H = buckled_mob
- if(istype(H) && H.species)
- heat_limit = H.species.heat_level_3
+ var/heat_limit = 1000
- if(pipe_air.temperature > heat_limit + 1)
- buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_TORSO, used_weapon = "Excessive Heat")
+ var/mob/living/carbon/human/H = L
+ if(istype(H) && H.species)
+ heat_limit = H.species.heat_level_3
+
+ if(pipe_air.temperature > heat_limit + 1)
+ L.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_TORSO, used_weapon = "Excessive Heat")
//fancy radiation glowing
if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K
@@ -136,15 +135,12 @@
initialize_directions_he = WEST
-/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/atmos_init()
for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he))
- target.init_dir()
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
diff --git a/code/ATMOSPHERICS/mainspipe.dm b/code/ATMOSPHERICS/mainspipe.dm
index a2976974bf..1b0c617564 100644
--- a/code/ATMOSPHERICS/mainspipe.dm
+++ b/code/ATMOSPHERICS/mainspipe.dm
@@ -101,7 +101,7 @@ obj/machinery/atmospherics/mains_pipe
disconnect()
..()
- initialize()
+ atmos_init()
for(var/i = 1 to nodes.len)
var/obj/machinery/atmospherics/mains_pipe/node = nodes[i]
if(node)
@@ -155,7 +155,7 @@ obj/machinery/atmospherics/mains_pipe/simple
var/have_node2 = nodes[2]?1:0
icon_state = "exposed[have_node1][have_node2][invisibility ? "-f" : "" ]"
- initialize()
+ atmos_init()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -203,7 +203,7 @@ obj/machinery/atmospherics/mains_pipe/manifold
..()
initialize_mains_directions = (NORTH|SOUTH|EAST|WEST) & ~dir
- initialize()
+ atmos_init()
var/connect_directions = initialize_mains_directions
for(var/direction in cardinal)
@@ -267,7 +267,7 @@ obj/machinery/atmospherics/mains_pipe/manifold4w
nodes.len = 4
..()
- initialize()
+ atmos_init()
for(var/obj/machinery/atmospherics/mains_pipe/target in get_step(src,NORTH))
if(target.initialize_mains_directions & get_dir(target,src))
nodes[1] = target
@@ -319,7 +319,7 @@ obj/machinery/atmospherics/mains_pipe/split
initialize_mains_directions = turn(dir, 90) | turn(dir, -90)
initialize_directions = dir // actually have a normal connection too
- initialize()
+ atmos_init()
var/node1_dir
var/node2_dir
var/node3_dir
@@ -420,7 +420,7 @@ obj/machinery/atmospherics/mains_pipe/split3
initialize_mains_directions = dir
initialize_directions = cardinal & ~dir // actually have a normal connection too
- initialize()
+ atmos_init()
var/node1_dir
var/supply_node_dir
var/scrubbers_node_dir
@@ -514,7 +514,7 @@ obj/machinery/atmospherics/mains_pipe/cap
update_icon()
icon_state = "cap[invisibility ? "-f" : ""]"
- initialize()
+ atmos_init()
for(var/obj/machinery/atmospherics/mains_pipe/target in get_step(src,dir))
if(target.initialize_mains_directions & get_dir(target,src))
nodes[1] = target
@@ -647,7 +647,7 @@ obj/machinery/atmospherics/mains_pipe/valve
attack_hand(mob/user as mob)
if(!src.allowed(user))
- user << "Access denied."
+ to_chat(user, "Access denied.")
return
..()
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index 757680c761..14693c311b 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -1,3 +1,6 @@
+//
+// Base type of pipes
+//
/obj/machinery/atmospherics/pipe
var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke
@@ -34,10 +37,6 @@
return 1
-// This is used to set up what directions pipes will connect to. Called inside New(), initialize(), and when pipes look at another pipe, incase they didn't get to initialize() yet.
-/obj/machinery/atmospherics/proc/init_dir()
- return
-
/obj/machinery/atmospherics/pipe/return_air()
if(!parent)
parent = new /datum/pipeline()
@@ -84,14 +83,14 @@
return ..()
var/turf/T = src.loc
if (level==1 && isturf(T) && !T.is_plating())
- user << "You must remove the plating first."
+ to_chat(user, "You must remove the plating first.")
return 1
if(!can_unwrench())
to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.")
add_fingerprint(user)
return 1
playsound(src, W.usesound, 50, 1)
- user << "You begin to unfasten \the [src]..."
+ to_chat(user, "You begin to unfasten \the [src]...")
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"\The [user] unfastens \the [src].", \
@@ -104,7 +103,7 @@
qdel(meter)
qdel(src)
-/obj/machinery/atmospherics/proc/change_color(var/new_color)
+/obj/machinery/atmospherics/pipe/proc/change_color(var/new_color)
//only pass valid pipe colors please ~otherwise your pipe will turn invisible
if(!pipe_color_check(new_color))
return
@@ -112,21 +111,6 @@
pipe_color = new_color
update_icon()
-/*
-/obj/machinery/atmospherics/pipe/add_underlay(var/obj/machinery/atmospherics/node, var/direction)
- if(istype(src, /obj/machinery/atmospherics/pipe/tank)) //todo: move tanks to unary devices
- return ..()
-
- if(node)
- var/temp_dir = get_dir(src, node)
- underlays += icon_manager.get_atmos_icon("pipe_underlay_intact", temp_dir, color_cache_name(node))
- return temp_dir
- else if(direction)
- underlays += icon_manager.get_atmos_icon("pipe_underlay_exposed", direction, pipe_color)
- else
- return null
-*/
-
/obj/machinery/atmospherics/pipe/color_cache_name(var/obj/machinery/atmospherics/node)
if(istype(src, /obj/machinery/atmospherics/pipe/tank))
return ..()
@@ -141,6 +125,20 @@
else
return pipe_color
+/obj/machinery/atmospherics/pipe/hide(var/i)
+ if(istype(loc, /turf/simulated))
+ invisibility = i ? 101 : 0
+ update_icon()
+
+/obj/machinery/atmospherics/pipe/process()
+ if(!parent) //This should cut back on the overhead calling build_network thousands of times per cycle
+ ..()
+ else
+ . = PROCESS_KILL
+
+//
+// Simple Pipes - Just a tube, maybe bent
+//
/obj/machinery/atmospherics/pipe/simple
icon = 'icons/atmos/pipes.dmi'
icon_state = ""
@@ -170,21 +168,6 @@
icon = null
alpha = 255
- init_dir()
-
-
-
-/obj/machinery/atmospherics/pipe/simple/hide(var/i)
- if(istype(loc, /turf/simulated))
- invisibility = i ? 101 : 0
- update_icon()
-
-/obj/machinery/atmospherics/pipe/simple/process()
- if(!parent) //This should cut back on the overhead calling build_network thousands of times per cycle
- ..()
- else
- . = PROCESS_KILL
-
/obj/machinery/atmospherics/pipe/simple/check_pressure(pressure)
var/datum/gas_mixture/environment = loc.return_air()
@@ -274,8 +257,7 @@
/obj/machinery/atmospherics/pipe/simple/update_underlays()
return
-/obj/machinery/atmospherics/pipe/simple/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/simple/atmos_init()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -288,13 +270,11 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -422,7 +402,9 @@
level = 2
-
+//
+// Manifold Pipes - Three way "T" joints
+//
/obj/machinery/atmospherics/pipe/manifold
icon = 'icons/atmos/manifold.dmi'
icon_state = ""
@@ -444,8 +426,6 @@
alpha = 255
icon = null
- init_dir()
-
/obj/machinery/atmospherics/pipe/manifold/init_dir()
switch(dir)
if(NORTH)
@@ -457,20 +437,9 @@
if(WEST)
initialize_directions = NORTH|EAST|SOUTH
-/obj/machinery/atmospherics/pipe/manifold/hide(var/i)
- if(istype(loc, /turf/simulated))
- invisibility = i ? 101 : 0
- update_icon()
-
/obj/machinery/atmospherics/pipe/manifold/pipeline_expansion()
return list(node1, node2, node3)
-/obj/machinery/atmospherics/pipe/manifold/process()
- if(!parent)
- ..()
- else
- . = PROCESS_KILL
-
/obj/machinery/atmospherics/pipe/manifold/Destroy()
if(node1)
node1.disconnect(src)
@@ -554,14 +523,12 @@
..()
update_icon()
-/obj/machinery/atmospherics/pipe/manifold/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/manifold/atmos_init()
var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir)
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -574,7 +541,6 @@
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -587,7 +553,6 @@
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
@@ -691,6 +656,10 @@
/obj/machinery/atmospherics/pipe/manifold/hidden/purple
color = PIPE_COLOR_PURPLE
+
+//
+// 4-Way Manifold Pipes - 4 way "cross" junction
+//
/obj/machinery/atmospherics/pipe/manifold4w
icon = 'icons/atmos/manifold.dmi'
icon_state = ""
@@ -716,12 +685,6 @@
/obj/machinery/atmospherics/pipe/manifold4w/pipeline_expansion()
return list(node1, node2, node3, node4)
-/obj/machinery/atmospherics/pipe/manifold4w/process()
- if(!parent)
- ..()
- else
- . = PROCESS_KILL
-
/obj/machinery/atmospherics/pipe/manifold4w/Destroy()
if(node1)
node1.disconnect(src)
@@ -830,36 +793,27 @@
..()
update_icon()
-/obj/machinery/atmospherics/pipe/manifold4w/hide(var/i)
- if(istype(loc, /turf/simulated))
- invisibility = i ? 101 : 0
- update_icon()
-
-/obj/machinery/atmospherics/pipe/manifold4w/initialize()
+/obj/machinery/atmospherics/pipe/manifold4w/atmos_init()
for(var/obj/machinery/atmospherics/target in get_step(src,1))
- target.init_dir()
if(target.initialize_directions & 2)
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,2))
- target.init_dir()
if(target.initialize_directions & 1)
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,4))
- target.init_dir()
if(target.initialize_directions & 8)
if (check_connect_types(target,src))
node3 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,8))
- target.init_dir()
if(target.initialize_directions & 4)
if (check_connect_types(target,src))
node4 = target
@@ -960,6 +914,9 @@
/obj/machinery/atmospherics/pipe/manifold4w/hidden/purple
color = PIPE_COLOR_PURPLE
+//
+// Pipe Cap - They go on the end
+//
/obj/machinery/atmospherics/pipe/cap
name = "pipe endcap"
desc = "An endcap for pipes"
@@ -975,26 +932,12 @@
var/obj/machinery/atmospherics/node
-/obj/machinery/atmospherics/pipe/cap/New()
- ..()
- init_dir()
-
/obj/machinery/atmospherics/pipe/cap/init_dir()
initialize_directions = dir
-/obj/machinery/atmospherics/pipe/cap/hide(var/i)
- if(istype(loc, /turf/simulated))
- invisibility = i ? 101 : 0
- update_icon()
-
/obj/machinery/atmospherics/pipe/cap/pipeline_expansion()
return list(node)
-/obj/machinery/atmospherics/pipe/cap/process()
- if(!parent)
- ..()
- else
- . = PROCESS_KILL
/obj/machinery/atmospherics/pipe/cap/Destroy()
if(node)
node.disconnect(src)
@@ -1027,10 +970,8 @@
overlays.Cut()
overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, "cap")
-/obj/machinery/atmospherics/pipe/cap/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/cap/atmos_init()
for(var/obj/machinery/atmospherics/target in get_step(src, dir))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
@@ -1088,7 +1029,9 @@
icon_connect_type = "-supply"
color = PIPE_COLOR_BLUE
-
+//
+// Tanks - These are implemented as pipes with large volume
+//
/obj/machinery/atmospherics/pipe/tank
icon = 'icons/atmos/tank_vr.dmi' //VOREStation Edit - New Icons
icon_state = "air_map"
@@ -1106,18 +1049,11 @@
/obj/machinery/atmospherics/pipe/tank/New()
icon_state = "air"
- init_dir()
..()
/obj/machinery/atmospherics/pipe/tank/init_dir()
initialize_directions = dir
-/obj/machinery/atmospherics/pipe/tank/process()
- if(!parent)
- ..()
- else
- . = PROCESS_KILL
-
/obj/machinery/atmospherics/pipe/tank/Destroy()
if(node1)
node1.disconnect(src)
@@ -1139,12 +1075,10 @@
/obj/machinery/atmospherics/pipe/tank/hide()
update_underlays()
-/obj/machinery/atmospherics/pipe/tank/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/tank/atmos_init()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -1257,6 +1191,9 @@
..()
icon_state = "n2o"
+//
+// Vent Pipe - Unpowered vent
+//
/obj/machinery/atmospherics/pipe/vent
icon = 'icons/obj/atmospherics/pipe_vent.dmi'
icon_state = "intact"
@@ -1273,10 +1210,6 @@
var/build_killswitch = 1
-/obj/machinery/atmospherics/pipe/vent/New()
- init_dir()
- ..()
-
/obj/machinery/atmospherics/pipe/vent/init_dir()
initialize_directions = dir
@@ -1314,12 +1247,10 @@
else
icon_state = "exposed"
-/obj/machinery/atmospherics/pipe/vent/initialize()
- init_dir()
+/obj/machinery/atmospherics/pipe/vent/atmos_init()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
- target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -1344,7 +1275,9 @@
else
icon_state = "exposed"
-
+//
+// Universal Pipe Adapter - Designed for connecting scrubbers, normal, and supply pipes together.
+//
/obj/machinery/atmospherics/pipe/simple/visible/universal
name="Universal pipe adapter"
desc = "An adapter for regular, supply and scrubbers pipes"
diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
index 791e17de20..9688fabf72 100644
--- a/code/ZAS/Controller.dm
+++ b/code/ZAS/Controller.dm
@@ -128,6 +128,16 @@ Total Active Edges: [active_edges.len ? "[active_edges.len]
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]
"}, R_DEBUG)
+ // Uncomment this if you're having problems finding where active edges are.
+ /*
+ for(var/connection_edge/E in active_edges)
+ world << "Edge became active: [E]."
+ var/i = 1
+ for(var/turf/T in E.connecting_turfs)
+ world << "[i] [T]:[T.x],[T.y],[T.z]"
+ i++
+ */
+
// spawn Start()
diff --git a/code/ZAS/Phoron.dm b/code/ZAS/Phoron.dm
index 1f7ed8c04a..0f39ab4b56 100644
--- a/code/ZAS/Phoron.dm
+++ b/code/ZAS/Phoron.dm
@@ -76,7 +76,8 @@ obj/var/phoronproof = 0
suit_contamination()
if(!pl_head_protected())
- if(prob(1)) suit_contamination() //Phoron can sometimes get through such an open suit.
+ if(prob(1))
+ suit_contamination() //Phoron can sometimes get through such an open suit.
//Cannot wash backpacks currently.
// if(istype(back,/obj/item/weapon/storage/backpack))
@@ -88,7 +89,8 @@ obj/var/phoronproof = 0
//Handles all the bad things phoron can do.
//Contamination
- if(vsc.plc.CLOTH_CONTAMINATION) contaminate()
+ if(vsc.plc.CLOTH_CONTAMINATION)
+ contaminate()
//Anything else requires them to not be dead.
if(stat >= 2)
@@ -143,22 +145,22 @@ obj/var/phoronproof = 0
Blind(20)
/mob/living/carbon/human/proc/pl_head_protected()
- //Checks if the head is adequately sealed.
+ //Checks if the head is adequately sealed. //This is just odd. TODO: Make this respect the body_parts_covered stuff like thermal gear does.
if(head)
if(vsc.plc.PHORONGUARD_ONLY)
- if(head.flags & PHORONGUARD)
+ if(head.flags & PHORONGUARD || head.phoronproof)
return 1
else if(head.body_parts_covered & EYES)
return 1
return 0
/mob/living/carbon/human/proc/pl_suit_protected()
- //Checks if the suit is adequately sealed.
+ //Checks if the suit is adequately sealed. //This is just odd. TODO: Make this respect the body_parts_covered stuff like thermal gear does.
var/coverage = 0
- for(var/obj/item/protection in list(wear_suit, gloves, shoes))
+ for(var/obj/item/protection in list(wear_suit, gloves, shoes)) //This is why it's odd. If I'm in a full suit, but my shoes and gloves aren't phoron proof, damage.
if(!protection)
continue
- if(vsc.plc.PHORONGUARD_ONLY && !(protection.flags & PHORONGUARD))
+ if(vsc.plc.PHORONGUARD_ONLY && !(protection.flags & PHORONGUARD) && !protection.phoronproof)
return 0
coverage |= protection.body_parts_covered
@@ -169,9 +171,12 @@ obj/var/phoronproof = 0
/mob/living/carbon/human/proc/suit_contamination()
//Runs over the things that can be contaminated and does so.
- if(w_uniform) w_uniform.contaminate()
- if(shoes) shoes.contaminate()
- if(gloves) gloves.contaminate()
+ if(w_uniform)
+ w_uniform.contaminate()
+ if(shoes)
+ shoes.contaminate()
+ if(gloves)
+ gloves.contaminate()
turf/Entered(obj/item/I)
diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm
index fb15a693bf..5620f51b3b 100644
--- a/code/__defines/MC.dm
+++ b/code/__defines/MC.dm
@@ -1,15 +1,28 @@
-#define MC_TICK_CHECK ( ( world.tick_usage > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
+#define MC_TICK_CHECK ( ( TICK_USAGE > Master.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 = Master.current_ticklimit; var/split_tick_phases = ##phase_count
#define MC_SPLIT_TICK \
if(split_tick_phases > 1){\
- Master.current_ticklimit = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
+ Master.current_ticklimit = ((original_tick_limit - TICK_USAGE) / split_tick_phases) + TICK_USAGE;\
--split_tick_phases;\
} else {\
Master.current_ticklimit = original_tick_limit;\
}
+// 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)\
+if(current_step == this_step || (initial_step && !resumed)) /* So we start at step 1 if not resumed.*/ {\
+ timer = TICK_USAGE;\
+ proc_to_call(resumed);\
+ cost_var = MC_AVERAGE(cost_var, TICK_DELTA_TO_MS(TICK_USAGE - timer));\
+ if(state != SS_RUNNING){\
+ return;\
+ }\
+ 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))
diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm
index ac5f7120fd..3190e391a4 100644
--- a/code/__defines/_compile_options.dm
+++ b/code/__defines/_compile_options.dm
@@ -1,2 +1,14 @@
#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary.
- // 1 will enable set background. 0 will disable set background.
\ No newline at end of file
+ // 1 will enable set background. 0 will disable set background.
+
+#define PRELOAD_RSC 1 /*set to:
+ 0 to allow using external resources or on-demand behaviour;
+ 1 to use the default behaviour (preload compiled in recourses, not player uploaded ones);
+ 2 for preloading absolutely everything;
+ */
+
+// If we are doing the map test build, do not include the main maps, only the submaps.
+#if MAP_TEST
+ #define USING_MAP_DATUM /datum/map
+ #define MAP_OVERRIDE 1
+#endif
diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 129236125c..baff5ad577 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -40,15 +40,83 @@ What is the naming convention for planes or layers?
*/
-#define DEFAULT_PLANE 0 // BYOND's default value for plane, the "base plane"
+#define PLANE_ADMIN1 -92 //Purely for shenanigans
+#define PLANE_ADMIN2 -91 //And adminbuse
+#define PLANE_ADMIN3 -90 //And generating salt
-#define SPACE_PLANE -32 // Reserved for use in space/parallax
-
-#define PARALLAX_PLANE -30 // Reserved for use in space/parallax
+#define SPACE_PLANE -32 // Reserved for use in space/parallax
+#define PARALLAX_PLANE -30 // Reserved for use in space/parallax
// OPENSPACE_PLANE reserves all planes between OPENSPACE_PLANE_START and OPENSPACE_PLANE_END inclusive
-#define OPENSPACE_PLANE_START -23
-#define OPENSPACE_PLANE_END -8
-#define OPENSPACE_PLANE -25 // /turf/simulated/open will use OPENSPACE_PLANE + z (Valid z's being 2 thru 17)
+#define OPENSPACE_PLANE -55 // /turf/simulated/open will use OPENSPACE_PLANE + z (Valid z's being 2 thru 17)
+#define OPENSPACE_PLANE_START -53
+#define OPENSPACE_PLANE_END -38
+#define OVER_OPENSPACE_PLANE -37
-#define OVER_OPENSPACE_PLANE -7
+////////////////////////////////////////////////////////////////////////////////////////
+#define PLANE_WORLD 0 // BYOND's default value for plane, the "base plane"
+////////////////////////////////////////////////////////////////////////////////////////
+
+ //#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define
+ #define DOOR_OPEN_LAYER 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6
+ //#define OBJ_LAYER 3 //For easy recordkeeping; this is a byond define
+ #define DOOR_CLOSED_LAYER 3.1 //Above most items if closed
+ #define SHOWER_OPEN_LAYER 3.4
+ #define BELOW_MOB_LAYER 3.9
+ //#define MOB_LAYER 4 //For easy recordkeeping; this is a byond define
+ #define ABOVE_MOB_LAYER 4.1
+ #define SHOWER_CLOSED_LAYER 4.2
+
+ //#define FLY_LAYER 5 //For easy recordkeeping; this is a byond define
+ #define LIGHTING_LAYER 11 //Layer that lighting used to be on (now it's on a plane)
+ #define HUD_LAYER 20 //Above lighting, but below obfuscation. For in-game HUD effects (whereas SCREEN_LAYER is for abstract/OOC things like inventory slots)
+ #define OBFUSCATION_LAYER 21 //Where images covering the view for eyes are put
+ #define SCREEN_LAYER 22 //Mob HUD/effects layer
+
+#define PLANE_LIGHTING 5 //Where the lighting (and darkness) lives
+#define PLANE_LIGHTING_ABOVE 6 //For glowy eyes etc. that shouldn't be affected by darkness
+
+#define PLANE_GHOSTS 10 //Spooooooooky ghooooooosts
+#define PLANE_AI_EYE 11 //The AI eye lives here
+
+// "Character HUDs", aka HUDs, but not the game's UI. Things like medhuds. I know Planes say they must be intergers, but it's lies.
+#define PLANE_CH_STATUS 15 //Status icon
+#define PLANE_CH_HEALTH 16 //Health icon
+#define PLANE_CH_LIFE 17 //Health bar
+#define PLANE_CH_ID 18 //Job icon
+#define PLANE_CH_WANTED 19 //Arrest icon
+#define PLANE_CH_IMPLOYAL 20 //Loyalty implant icon
+#define PLANE_CH_IMPTRACK 21 //Tracking implant icon
+#define PLANE_CH_IMPCHEM 22 //Chemical implant icon
+#define PLANE_CH_SPECIAL 23 //Special role icon (revhead or w/e)
+#define PLANE_CH_STATUS_OOC 24 //OOC status hud for spooks
+
+
+//Fullscreen overlays under inventory
+#define PLANE_FULLSCREEN 90 //Blindness, mesons, druggy, etc
+ #define FULLSCREEN_LAYER 18
+ #define DAMAGE_LAYER 18.1
+ #define BLIND_LAYER 18.2
+ #define CRIT_LAYER 18.3
+
+//Client UI HUD stuff
+#define PLANE_PLAYER_HUD 95 //The character's UI is on this plane
+ #define LAYER_HUD_UNDER 1 //Under the HUD items
+ #define LAYER_HUD_BASE 2 //The HUD items themselves
+ #define LAYER_HUD_ITEM 3 //Things sitting on HUD items (largely irrelevant because PLANE_PLAYER_HUD_ITEMS)
+ #define LAYER_HUD_ABOVE 4 //Things that reside above items (highlights)
+#define PLANE_PLAYER_HUD_ITEMS 96 //Separate layer with which to apply colorblindness
+
+
+//////////////////////////
+/atom/proc/hud_layerise()
+ plane = PLANE_PLAYER_HUD_ITEMS
+ layer = LAYER_HUD_ITEM
+
+/atom/proc/reset_plane_and_layer()
+ plane = initial(plane)
+ layer = initial(layer)
+
+
+// Check if a mob can "logically" see an atom plane
+#define MOB_CAN_SEE_PLANE(M, P) (P == PLANE_WORLD || (P >= OPENSPACE_PLANE_START && P <= OPENSPACE_PLANE_END) || (P in M.planes_visible))
diff --git a/code/__defines/_planes+layers_vr.dm b/code/__defines/_planes+layers_vr.dm
new file mode 100644
index 0000000000..744f299224
--- /dev/null
+++ b/code/__defines/_planes+layers_vr.dm
@@ -0,0 +1,9 @@
+// "Character HUDs", aka HUDs, but not the game's UI. Things like medhuds.
+#define PLANE_CH_HEALTH_VR 27 //Hidden healthbar when at full health
+#define PLANE_CH_STATUS_R 28 //Right-side status icon
+#define PLANE_CH_BACKUP 29 //Backup implant
+#define PLANE_CH_VANTAG 30 //Vore Antag hud
+
+#define PLANE_AUGMENTED 40 //Augmented-reality plane
+
+#define ABOVE_WINDOW_LAYER 3.25 //Above full tile windows so wall items are clickable
diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm
new file mode 100644
index 0000000000..2c761b86f9
--- /dev/null
+++ b/code/__defines/_tick.dm
@@ -0,0 +1,9 @@
+#define TICK_LIMIT_RUNNING 80
+#define TICK_LIMIT_TO_RUN 70
+#define TICK_LIMIT_MC 70
+#define TICK_LIMIT_MC_INIT_DEFAULT 98
+
+#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/construction.dm b/code/__defines/construction.dm
new file mode 100644
index 0000000000..7aceb1ffc4
--- /dev/null
+++ b/code/__defines/construction.dm
@@ -0,0 +1,18 @@
+
+
+// Frame construction states
+#define FRAME_PLACED 0 // Has been placed (can be anchored or not).
+#define FRAME_UNFASTENED 1 // Circuit added.
+#define FRAME_FASTENED 2 // Circuit fastened.
+#define FRAME_WIRED 3 // Frame wired.
+#define FRAME_PANELED 4 // Glass panel added.
+
+// The frame classes define a sequence of construction steps.
+#define FRAME_CLASS_ALARM "alarm"
+#define FRAME_CLASS_COMPUTER "computer"
+#define FRAME_CLASS_DISPLAY "display"
+#define FRAME_CLASS_MACHINE "machine"
+
+// Does the frame get built on the floor or a wall?
+#define FRAME_STYLE_FLOOR "floor"
+#define FRAME_STYLE_WALL "wall"
diff --git a/code/__defines/items_clothing.dm b/code/__defines/items_clothing.dm
index 7c80895a81..f857a8f232 100644
--- a/code/__defines/items_clothing.dm
+++ b/code/__defines/items_clothing.dm
@@ -57,6 +57,7 @@
#define PASSTABLE 0x1
#define PASSGLASS 0x2
#define PASSGRILLE 0x4
+#define PASSBLOB 0x8
// Bitmasks for the flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses.
// WARNING: The following flags apply only to the external suit!
diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm
index f311ea4aa2..6b05896b8d 100644
--- a/code/__defines/lighting.dm
+++ b/code/__defines/lighting.dm
@@ -9,7 +9,7 @@
#define LIGHTING_LAMBERTIAN 0 // use lambertian shading for light sources
#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone
-#define LIGHTING_LAYER 10 // drawing layer for lighting overlays
+//#define LIGHTING_LAYER 10 // drawing layer for lighting overlays
#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects
#define LIGHTING_ICON_STATE_DARK "soft_dark" // Change between "soft_dark" and "dark" to swap soft darkvision
@@ -49,3 +49,28 @@
#define CL_MATRIX_CG 18
#define CL_MATRIX_CB 19
#define CL_MATRIX_CA 20
+
+//Some defines to generalise colours used in lighting.
+//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated
+#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130)
+#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100)
+#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250)
+
+#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175)
+#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225)
+#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255)
+#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248)
+#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225)
+#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125)
+#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50)
+#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50)
+#define LIGHT_COLOR_PURPLE "#952CF4" //Light Purple. rgb(149, 44, 244)
+#define LIGHT_COLOR_LAVENDER "#9B51FF" //Less-saturated light purple. rgb(155, 81, 255)
+
+//These ones aren't a direct colour like the ones above, because nothing would fit
+#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25)
+#define LIGHT_COLOR_LAVA "#C48A18" //Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24)
+#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75)
+#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75)
+#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175)
+#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250)
\ No newline at end of file
diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index deaaec0fad..b577225e01 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -105,3 +105,37 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define ATMOS_DEFAULT_VOLUME_FILTER 200 // L.
#define ATMOS_DEFAULT_VOLUME_MIXER 200 // L.
#define ATMOS_DEFAULT_VOLUME_PIPE 70 // L.
+
+// Fancy-pants START/STOP_PROCESSING() macros that lets us custom define what the list is.
+#define START_PROCESSING_IN_LIST(DATUM, LIST) \
+if (DATUM.is_processing) {\
+ if(DATUM.is_processing != #LIST)\
+ {\
+ crash_with("Failed to start processing. [log_info_line(DATUM)] is already being processed by [DATUM.is_processing] but queue attempt occured on [#LIST]."); \
+ }\
+} else {\
+ DATUM.is_processing = #LIST;\
+ LIST += DATUM;\
+}
+
+#define STOP_PROCESSING_IN_LIST(DATUM, LIST) \
+if(DATUM.is_processing) {\
+ if(LIST.Remove(DATUM)) {\
+ DATUM.is_processing = null;\
+ } else {\
+ crash_with("Failed to stop processing. [log_info_line(DATUM)] is being processed by [is_processing] and not found in SSmachines.[#LIST]"); \
+ }\
+}
+
+// Note - I would prefer these be defined machines.dm, but some are used prior in file order. ~Leshana
+#define START_MACHINE_PROCESSING(Datum) START_PROCESSING_IN_LIST(Datum, global.machines)
+#define STOP_MACHINE_PROCESSING(Datum) STOP_PROCESSING_IN_LIST(Datum, global.machines)
+
+#define START_PROCESSING_PIPENET(Datum) START_PROCESSING_IN_LIST(Datum, global.pipe_networks)
+#define STOP_PROCESSING_PIPENET(Datum) STOP_PROCESSING_IN_LIST(Datum, global.pipe_networks)
+
+#define START_PROCESSING_POWERNET(Datum) START_PROCESSING_IN_LIST(Datum, global.powernets)
+#define STOP_PROCESSING_POWERNET(Datum) STOP_PROCESSING_IN_LIST(Datum, global.powernets)
+
+#define START_PROCESSING_POWER_OBJECT(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_power_items)
+#define STOP_PROCESSING_POWER_OBJECT(Datum) STOP_PROCESSING_IN_LIST(Datum, global.processing_power_items)
diff --git a/code/__defines/map.dm b/code/__defines/map.dm
index 9419415e1c..13b1bfd795 100644
--- a/code/__defines/map.dm
+++ b/code/__defines/map.dm
@@ -6,3 +6,6 @@
#define MAP_LEVEL_SEALED 0x010 // Z-levels that don't allow random transit at edge
#define MAP_LEVEL_EMPTY 0x020 // Empty Z-levels that may be used for various things (currently used by bluespace jump)
#define MAP_LEVEL_CONSOLES 0x040 // Z-levels available to various consoles, such as the crew monitor (when that gets coded in). Defaults to station_levels if unset.
+
+// Misc map defines.
+#define SUBMAP_MAP_EDGE_PAD 15 // Automatically created submaps are forbidden from being this close to the main map's edge.
\ No newline at end of file
diff --git a/code/__defines/math.dm b/code/__defines/math.dm
index 0729a4526d..79dc1b4d24 100644
--- a/code/__defines/math.dm
+++ b/code/__defines/math.dm
@@ -2,9 +2,13 @@
//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(world.tick_usage-starting_tickusage))
+#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(TICK_USAGE-starting_tickusage))
//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.
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
#define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers )
+
+#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) )
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index 2caf39c992..3a8ee870e5 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -87,7 +87,6 @@
#define SHUTTLE_IDLE 0
#define SHUTTLE_WARMUP 1
#define SHUTTLE_INTRANSIT 2
-#define SHUTTLE_CRASHED 3 // VOREStation Edit - Yup that can happen now
// Sound defines for shuttles.
#define HYPERSPACE_WARMUP 0
@@ -121,18 +120,6 @@
//Area flags, possibly more to come
#define RAD_SHIELDED 1 //shielded from radiation, clearly
-// VOREStation Edit Begin
-#define BLUE_SHIELDED 2 // shield from bluespace teleportation (telescience)
-// VOREStation Edit End
-
-// Custom layer definitions, supplementing the default TURF_LAYER, MOB_LAYER, etc.
-#define DOOR_OPEN_LAYER 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6
-#define DOOR_CLOSED_LAYER 3.1 //Above most items if closed
-#define LIGHTING_LAYER 11
-#define HUD_LAYER 20 //Above lighting, but below obfuscation. For in-game HUD effects (whereas SCREEN_LAYER is for abstract/OOC things like inventory slots)
-#define OBFUSCATION_LAYER 21 //Where images covering the view for eyes are put
-#define SCREEN_LAYER 22 //Mob HUD/effects layer
-#define ABOVE_WINDOW_LAYER 3.25 //Above full tile windows so wall items are clickable // VOREStation Edit
// Convoluted setup so defines can be supplied by Bay12 main server compile script.
// Should still work fine for people jamming the icons into their repo.
@@ -159,6 +146,11 @@
#define MAT_TITANIUM "titanium"
#define MAT_PHORON "phoron"
#define MAT_DIAMOND "diamond"
+#define MAT_SNOW "snow"
+#define MAT_WOOD "wood"
+#define MAT_LOG "log"
+#define MAT_SIFWOOD "alien wood"
+#define MAT_SIFLOG "alien log"
#define SHARD_SHARD "shard"
#define SHARD_SHRAPNEL "shrapnel"
@@ -226,4 +218,22 @@
#define MAP_MINZ 3
#define MAP_MAXX 4
#define MAP_MAXY 5
-#define MAP_MAXZ 6
\ No newline at end of file
+#define MAP_MAXZ 6
+
+// /atom/proc/use_check flags
+#define USE_ALLOW_NONLIVING 1
+#define USE_ALLOW_NON_ADV_TOOL_USR 2
+#define USE_ALLOW_DEAD 4
+#define USE_ALLOW_INCAPACITATED 8
+#define USE_ALLOW_NON_ADJACENT 16
+#define USE_FORCE_SRC_IN_USER 32
+#define USE_DISALLOW_SILICONS 64
+
+#define USE_SUCCESS 0
+#define USE_FAIL_NON_ADJACENT 1
+#define USE_FAIL_NONLIVING 2
+#define USE_FAIL_NON_ADV_TOOL_USR 3
+#define USE_FAIL_DEAD 4
+#define USE_FAIL_INCAPACITATED 5
+#define USE_FAIL_NOT_IN_USER 6
+#define USE_FAIL_IS_SILICON 7
\ No newline at end of file
diff --git a/code/__defines/misc_vr.dm b/code/__defines/misc_vr.dm
index e47a6492eb..ed6d8e8031 100644
--- a/code/__defines/misc_vr.dm
+++ b/code/__defines/misc_vr.dm
@@ -24,3 +24,8 @@
#define MR_NORMAL 0
#define MR_UNSURE 1
#define MR_DEAD 2
+
+//Shuttle madness!
+#define SHUTTLE_CRASHED 3 // Yup that can happen now
+
+#define BLUE_SHIELDED 2 // Shield from bluespace teleportation (telescience)
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 9ebf60632c..cb0ecc37f5 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -34,8 +34,9 @@
#define STANCE_FOLLOW 6 // Following somone
#define STANCE_BUSY 7 // Do nothing on life ticks (Other code is running)
-#define LEFT 1
-#define RIGHT 2
+#define LEFT 0x1
+#define RIGHT 0x2
+#define UNDER 0x4
// Pulse levels, very simplified.
#define PULSE_NONE 0 // So !M.pulse checks would be possible.
@@ -101,14 +102,14 @@
#define INV_SUIT_DEF_ICON 'icons/mob/suit.dmi'
#define MAX_SUPPLIED_LAW_NUMBER 50
-// NT's alignment towards the character
-#define COMPANY_LOYAL "Loyal"
-#define COMPANY_SUPPORTATIVE "Supportive"
-#define COMPANY_NEUTRAL "Neutral"
-#define COMPANY_SKEPTICAL "Skeptical"
-#define COMPANY_OPPOSED "Opposed"
+// Character's economic class
+#define CLASS_UPPER "Wealthy"
+#define CLASS_UPMID "Well-off"
+#define CLASS_MIDDLE "Average"
+#define CLASS_LOWMID "Underpaid"
+#define CLASS_LOWER "Poor"
-#define COMPANY_ALIGNMENTS list(COMPANY_LOYAL,COMPANY_SUPPORTATIVE,COMPANY_NEUTRAL,COMPANY_SKEPTICAL,COMPANY_OPPOSED)
+#define ECONOMIC_CLASS list(CLASS_UPPER,CLASS_UPMID,CLASS_MIDDLE,CLASS_LOWMID,CLASS_LOWER)
// Defines mob sizes, used by lockers and to determine what is considered a small sized mob, etc.
@@ -124,10 +125,12 @@
#define TINT_HEAVY 2
#define TINT_BLIND 3
+#define FLASH_PROTECTION_VULNERABLE -2
#define FLASH_PROTECTION_REDUCED -1
#define FLASH_PROTECTION_NONE 0
#define FLASH_PROTECTION_MODERATE 1
#define FLASH_PROTECTION_MAJOR 2
+
#define ANIMAL_SPAWN_DELAY round(config.respawn_delay / 6)
#define DRONE_SPAWN_DELAY round(config.respawn_delay / 3)
@@ -224,4 +227,29 @@
// 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.
\ No newline at end of file
+#define SLIME_COMMAND_FRIEND 3 // When befriended with a slime friendship agent.
+
+//Vision flags, for dealing with plane visibility
+#define VIS_FULLBRIGHT 1
+#define VIS_GHOSTS 2
+#define VIS_AI_EYE 3
+
+#define VIS_CH_STATUS 4
+#define VIS_CH_HEALTH 5
+#define VIS_CH_LIFE 6
+#define VIS_CH_ID 7
+#define VIS_CH_WANTED 8
+#define VIS_CH_IMPLOYAL 9
+#define VIS_CH_IMPTRACK 10
+#define VIS_CH_IMPCHEM 11
+#define VIS_CH_SPECIAL 12
+#define VIS_CH_STATUS_OOC 13
+
+#define VIS_D_COLORBLIND 14
+#define VIS_D_COLORBLINDI 15
+
+#define VIS_ADMIN1 16
+#define VIS_ADMIN2 17
+#define VIS_ADMIN3 18
+
+#define VIS_COUNT 18 //Must be highest number from above.
\ No newline at end of file
diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm
new file mode 100644
index 0000000000..9209b86159
--- /dev/null
+++ b/code/__defines/mobs_vr.dm
@@ -0,0 +1,10 @@
+#undef VIS_COUNT
+
+#define VIS_CH_STATUS_R 18
+#define VIS_CH_HEALTH_VR 19
+#define VIS_CH_BACKUP 20
+#define VIS_CH_VANTAG 21
+
+#define VIS_AUGMENTED 22
+
+#define VIS_COUNT 22
\ No newline at end of file
diff --git a/code/__defines/nifsoft.dm b/code/__defines/nifsoft.dm
index 2772aadede..dc03b2b411 100644
--- a/code/__defines/nifsoft.dm
+++ b/code/__defines/nifsoft.dm
@@ -38,9 +38,10 @@
#define NIF_COMPLIANCE 31
#define NIF_SIZECHANGE 32
#define NIF_SOULCATCHER 33
+#define NIF_WORLDBEND 34
// Must be equal to the highest number above
-#define TOTAL_NIF_SOFTWARE 33
+#define TOTAL_NIF_SOFTWARE 34
//////////////////////
// NIF flag list hints
diff --git a/code/__defines/process_scheduler.dm b/code/__defines/process_scheduler.dm
index f1e30c23ed..4bc841022f 100644
--- a/code/__defines/process_scheduler.dm
+++ b/code/__defines/process_scheduler.dm
@@ -15,4 +15,4 @@
#define PROCESS_DEFAULT_DEFER_USAGE 90 // 90% of a tick
// Sleep check macro
-#define SCHECK if(world.tick_usage >= next_sleep_usage) defer()
+#define SCHECK if(TICK_USAGE >= next_sleep_usage) defer()
diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm
index 168a6adf0c..f50c012ce3 100644
--- a/code/__defines/qdel.dm
+++ b/code/__defines/qdel.dm
@@ -9,9 +9,14 @@
//if TESTING is enabled, qdel will call this object's find_references() verb.
//defines for the gc_destroyed var
+#define GC_QUEUE_PREQUEUE 1
+#define GC_QUEUE_CHECK 2
+#define GC_QUEUE_HARDDELETE 3
+#define GC_QUEUE_COUNT 3 //increase this when adding more steps.
+
#define GC_QUEUED_FOR_QUEUING -1
#define GC_QUEUED_FOR_HARD_DEL -2
#define GC_CURRENTLY_BEING_QDELETED -3
#define QDELETED(X) (!X || X.gc_destroyed)
-#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
\ No newline at end of file
diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm
index 9082482ebe..5cfb5f37d0 100644
--- a/code/__defines/species_languages.dm
+++ b/code/__defines/species_languages.dm
@@ -9,6 +9,7 @@
#define NO_HALLUCINATION 0x80 // Don't hallucinate, ever
#define NO_BLOOD 0x100 // Never bleed, never show blood amount
#define UNDEAD 0x200 // Various things that living things don't do, mostly for skeletons
+#define NO_INFECT 0x400 // Don't allow infections in limbs or organs, similar to IS_PLANT, without other strings.
// unused: 0x8000 - higher than this will overflow
// Species spawn flags
@@ -56,3 +57,7 @@
#define NO_TALK_MSG 128 // Do not show the "\The [speaker] talks into \the [radio]" message
#define NO_STUTTER 256 // No stuttering, slurring, or other speech problems
#define ALT_TRANSMIT 512 // Language is not based on vision or sound (Todo: add this into the say code and use it for the rootspeak languages)
+
+#define SKIN_NORMAL 0
+#define SKIN_THREAT 1
+#define SKIN_CLOAK 2
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index 75780be3eb..eb350656b9 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -12,4 +12,9 @@
var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME)
#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list
+// 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_MAPPING 20 // VOREStation Edit
+#define INIT_ORDER_MACHINES 10
#define INIT_ORDER_LIGHTING 0
diff --git a/code/__defines/tick.dm b/code/__defines/tick.dm
index 4c88fd643e..e69de29bb2 100644
--- a/code/__defines/tick.dm
+++ b/code/__defines/tick.dm
@@ -1,7 +0,0 @@
-#define TICK_LIMIT_RUNNING 80
-#define TICK_LIMIT_TO_RUN 78
-#define TICK_LIMIT_MC 70
-#define TICK_LIMIT_MC_INIT_DEFAULT 98
-
-#define TICK_CHECK ( world.tick_usage > Master.current_ticklimit )
-#define CHECK_TICK if TICK_CHECK stoplag()
diff --git a/code/__defines/xenoarcheaology.dm b/code/__defines/xenoarcheaology.dm
index b6af0d0681..b8b781795f 100644
--- a/code/__defines/xenoarcheaology.dm
+++ b/code/__defines/xenoarcheaology.dm
@@ -33,7 +33,8 @@
#define ARCHAEO_REMAINS_ROBOT 33
#define ARCHAEO_REMAINS_XENO 34
#define ARCHAEO_GASMASK 35
-#define MAX_ARCHAEO 35
+#define ARCHAEO_ALIEN_ITEM 36
+#define MAX_ARCHAEO 36
#define DIGSITE_GARDEN 1
#define DIGSITE_ANIMAL 2
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index ddcae4980b..4c6d2c6aee 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -264,7 +264,7 @@
// then adds additional mobs or objects if they are in range 'smartly',
// based on their presence in lists of players or registered objects
// Type: 1-audio, 2-visual, 0-neither
-/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/type = 1)
+/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/type = 1, var/remote_ghosts = TRUE)
var/list/mobs = list()
var/list/objs = list()
@@ -274,22 +274,25 @@
for(var/thing in hear)
if(istype(thing,/obj))
objs += thing
- hearturfs += get_turf(thing)
+ hearturfs |= get_turf(thing)
else if(istype(thing,/mob))
mobs += thing
- hearturfs += get_turf(thing)
+ hearturfs |= get_turf(thing)
//A list of every mob with a client
for(var/mob in player_list)
+ //VOREStation Edit - Trying to fix some vorestation bug.
if(!istype(mob, /mob))
- crash_with("There is a null or non-mob reference inside player_list.")
+ player_list -= mob
+ crash_with("There is a null or non-mob reference inside player_list ([mob]).")
continue
+ //VOREStation Edit End - Trying to fix some vorestation bug.
if(get_turf(mob) in hearturfs)
mobs |= mob
continue
var/mob/M = mob
- if(M && M.stat == DEAD && !M.forbid_seeing_deadchat)
+ if(M && M.stat == DEAD && remote_ghosts && !M.forbid_seeing_deadchat)
switch(type)
if(1) //Audio messages use ghost_ears
if(M.is_preference_enabled(/datum/client_preference/ghost_ears))
diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm
index 2159df1d97..90c93aba8f 100644
--- a/code/_helpers/global_lists_vr.dm
+++ b/code/_helpers/global_lists_vr.dm
@@ -26,6 +26,7 @@ var/global/list/vantag_choices_list = list(
VANTAG_KIDNAP = "Be Kidnapped",
VANTAG_KILL = "Be Killed")
+/* Time to finally undo this. Replaced with digest_act on these items.
//Important items that are preserved when people are digested, etc.
//On Polaris, different from Cryo list as MMIs need to be removed for FBPs to be logged out.
var/global/list/important_items = list(
@@ -40,7 +41,9 @@ var/global/list/important_items = list(
/obj/item/blueprints,
/obj/item/clothing/head/helmet/space,
/obj/item/weapon/disk/nuclear,
- /obj/item/clothing/suit/storage/hooded/wintercoat/roiz)
+ /obj/item/clothing/suit/storage/hooded/wintercoat/roiz,
+ /obj/item/device/perfect_tele_beacon)
+*/
var/global/list/digestion_sounds = list(
'sound/vore/digest1.ogg',
diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm
index 651e6af83c..8819511973 100644
--- a/code/_helpers/icons.dm
+++ b/code/_helpers/icons.dm
@@ -635,7 +635,7 @@ The _flatIcons list is a cache for generated icon files.
*/
proc // Creates a single icon from a given /atom or /image. Only the first argument is required.
- getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0)
+ getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0, picture_planes = list(PLANE_WORLD))
// We start with a blank canvas, otherwise some icon procs crash silently
var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon
if(!A)
@@ -700,6 +700,10 @@ proc // Creates a single icon from a given /atom or /image. Only the first argu
if(curIndex<=process.len)
current = process[curIndex]
if(current)
+ var/currentPlane = current:plane
+ if (currentPlane != FLOAT_PLANE && !(currentPlane in picture_planes))
+ curIndex++
+ continue;
currentLayer = current:layer
if(currentLayer<0) // Special case for FLY_LAYER
if(currentLayer <= -1000) return flat
@@ -760,7 +764,7 @@ proc // Creates a single icon from a given /atom or /image. Only the first argu
// Pull the default direction.
add = icon(I:icon, I:icon_state)
else // 'I' is an appearance object.
- add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend)
+ add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, picture_planes = picture_planes)
// Find the new dimensions of the flat icon to fit the added overlay
addX1 = min(flatX1, I:pixel_x+1)
@@ -874,21 +878,8 @@ proc/sort_atoms_by_layer(var/list/atoms)
swapped = 1
return result
-// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM.
-// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity.
-// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug.
-
-// Mutable appearances are children of images, just so you know.
-
-/mutable_appearance/New()
- ..()
- plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE
- // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var
-
-// Helper similar to image()
-/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER)
- var/mutable_appearance/MA = new()
- MA.icon = icon
- MA.icon_state = icon_state
- MA.layer = layer
- return MA
+/proc/gen_hud_image(var/file, var/person, var/state, var/plane)
+ var/image/img = image(file, person, state)
+ img.plane = plane //Thanks Byond.
+ img.appearance_flags = APPEARANCE_UI|KEEP_APART
+ return img
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index 8b9040820f..17bc605ae7 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -56,6 +56,68 @@ proc/isemptylist(list/list)
return 1
return 0
+//////////////////////////////////////////////////////
+// "typecache" utilities - Making and searching them
+//////////////////////////////////////////////////////
+
+//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
+/proc/is_type_in_typecache(atom/A, list/L)
+ if(!LAZYLEN(L) || !A)
+ return FALSE
+ return L[A.type]
+
+//returns a new list with only atoms that are in typecache L
+/proc/typecache_filter_list(list/atoms, list/typecache)
+ . = list()
+ for(var/thing in atoms)
+ var/atom/A = thing
+ if(typecache[A.type])
+ . += A
+
+/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
+ . = list()
+ for(var/thing in atoms)
+ var/atom/A = thing
+ if(!typecache[A.type])
+ . += A
+
+/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
+ . = list()
+ for(var/thing in atoms)
+ var/atom/A = thing
+ if(typecache_include[A.type] && !typecache_exclude[A.type])
+ . += A
+
+//Like typesof() or subtypesof(), but returns a typecache instead of a list
+/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
+ if(ispath(path))
+ var/list/types = list()
+ if(only_root_path)
+ types = list(path)
+ else
+ types = ignore_root_path ? subtypesof(path) : typesof(path)
+ var/list/L = list()
+ for(var/T in types)
+ L[T] = TRUE
+ return L
+ else if(islist(path))
+ var/list/pathlist = path
+ var/list/L = list()
+ if(ignore_root_path)
+ for(var/P in pathlist)
+ for(var/T in subtypesof(P))
+ L[T] = TRUE
+ else
+ for(var/P in pathlist)
+ if(only_root_path)
+ L[P] = TRUE
+ else
+ for(var/T in typesof(P))
+ L[T] = TRUE
+ return L
+
+//////////////////////////////////////////////////////
+
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
proc/clearlist(list/list)
if(istype(list))
@@ -164,6 +226,14 @@ proc/listclearnulls(list/list)
L.Swap(i, rand(i,L.len))
return L
+//same, but returns nothing and acts on list in place
+/proc/shuffle_inplace(list/L)
+ if(!L)
+ return
+
+ for(var/i=1, i= text2num(icon_state))
return icon_state
@@ -182,7 +182,7 @@ Proc for attack log creation, because really why not
var/starttime = world.time
. = 1
while (world.time < endtime)
- sleep(1)
+ stoplag(1)
if (progress)
progbar.update(world.time - starttime)
if(!user || !target)
@@ -229,7 +229,7 @@ Proc for attack log creation, because really why not
var/starttime = world.time
. = 1
while (world.time < endtime)
- sleep(1)
+ stoplag(1)
if (progress)
progbar.update(world.time - starttime)
diff --git a/code/_helpers/mobs_vr.dm b/code/_helpers/mobs_vr.dm
new file mode 100644
index 0000000000..602a2ed6ce
--- /dev/null
+++ b/code/_helpers/mobs_vr.dm
@@ -0,0 +1,7 @@
+/atom/proc/living_mobs(var/range = world.view)
+ var/list/viewers = oviewers(src,range)
+ var/list/living = list()
+ for(var/mob/living/L in viewers)
+ living += L
+
+ return living
diff --git a/code/_helpers/sorts/comparators.dm b/code/_helpers/sorts/comparators.dm
index 8c1f954a00..58f9251392 100644
--- a/code/_helpers/sorts/comparators.dm
+++ b/code/_helpers/sorts/comparators.dm
@@ -13,8 +13,18 @@
// Sorts subsystems by init_order
/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b)
- return b.init_order - a.init_order
+ return initial(b.init_order) - initial(a.init_order) //uses initial() so it can be used on types
// Sorts subsystems by priority
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
return a.priority - b.priority
+
+// Sorts qdel statistics recorsd by time and count
+/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B)
+ . = B.hard_delete_time - A.hard_delete_time
+ if (!.)
+ . = B.destroy_time - A.destroy_time
+ if (!.)
+ . = B.failures - A.failures
+ if (!.)
+ . = B.qdels - A.qdels
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index 3a6bcedcd1..6dad42e128 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -11,7 +11,13 @@
#define DAYS *864000
#define TimeOfGame (get_game_time())
-#define TimeOfTick (world.tick_usage*0.01*world.tick_lag)
+#define TimeOfTick (TICK_USAGE*0.01*world.tick_lag)
+
+#define TICK *world.tick_lag
+#define TICKS *world.tick_lag
+
+#define DS2TICKS(DS) (DS/world.tick_lag) // Convert deciseconds to ticks
+#define TICKS2DS(T) (T TICKS) // Convert ticks to deciseconds
/proc/get_game_time()
var/global/time_offset = 0
@@ -19,7 +25,7 @@
var/global/last_usage = 0
var/wtime = world.time
- var/wusage = world.tick_usage * 0.01
+ var/wusage = TICK_USAGE * 0.01
if(last_time < wtime && last_usage > 1)
time_offset += last_usage - 1
@@ -111,13 +117,21 @@ var/round_start_time = 0
//Increases delay as the server gets more overloaded,
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
-#define DELTA_CALC max(((max(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta,1)), 1)
+#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1)
-/proc/stoplag()
+//returns the number of ticks slept
+/proc/stoplag(initial_delay)
+ if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT))
+ sleep(world.tick_lag)
+ return 1
+ if (!initial_delay)
+ initial_delay = world.tick_lag
. = 0
- var/i = 1
+ var/i = DS2TICKS(initial_delay)
do
- . += round(i*DELTA_CALC)
+ . += CEILING(i*DELTA_CALC, 1)
sleep(i*world.tick_lag*DELTA_CALC)
i *= 2
- while (world.tick_usage > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
+ while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
+
+#undef DELTA_CALC
\ No newline at end of file
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 2986216641..2fe3e1f6e4 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -7,6 +7,13 @@
//Checks if all high bits in req_mask are set in bitfield
#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0)
+//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
+#define RANGE_TURFS(RADIUS, CENTER) \
+ block( \
+ locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
+ locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
+ )
+
//Inverts the colour of an HTML string
/proc/invertHTML(HTMLstring)
@@ -476,6 +483,8 @@ Turf and target are seperate in case you want to teleport some distance from a t
var/list/sortmob = sortAtom(mob_list)
for(var/mob/observer/eye/M in sortmob)
moblist.Add(M)
+ for(var/mob/observer/blob/M in sortmob)
+ moblist.Add(M)
for(var/mob/living/silicon/ai/M in sortmob)
moblist.Add(M)
for(var/mob/living/silicon/pai/M in sortmob)
@@ -1085,6 +1094,15 @@ var/global/list/common_tools = list(
return 1
return 0
+/proc/is_wire_tool(obj/item/I)
+ if(istype(I, /obj/item/device/multitool))
+ return TRUE
+ if(istype(I, /obj/item/weapon/wirecutters))
+ return TRUE
+ if(istype(I, /obj/item/device/assembly/signaler))
+ return TRUE
+ return
+
proc/is_hot(obj/item/W as obj)
switch(W.type)
if(/obj/item/weapon/weldingtool)
@@ -1331,3 +1349,69 @@ var/mob/dview/dview_mob = new
return "[round(number / 1e9, 0.1)] G[symbol]" // giga
if(1e12 to 1e15-1)
return "[round(number / 1e12, 0.1)] T[symbol]" // tera
+
+
+
+//ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance
+/proc/urange(dist=0, atom/center=usr, orange=0, areas=0)
+ if(!dist)
+ if(!orange)
+ return list(center)
+ else
+ return list()
+
+ var/list/turfs = RANGE_TURFS(dist, center)
+ if(orange)
+ turfs -= get_turf(center)
+ . = list()
+ for(var/V in turfs)
+ var/turf/T = V
+ . += T
+ . += T.contents
+ if(areas)
+ . |= T.loc
+
+#define NOT_FLAG(flag) (!(flag & use_flags))
+#define HAS_FLAG(flag) (flag & use_flags)
+
+// Checks if user can use this object. Set use_flags to customize what checks are done.
+// Returns 0 if they can use it, a value representing why they can't if not.
+// Flags are in `code/__defines/misc.dm`
+/atom/proc/use_check(mob/user, use_flags = 0, show_messages = FALSE)
+ . = 0
+ if (NOT_FLAG(USE_ALLOW_NONLIVING) && !isliving(user))
+ // No message for ghosts.
+ return USE_FAIL_NONLIVING
+
+ if (NOT_FLAG(USE_ALLOW_NON_ADJACENT) && !Adjacent(user))
+ if (show_messages)
+ to_chat(user, span("notice","You're too far away from [src] to do that."))
+ return USE_FAIL_NON_ADJACENT
+
+ if (NOT_FLAG(USE_ALLOW_DEAD) && user.stat == DEAD)
+ if (show_messages)
+ to_chat(user, span("notice","You can't do that when you're dead."))
+ return USE_FAIL_DEAD
+
+ if (NOT_FLAG(USE_ALLOW_INCAPACITATED) && (user.incapacitated()))
+ if (show_messages)
+ to_chat(user, span("notice","You cannot do that in your current state."))
+ return USE_FAIL_INCAPACITATED
+
+ if (NOT_FLAG(USE_ALLOW_NON_ADV_TOOL_USR) && !user.IsAdvancedToolUser())
+ if (show_messages)
+ to_chat(user, span("notice","You don't know how to operate [src]."))
+ return USE_FAIL_NON_ADV_TOOL_USR
+
+ if (HAS_FLAG(USE_DISALLOW_SILICONS) && issilicon(user))
+ if (show_messages)
+ to_chat(user, span("notice","You need hands for that."))
+ return USE_FAIL_IS_SILICON
+
+ if (HAS_FLAG(USE_FORCE_SRC_IN_USER) && !(src in user))
+ if (show_messages)
+ to_chat(user, span("notice","You need to be holding [src] to do that."))
+ return USE_FAIL_NOT_IN_USER
+
+#undef NOT_FLAG
+#undef HAS_FLAG
\ No newline at end of file
diff --git a/code/_helpers/unsorted_vr.dm b/code/_helpers/unsorted_vr.dm
index d7650deb8f..9336591552 100644
--- a/code/_helpers/unsorted_vr.dm
+++ b/code/_helpers/unsorted_vr.dm
@@ -38,4 +38,17 @@
part2 = total-part2
part3 = -part3
- return list(part1, part2, part3)
\ No newline at end of file
+ return list(part1, part2, part3)
+
+//Sender is optional
+/proc/admin_chat_message(var/message = "Debug Message", var/color = "#FFFFFF", var/sender)
+ if (!config.chat_webhook_url || !message)
+ return
+ spawn(0)
+ var/query_string = "type=adminalert"
+ query_string += "&key=[url_encode(config.chat_webhook_key)]"
+ query_string += "&msg=[url_encode(message)]"
+ query_string += "&color=[url_encode(color)]"
+ if(sender)
+ query_string += "&from=[url_encode(sender)]"
+ world.Export("[config.chat_webhook_url]?[query_string]")
diff --git a/code/_macros.dm b/code/_macros.dm
index 38b9d15c71..efd1e72edd 100644
--- a/code/_macros.dm
+++ b/code/_macros.dm
@@ -2,6 +2,8 @@
#define CLAMP01(x) (Clamp(x, 0, 1))
+#define span(class, text) ("[text]")
+
#define get_turf(A) get_step(A,0)
#define isAI(A) istype(A, /mob/living/silicon/ai)
@@ -57,6 +59,8 @@
#define to_world_log(message) world.log << message
// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later.
#define log_world(message) world.log << message
+#define to_file(file_entry, source_var) file_entry << source_var
+#define from_file(file_entry, target_var) file_entry >> target_var
#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)
@@ -87,4 +91,4 @@
// 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 SANITIZE_LIST(L) ( islist(L) ? L : list() )
\ No newline at end of file
diff --git a/code/_map_tests.dm b/code/_map_tests.dm
new file mode 100644
index 0000000000..90a4004b79
--- /dev/null
+++ b/code/_map_tests.dm
@@ -0,0 +1,10 @@
+/*
+ *
+ * This file is used by Travis to indicate that additional maps need to be compiled to look for errors such as missing paths.
+ * Do not add anything but the MAP_TEST definition here as it will be overwritten by Travis when running tests.
+ *
+ *
+ * Should you wish to edit set MAP_TEST to 1 like so:
+ * #define MAP_TEST 1
+ */
+#define MAP_TEST 0
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 8fec6c9c01..17b62d739f 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -108,7 +108,7 @@
W.afterattack(A, src, 1, params) // 1 indicates adjacency
else
if(ismob(A)) // No instant mob attacking
- setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ setClickCooldown(get_attack_speed())
UnarmedAttack(A, 1)
trigger_aiming(TARGET_CAN_CLICK)
@@ -121,7 +121,7 @@
// A is a turf or is on a turf, or in something on a turf (pen in a box); but not something in something on a turf (pen in a box in a backpack)
sdepth = A.storage_depth_turf()
if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1))
- if(A.Adjacent(src)) // see adjacent.dm
+ if(A.Adjacent(src) || (W && W.attack_can_reach(src, A, W.reach)) ) // see adjacent.dm
if(W)
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = W.resolve_attackby(A,src)
@@ -129,7 +129,7 @@
W.afterattack(A, src, 1, params) // 1: clicking something Adjacent
else
if(ismob(A)) // No instant mob attacking
- setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ setClickCooldown(get_attack_speed())
UnarmedAttack(A, 1)
trigger_aiming(TARGET_CAN_CLICK)
return
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index a00fa6e10d..94a067200a 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -1,8 +1,3 @@
-#define FULLSCREEN_LAYER 18
-#define DAMAGE_LAYER FULLSCREEN_LAYER + 0.1
-#define BLIND_LAYER DAMAGE_LAYER + 0.1
-#define CRIT_LAYER BLIND_LAYER + 0.1
-
/mob
var/list/screens = list()
@@ -68,6 +63,7 @@
icon_state = "default"
screen_loc = "CENTER-7,CENTER-7"
layer = FULLSCREEN_LAYER
+ plane = PLANE_FULLSCREEN
mouse_opacity = 0
var/severity = 0
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 6e1b8b2c37..3c6a6a6c56 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -13,7 +13,8 @@ var/list/global_huds = list(
global_hud.thermal,
global_hud.meson,
global_hud.science,
- global_hud.holomap
+ global_hud.material,
+ global_hud.holomap // VOREStation Edit - Holomap
)
/datum/hud/var/obj/screen/grab_intent
@@ -31,7 +32,8 @@ var/list/global_huds = list(
var/obj/screen/thermal
var/obj/screen/meson
var/obj/screen/science
- var/obj/screen/holomap
+ var/obj/screen/material
+ var/obj/screen/holomap // VOREStation Edit - Holomap
/datum/global_hud/proc/setup_overlay(var/icon_state)
var/obj/screen/screen = new /obj/screen()
@@ -39,38 +41,38 @@ var/list/global_huds = list(
screen.icon = 'icons/obj/hud_full.dmi'
screen.icon_state = icon_state
screen.layer = SCREEN_LAYER
+ screen.plane = PLANE_FULLSCREEN
screen.mouse_opacity = 0
return screen
+/obj/screen/global_screen
+ screen_loc = ui_entire_screen
+ layer = 17
+ plane = PLANE_FULLSCREEN
+ mouse_opacity = 0
+
/datum/global_hud/New()
//420erryday psychedellic colours screen overlay for when you are high
- druggy = new /obj/screen()
- druggy.screen_loc = ui_entire_screen
+ druggy = new /obj/screen/global_screen()
druggy.icon_state = "druggy"
- druggy.layer = 17
- druggy.mouse_opacity = 0
//that white blurry effect you get when you eyes are damaged
- blurry = new /obj/screen()
- blurry.screen_loc = ui_entire_screen
+ blurry = new /obj/screen/global_screen()
blurry.icon_state = "blurry"
- blurry.layer = 17
- blurry.mouse_opacity = 0
//static overlay effect for cameras and the like
- whitense = new /obj/screen()
- whitense.screen_loc = ui_entire_screen
+ whitense = new /obj/screen/global_screen()
whitense.icon = 'icons/effects/static.dmi'
whitense.icon_state = "1 light"
- whitense.layer = 17
- whitense.mouse_opacity = 0
nvg = setup_overlay("nvg_hud")
thermal = setup_overlay("thermal_hud")
meson = setup_overlay("meson_hud")
science = setup_overlay("science_hud")
+ material = setup_overlay("material_hud")
+ // VOREStation Edit Begin - Holomap
// The holomap screen object is actually totally invisible.
// Station maps work by setting it as an images location before sending to client, not
// actually changing the icon or icon state of the screen object itself!
@@ -82,6 +84,7 @@ var/list/global_huds = list(
holomap.icon = null
holomap.screen_loc = ui_holomap
holomap.mouse_opacity = 0
+ // VOREStation Edit End
var/obj/screen/O
var/i
@@ -89,12 +92,16 @@ var/list/global_huds = list(
vimpaired = newlist(/obj/screen,/obj/screen,/obj/screen,/obj/screen)
O = vimpaired[1]
O.screen_loc = "1,1 to 5,15"
+ O.plane = PLANE_FULLSCREEN
O = vimpaired[2]
O.screen_loc = "5,1 to 10,5"
+ O.plane = PLANE_FULLSCREEN
O = vimpaired[3]
O.screen_loc = "6,11 to 10,15"
+ O.plane = PLANE_FULLSCREEN
O = vimpaired[4]
O.screen_loc = "11,1 to 15,15"
+ O.plane = PLANE_FULLSCREEN
//welding mask overlay black/dither
darkMask = newlist(/obj/screen, /obj/screen, /obj/screen, /obj/screen, /obj/screen, /obj/screen, /obj/screen, /obj/screen)
@@ -119,18 +126,21 @@ var/list/global_huds = list(
O = vimpaired[i]
O.icon_state = "dither50"
O.layer = 17
+ O.plane = PLANE_FULLSCREEN
O.mouse_opacity = 0
O = darkMask[i]
O.icon_state = "dither50"
O.layer = 17
+ O.plane = PLANE_FULLSCREEN
O.mouse_opacity = 0
for(i = 5, i <= 8, i++)
O = darkMask[i]
O.icon_state = "black"
O.layer = 17
- O.mouse_opacity = 0
+ O.plane = PLANE_FULLSCREEN
+ O.mouse_opacity = 2
/*
The hud datum
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 57e1e4bb6a..83f23930dd 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -13,7 +13,7 @@
src.adding = list()
src.other = list()
- src.hotkeybuttons = list() //These can be disabled for hotkey usersx
+ src.hotkeybuttons = list() //These can be disabled for hotkey users
var/list/hud_elements = list()
var/obj/screen/using
@@ -25,7 +25,6 @@
inv_box = new /obj/screen/inventory()
inv_box.icon = ui_style
- inv_box.layer = 19
inv_box.color = ui_color
inv_box.alpha = ui_alpha
@@ -50,7 +49,7 @@
using.icon = ui_style
using.icon_state = "other"
using.screen_loc = ui_inventory
- using.layer = 20
+ using.hud_layerise()
using.color = ui_color
using.alpha = ui_alpha
src.adding += using
@@ -65,7 +64,6 @@
using.screen_loc = ui_acti
using.color = ui_color
using.alpha = ui_alpha
- using.layer = 20
src.adding += using
action_intent = using
@@ -82,7 +80,7 @@
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
- using.layer = 21
+ using.layer = LAYER_HUD_ITEM //These sit on the intent box
src.adding += using
help_intent = using
@@ -94,7 +92,7 @@
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
- using.layer = 21
+ using.layer = LAYER_HUD_ITEM
src.adding += using
disarm_intent = using
@@ -106,7 +104,7 @@
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
- using.layer = 21
+ using.layer = LAYER_HUD_ITEM
src.adding += using
grab_intent = using
@@ -118,7 +116,7 @@
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
- using.layer = 21
+ using.layer = LAYER_HUD_ITEM
src.adding += using
hurt_intent = using
//end intent small hud objects
@@ -129,7 +127,6 @@
using.icon = ui_style
using.icon_state = (mymob.m_intent == "run" ? "running" : "walking")
using.screen_loc = ui_movi
- using.layer = 20
using.color = ui_color
using.alpha = ui_alpha
src.adding += using
@@ -141,7 +138,6 @@
using.icon = ui_style
using.icon_state = "act_drop"
using.screen_loc = ui_drop_throw
- using.layer = 19
using.color = ui_color
using.alpha = ui_alpha
src.hotkeybuttons += using
@@ -153,7 +149,6 @@
using.icon = ui_style
using.icon_state = "act_equip"
using.screen_loc = ui_equip
- using.layer = 20
using.color = ui_color
using.alpha = ui_alpha
src.adding += using
@@ -167,7 +162,6 @@
inv_box.icon_state = "r_hand_active"
inv_box.screen_loc = ui_rhand
inv_box.slot_id = slot_r_hand
- inv_box.layer = 19
inv_box.color = ui_color
inv_box.alpha = ui_alpha
@@ -183,7 +177,6 @@
inv_box.icon_state = "l_hand_active"
inv_box.screen_loc = ui_lhand
inv_box.slot_id = slot_l_hand
- inv_box.layer = 19
inv_box.color = ui_color
inv_box.alpha = ui_alpha
src.l_hand_hud_object = inv_box
@@ -194,7 +187,6 @@
using.icon = ui_style
using.icon_state = "hand1"
using.screen_loc = ui_swaphand1
- using.layer = 19
using.color = ui_color
using.alpha = ui_alpha
src.adding += using
@@ -204,7 +196,6 @@
using.icon = ui_style
using.icon_state = "hand2"
using.screen_loc = ui_swaphand2
- using.layer = 19
using.color = ui_color
using.alpha = ui_alpha
src.adding += using
@@ -215,7 +206,6 @@
using.icon = ui_style
using.icon_state = "act_resist"
using.screen_loc = ui_pull_resist
- using.layer = 19
using.color = ui_color
using.alpha = ui_alpha
src.hotkeybuttons += using
diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm
index 52aa248431..daaa52e827 100644
--- a/code/_onclick/hud/robot.dm
+++ b/code/_onclick/hud/robot.dm
@@ -258,7 +258,7 @@ var/obj/screen/robot_inventory
A.screen_loc = "CENTER[x]:16,SOUTH+[y]:7"
else
A.screen_loc = "CENTER+[x]:16,SOUTH+[y]:7"
- A.layer = 20
+ A.hud_layerise()
x++
if(x == 4)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 35ef300978..79e7beb87c 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -9,7 +9,9 @@
/obj/screen
name = ""
icon = 'icons/mob/screen1.dmi'
- layer = 20.0
+ appearance_flags = TILE_BOUND|PIXEL_SCALE|NO_CLIENT_COLOR
+ layer = LAYER_HUD_BASE
+ plane = PLANE_PLAYER_HUD
unacidable = 1
var/obj/master = null //A reference to the object in the slot. Grabs or items, generally.
var/datum/hud/hud = null // A reference to the owner HUD, if any.
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 7b8fe1bc86..01e87b490d 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -23,8 +23,13 @@ avoid code duplication. This includes items that may sometimes act as a standard
/obj/item/proc/attack_self(mob/user)
return
+// Called at the start of resolve_attackby(), before the actual attack.
+/obj/item/proc/pre_attack(atom/a, mob/user)
+ return
+
//I would prefer to rename this to attack(), but that would involve touching hundreds of files.
/obj/item/proc/resolve_attackby(atom/A, mob/user)
+ pre_attack(A, user)
add_fingerprint(user)
return A.attackby(src, user)
@@ -44,6 +49,22 @@ avoid code duplication. This includes items that may sometimes act as a standard
if(attempt_vr(src,"vore_attackby",args)) return //VOREStation Code
return I.attack(src, user, user.zone_sel.selecting)
+// Used to get how fast a mob should attack, and influences click delay.
+// This is just for inheritence.
+/mob/proc/get_attack_speed()
+ return DEFAULT_ATTACK_COOLDOWN
+
+// 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
+ if(W && istype(W))
+ speed = W.attackspeed
+ for(var/datum/modifier/M in modifiers)
+ if(!isnull(M.attack_speed_percent))
+ speed *= M.attack_speed_percent
+ return speed
+
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
@@ -66,7 +87,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" )
/////////////////////////
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(M)
var/hit_zone = M.resolve_item_attack(src, user, target_zone)
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 8cf8948bb7..a80ff05eac 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -59,7 +59,7 @@
if(!..())
return 0
- setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ setClickCooldown(get_attack_speed())
A.attack_generic(src,rand(5,6),"bitten")
/*
@@ -87,7 +87,7 @@
custom_emote(1,"[friendly] [A]!")
return
- setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ setClickCooldown(get_attack_speed())
if(isliving(A))
target_mob = A
PunchTarget()
@@ -96,7 +96,7 @@
A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper), attacktext)
/mob/living/simple_animal/RangedAttack(var/atom/A)
- setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ setClickCooldown(get_attack_speed())
var/distance = get_dist(src, A)
if(prob(spattack_prob) && (distance >= spattack_min_range) && (distance <= spattack_max_range))
diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm
index 436e7353ec..5f2561c25c 100644
--- a/code/_onclick/rig.dm
+++ b/code/_onclick/rig.dm
@@ -74,7 +74,7 @@
return 0
rig.selected_module.engage(A, alert_ai)
if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns
- setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ setClickCooldown(get_attack_speed())
return 1
return 0
diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm
index 9f08241978..2965a52abe 100644
--- a/code/controllers/ProcessScheduler/core/process.dm
+++ b/code/controllers/ProcessScheduler/core/process.dm
@@ -124,7 +124,7 @@
cpu_defer_count = 0
// Prepare usage tracking (defer() updates these)
- tick_usage_start = world.tick_usage
+ tick_usage_start = TICK_USAGE
tick_usage_accumulated = 0
running()
@@ -142,7 +142,7 @@
/datum/controller/process/proc/recordRunTime()
// Convert from tick usage (100/tick) to seconds of CPU time used
- var/total_usage = (tick_usage_accumulated + (world.tick_usage - tick_usage_start)) / 1000 * world.tick_lag
+ var/total_usage = (tick_usage_accumulated + (TICK_USAGE - tick_usage_start)) / 1000 * world.tick_lag
last_run_time = total_usage
if(total_usage > highest_run_time)
@@ -222,14 +222,14 @@
handleHung()
CRASH("Process [name] hung and was restarted.")
- tick_usage_accumulated += (world.tick_usage - tick_usage_start)
- if(world.tick_usage < defer_usage)
+ tick_usage_accumulated += (TICK_USAGE - tick_usage_start)
+ if(TICK_USAGE < defer_usage)
sleep(0)
else
sleep(world.tick_lag)
cpu_defer_count++
- tick_usage_start = world.tick_usage
- next_sleep_usage = min(world.tick_usage + sleep_interval, defer_usage)
+ tick_usage_start = TICK_USAGE
+ next_sleep_usage = min(TICK_USAGE + sleep_interval, defer_usage)
/datum/controller/process/proc/update()
// Clear delta
diff --git a/code/controllers/Processes/planet.dm b/code/controllers/Processes/planet.dm
index 063d6d3fb9..f9fd57f788 100644
--- a/code/controllers/Processes/planet.dm
+++ b/code/controllers/Processes/planet.dm
@@ -51,9 +51,10 @@ var/datum/controller/process/planet/planet_controller = null
//Redraw weather icons
for(var/T in P.planet_floors)
var/turf/simulated/turf = T
- turf.overlays -= turf.weather_overlay
+ // turf.overlays -= turf.weather_overlay
turf.weather_overlay = new_overlay
- turf.overlays += turf.weather_overlay
+ // turf.overlays += turf.weather_overlay
+ turf.update_icon()
SCHECK
//Sun light needs changing
diff --git a/code/controllers/autotransfer.dm b/code/controllers/autotransfer.dm
index 0220df0506..cb28cad516 100644
--- a/code/controllers/autotransfer.dm
+++ b/code/controllers/autotransfer.dm
@@ -18,7 +18,7 @@ datum/controller/transfer_controller/proc/process()
currenttick = currenttick + 1
if (round_duration_in_ticks >= shift_last_vote - 2 MINUTES) //VOREStation Edit START
shift_last_vote = 999999999999 //Setting to a stupidly high number since it'll be not used again.
- world << "Warning: This upcoming extend vote will be your ONE and ONLY Transfer vote for the next ten hours. Wrap up your scenes if the vote succeeds." //VOREStation Edit
+ world << "Warning: This upcoming round-extend vote will be your ONLY extend vote. Wrap up your scenes in the next 60 minutes if the round is extended." //VOREStation Edit
if (round_duration_in_ticks >= shift_hard_end - 1 MINUTE)
init_shift_change(null, 1)
shift_hard_end = timerbuffer + config.vote_autotransfer_interval //If shuttle somehow gets recalled, let's force it to call again next time a vote would occur.
diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm
index 4b56d94f0d..0b53ed169d 100644
--- a/code/controllers/communications.dm
+++ b/code/controllers/communications.dm
@@ -119,6 +119,7 @@ var/const/MED_FREQ = 1355
var/const/SCI_FREQ = 1351
var/const/SRV_FREQ = 1349
var/const/SUP_FREQ = 1347
+var/const/EXP_FREQ = 1361
// internal department channels
var/const/MED_I_FREQ = 1485
@@ -137,6 +138,7 @@ var/list/radiochannels = list(
"Raider" = RAID_FREQ,
"Supply" = SUP_FREQ,
"Service" = SRV_FREQ,
+ "Explorer" = EXP_FREQ,
"AI Private" = AI_FREQ,
"Entertainment" = ENT_FREQ,
"Medical(I)" = MED_I_FREQ,
@@ -181,6 +183,8 @@ var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, ENT_FREQ, MED_FREQ, SEC
return "supradio"
if(frequency == SRV_FREQ) // service
return "srvradio"
+ if(frequency == EXP_FREQ) // explorer
+ return "expradio"
if(frequency == ENT_FREQ) // entertainment
return "entradio"
if(frequency in DEPT_FREQS)
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 505a2745ad..811dc3e124 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -81,6 +81,7 @@ var/list/gamemode_cache = list()
var/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active.
var/character_slots = 10 // The number of available character slots
+ var/loadout_slots = 3 // The number of loadout slots per character
var/max_maint_drones = 5 //This many drones can spawn,
var/allow_drone_spawn = 1 //assuming the admin allow them to.
@@ -653,6 +654,9 @@ var/list/gamemode_cache = list()
if("character_slots")
config.character_slots = text2num(value)
+ if("loadout_slots")
+ config.loadout_slots = text2num(value)
+
if("allow_drone_spawn")
config.allow_drone_spawn = text2num(value)
diff --git a/code/controllers/configuration_vr.dm b/code/controllers/configuration_vr.dm
index 1436378616..f1298d4d61 100644
--- a/code/controllers/configuration_vr.dm
+++ b/code/controllers/configuration_vr.dm
@@ -2,6 +2,11 @@
// Lets read our settings from the configuration file on startup too!
//
+/datum/configuration
+ var/list/engine_map // Comma separated list of engines to choose from. Blank means fully random.
+ var/assistants_ratio
+ var/assistants_assured = 15 // Default 15, only used if the ratio is set though.
+
/hook/startup/proc/read_vs_config()
var/list/Lines = file2list("config/config.txt")
for(var/t in Lines)
@@ -27,12 +32,19 @@
continue
switch (name)
+ if ("assistants_ratio")
+ config.assistants_ratio = text2num(value)
+ if ("assistants_assured")
+ config.assistants_assured = text2num(value)
if ("chat_webhook_url")
config.chat_webhook_url = value
if ("chat_webhook_key")
config.chat_webhook_key = value
+ if ("engine_map")
+ config.engine_map = splittext(value, ",")
if ("fax_export_dir")
config.fax_export_dir = value
if ("items_survive_digestion")
config.items_survive_digestion = 1
+
return 1
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 5056e82e7b..3ecb3b7fcb 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -12,7 +12,7 @@ var/datum/controller/master/Master = new()
name = "Master"
// Are we processing (higher values increase the processing delay by n ticks)
- var/processing = 1
+ var/processing = TRUE
// How many times have we ran
var/iteration = 0
@@ -27,7 +27,7 @@ var/datum/controller/master/Master = new()
var/init_time
var/tickdrift = 0
- var/sleep_delta
+ var/sleep_delta = 1
var/make_runtime = 0
@@ -54,13 +54,17 @@ var/datum/controller/master/Master = new()
/datum/controller/master/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
- subsystems = list()
+ var/list/_subsystems = list()
+ subsystems = _subsystems
if (Master != src)
if (istype(Master))
Recover()
qdel(Master)
else
- init_subtypes(/datum/controller/subsystem, subsystems)
+ var/list/subsytem_types = subtypesof(/datum/controller/subsystem)
+ sortTim(subsytem_types, /proc/cmp_subsystem_init)
+ for(var/I in subsytem_types)
+ _subsystems += new I
Master = src
/datum/controller/master/Destroy()
@@ -73,7 +77,9 @@ var/datum/controller/master/Master = new()
sortTim(subsystems, /proc/cmp_subsystem_init)
reverseRange(subsystems)
for(var/datum/controller/subsystem/ss in subsystems)
+ log_world("Shutting down [ss.name] subsystem...")
ss.Shutdown()
+ log_world("Shutdown complete")
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
// -1 if we encountered a runtime trying to recreate it
@@ -87,7 +93,7 @@ var/datum/controller/master/Master = new()
var/delay = 50 * ++Master.restart_count
Master.restart_timeout = world.time + delay
Master.restart_clear = world.time + (delay * 2)
- Master.processing = 0 //stop ticking this one
+ Master.processing = FALSE //stop ticking this one
try
new/datum/controller/master()
catch
@@ -114,7 +120,8 @@ var/datum/controller/master/Master = new()
var/FireHim = FALSE
if(istype(BadBoy))
msg = null
- switch(++BadBoy.failure_strikes)
+ LAZYINITLIST(BadBoy.failure_strikes)
+ switch(++BadBoy.failure_strikes[BadBoy.type])
if(2)
msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again."
FireHim = TRUE
@@ -262,35 +269,45 @@ var/datum/controller/master/Master = new()
iteration = 1
var/error_level = 0
- var/sleep_delta = 0
+ var/sleep_delta = 1
var/list/subsystems_to_check
//the actual loop.
+
while (1)
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)
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
- //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later
- // because sleeps are processed in the order received, so longer sleeps are more likely to run first
- if (world.tick_usage > TICK_LIMIT_MC)
- sleep_delta += 2
+ //Anti-tick-contention heuristics:
+ //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
+ // (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
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
- sleep(world.tick_lag * (processing + sleep_delta))
+ sleep(world.tick_lag * (processing * sleep_delta))
continue
- sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0)
- if (last_run + (world.tick_lag * processing) > world.time)
- sleep_delta += 1
- if (world.tick_usage > (TICK_LIMIT_MC*0.5))
+ //Byond resumed us late. assume it might have to do the same next tick
+ if (last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time)
sleep_delta += 1
+ sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
+
+ if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) //we ran 3/4 of the way into the tick
+ sleep_delta += 1
+
+ //debug
if (make_runtime)
var/datum/controller/subsystem/SS
SS.can_fire = 0
+
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe.
+
+ //now do the actual stuff
if (!queue_head || !(iteration % 3))
var/checking_runlevel = current_runlevel
if(cached_runlevel != checking_runlevel)
@@ -307,6 +324,7 @@ var/datum/controller/master/Master = new()
subsystems_to_check = current_runlevel_subsystems
else
subsystems_to_check = tickersubsystems
+
if (CheckQueue(subsystems_to_check) <= 0)
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing")
@@ -337,8 +355,10 @@ var/datum/controller/master/Master = new()
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
- current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
- sleep(world.tick_lag * (processing + sleep_delta))
+ current_ticklimit = TICK_LIMIT_RUNNING
+ if (processing * sleep_delta <= world.tick_lag)
+ 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))
@@ -389,13 +409,13 @@ var/datum/controller/master/Master = new()
//keep running while we have stuff to run and we haven't gone over a tick
// this is so subsystems paused eariler can use tick time that later subsystems never used
- while (ran && queue_head && world.tick_usage < TICK_LIMIT_MC)
+ while (ran && queue_head && TICK_USAGE < TICK_LIMIT_MC)
ran = FALSE
bg_calc = FALSE
current_tick_budget = queue_priority_count
queue_node = queue_head
while (queue_node)
- if (ran && world.tick_usage > TICK_LIMIT_RUNNING)
+ if (ran && TICK_USAGE > TICK_LIMIT_RUNNING)
break
queue_node_flags = queue_node.flags
@@ -407,7 +427,7 @@ var/datum/controller/master/Master = new()
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
// in those cases, so we just let them run)
if (queue_node_flags & SS_NO_TICK_CHECK)
- if (queue_node.tick_usage > TICK_LIMIT_RUNNING - world.tick_usage && ran_non_ticker)
+ if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
queue_node.queued_priority += queue_priority_count * 0.10
queue_priority_count -= queue_node_priority
queue_priority_count += queue_node.queued_priority
@@ -419,19 +439,19 @@ var/datum/controller/master/Master = new()
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
- tick_remaining = TICK_LIMIT_RUNNING - world.tick_usage
+ tick_remaining = TICK_LIMIT_RUNNING - TICK_USAGE
if (current_tick_budget > 0 && queue_node_priority > 0)
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
else
tick_precentage = tick_remaining
- current_ticklimit = world.tick_usage + tick_precentage
+ current_ticklimit = TICK_USAGE + tick_precentage
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
ran = TRUE
- tick_usage = world.tick_usage
+ tick_usage = TICK_USAGE
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
@@ -441,7 +461,7 @@ var/datum/controller/master/Master = new()
if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
- tick_usage = world.tick_usage - tick_usage
+ tick_usage = TICK_USAGE - tick_usage
if (tick_usage < 0)
tick_usage = 0
@@ -540,10 +560,10 @@ var/datum/controller/master/Master = new()
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
-/datum/controller/master/StartLoadingMap()
+/datum/controller/master/StartLoadingMap(var/quiet = TRUE)
if(map_loading)
admin_notice("Another map is attempting to be loaded before first map released lock. Delaying.", R_DEBUG)
- else
+ else if(!quiet)
admin_notice("Map is now being built. Locking.", R_DEBUG)
//disallow more than one map to load at once, multithreading it will just cause race conditions
@@ -557,8 +577,9 @@ var/datum/controller/master/Master = new()
air_processing_killed = TRUE
map_loading = TRUE
-/datum/controller/master/StopLoadingMap(bounds = null)
- admin_notice("Map is finished. Unlocking.", R_DEBUG)
+/datum/controller/master/StopLoadingMap(var/quiet = TRUE)
+ if(!quiet)
+ admin_notice("Map is finished. Unlocking.", R_DEBUG)
air_processing_killed = FALSE
map_loading = FALSE
for(var/S in subsystems)
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index fef25b9d10..ab72c18292 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -60,6 +60,7 @@ datum/controller/game_controller/proc/setup_objects()
//Set up spawn points.
populate_spawn_points()
+ to_world_log("Initializing Floor Decals") // VOREStation Edit
admin_notice("Initializing Floor Decals", R_DEBUG)
var/list/turfs_with_decals = list()
for(var/obj/effect/floor_decal/D in world)
@@ -73,6 +74,7 @@ datum/controller/game_controller/proc/setup_objects()
floor_decals_initialized = TRUE
sleep(1)
+ to_world_log("Initializing objects") // VOREStation Edit
admin_notice("Initializing objects", R_DEBUG)
for(var/atom/movable/object in world)
if(!QDELETED(object))
@@ -80,17 +82,26 @@ datum/controller/game_controller/proc/setup_objects()
CHECK_SLEEP_MASTER
sleep(1)
+ to_world_log("Initializing areas") // VOREStation Edit
admin_notice("Initializing areas", R_DEBUG)
for(var/area/area in all_areas)
area.initialize()
CHECK_SLEEP_MASTER
sleep(1)
+ to_world_log("Initializing atmos machinery connections.") // VOREStation Edit
+ admin_notice("Initializing atmos machinery connections.", R_DEBUG)
+ for(var/obj/machinery/atmospherics/machine in machines)
+ machine.atmos_init()
+ CHECK_SLEEP_MASTER
+
+ to_world_log("Initializing pipe networks") // VOREStation Edit
admin_notice("Initializing pipe networks", R_DEBUG)
for(var/obj/machinery/atmospherics/machine in machines)
machine.build_network()
CHECK_SLEEP_MASTER
+ to_world_log("Initializing atmos machinery.") // VOREStation Edit
admin_notice("Initializing atmos machinery.", R_DEBUG)
for(var/obj/machinery/atmospherics/unary/U in machines)
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
@@ -101,6 +112,7 @@ datum/controller/game_controller/proc/setup_objects()
T.broadcast_status()
CHECK_SLEEP_MASTER
+ to_world_log("Initializing turbolifts") // VOREStation Edit
admin_notice("Initializing turbolifts", R_DEBUG)
for(var/thing in turbolifts)
var/obj/turbolift_map_holder/lift = thing
diff --git a/code/controllers/shuttle_controller.dm b/code/controllers/shuttle_controller.dm
index 5183304609..f68957ddb5 100644
--- a/code/controllers/shuttle_controller.dm
+++ b/code/controllers/shuttle_controller.dm
@@ -8,8 +8,12 @@ var/global/datum/shuttle_controller/shuttle_controller
/datum/shuttle_controller/proc/process()
//process ferry shuttles
- for (var/datum/shuttle/ferry/shuttle in process_shuttles)
- if (shuttle.process_state || shuttle.always_process)
+ for (var/datum/shuttle/shuttle in process_shuttles)
+ if(istype(shuttle, /datum/shuttle/ferry))
+ var/datum/shuttle/ferry/F = shuttle
+ if(F.process_state || F.always_process)
+ F.process()
+ else
shuttle.process()
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index 7dff609c76..bd9a2aeafb 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -28,11 +28,10 @@
var/datum/controller/subsystem/queue_next
var/datum/controller/subsystem/queue_prev
- var/static/failure_strikes = 0 //How many times we suspect this subsystem has crashed the MC, 3 strikes and you're out!
+ var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
//Do not override
-/datum/controller/subsystem/New()
- return
+///datum/controller/subsystem/New()
// Used to initialize the subsystem BEFORE the map has loaded
// Called AFTER Recover if that is called
@@ -66,7 +65,7 @@
can_fire = 0
flags |= SS_NO_FIRE
Master.subsystems -= src
-
+ return ..()
//Queue it to run.
// (we loop thru a linked list until we get to the end or find the right point)
diff --git a/code/controllers/subsystems/creation.dm b/code/controllers/subsystems/creation.dm
index e92a0447c8..d6f4b3c9c5 100644
--- a/code/controllers/subsystems/creation.dm
+++ b/code/controllers/subsystems/creation.dm
@@ -13,10 +13,10 @@ SUBSYSTEM_DEF(creation)
var/map_loading = FALSE
-/datum/controller/subsystem/creation/StartLoadingMap()
+/datum/controller/subsystem/creation/StartLoadingMap(var/quiet)
map_loading = TRUE
-/datum/controller/subsystem/creation/StopLoadingMap()
+/datum/controller/subsystem/creation/StopLoadingMap(var/quiet)
map_loading = FALSE
/datum/controller/subsystem/creation/proc/initialize_late_atoms()
diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm
index d6db860d5d..9deed740b6 100644
--- a/code/controllers/subsystems/garbage.dm
+++ b/code/controllers/subsystems/garbage.dm
@@ -4,11 +4,12 @@
SUBSYSTEM_DEF(garbage)
name = "Garbage"
priority = 15
- wait = 5
+ wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
- var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
+ var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
+
var/delslasttick = 0 // number of del()'s we've done this tick
var/gcedlasttick = 0 // number of things that gc'ed last tick
var/totaldels = 0
@@ -17,27 +18,32 @@ SUBSYSTEM_DEF(garbage)
var/highest_del_time = 0
var/highest_del_tickusage = 0
- var/list/queue = list() // list of refID's of things that should be garbage collected
- // refID's are associated with the time at which they time out and need to be manually del()
- // we do this so we aren't constantly locating them and preventing them from being gc'd
+ var/list/pass_counts
+ var/list/fail_counts
- var/list/tobequeued = list() //We store the references of things to be added to the queue seperately so we can spread out GC overhead over a few ticks
+ var/list/items = list() // Holds our qdel_item statistics datums
- var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
- // the types are stored as strings
- var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
+ // List of Queues
+ // Each queue is a list of refID's of things that should be garbage collected
+ // refID's are associated with the time at which they time out and need to be manually del()
+ // we do this so we aren't constantly locating them and preventing them from being gc'd
+ var/list/queues
- var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
- // all types that did not respect qdel(A, force=TRUE) and returned one
- // of the immortality qdel hints
- var/list/noforcerespect = list()
-#ifdef TESTING
- var/list/qdel_list = list() // list of all types that have been qdel()eted
-#endif
+/datum/controller/subsystem/garbage/PreInit()
+ queues = new(GC_QUEUE_COUNT)
+ pass_counts = new(GC_QUEUE_COUNT)
+ fail_counts = new(GC_QUEUE_COUNT)
+ for(var/i in 1 to GC_QUEUE_COUNT)
+ queues[i] = list()
+ pass_counts[i] = 0
+ fail_counts[i] = 0
/datum/controller/subsystem/garbage/stat_entry(msg)
- msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
+ var/list/counts = list()
+ for (var/list/L in queues)
+ counts += length(L)
+ msg += "Q:[counts.Join(",")]|D:[delslasttick]|G:[gcedlasttick]|"
msg += "GR:"
if (!(delslasttick+gcedlasttick))
msg += "n/a|"
@@ -49,116 +55,179 @@ SUBSYSTEM_DEF(garbage)
msg += "n/a|"
else
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
+ msg += " P:[pass_counts.Join(",")]"
+ msg += "|F:[fail_counts.Join(",")]"
..(msg)
/datum/controller/subsystem/garbage/Shutdown()
- //Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
- if(didntgc.len || sleptDestroy.len)
- var/list/dellog = list()
- for(var/path in didntgc)
- dellog += "Path : [path] \n"
- dellog += "Failures : [didntgc[path]] \n"
- if(path in sleptDestroy)
- dellog += "Sleeps : [sleptDestroy[path]] \n"
- sleptDestroy -= path
- for(var/path in sleptDestroy)
- dellog += "Path : [path] \n"
- dellog += "Sleeps : [sleptDestroy[path]] \n"
+ //Adds the del() log to the qdel log file
+ var/list/dellog = list()
+
+ //sort by how long it's wasted hard deleting
+ sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
+ for(var/path in items)
+ var/datum/qdel_item/I = items[path]
+ dellog += "Path: [path]"
+ if (I.failures)
+ dellog += "\tFailures: [I.failures]"
+ dellog += "\tqdel() Count: [I.qdels]"
+ dellog += "\tDestroy() Cost: [I.destroy_time]ms"
+ if (I.hard_deletes)
+ dellog += "\tTotal Hard Deletes [I.hard_deletes]"
+ dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms"
+ if (I.slept_destroy)
+ dellog += "\tSleeps: [I.slept_destroy]"
+ if (I.no_respect_force)
+ dellog += "\tIgnored force: [I.no_respect_force] times"
+ if (I.no_hint)
+ dellog += "\tNo hint: [I.no_hint] times"
log_misc(dellog.Join())
/datum/controller/subsystem/garbage/fire()
- HandleToBeQueued()
- if(state == SS_RUNNING)
- HandleQueue()
-
+ //the fact that this resets its processing each fire (rather then resume where it left off) is intentional.
+ var/queue = GC_QUEUE_PREQUEUE
+
+ while (state == SS_RUNNING)
+ switch (queue)
+ if (GC_QUEUE_PREQUEUE)
+ HandlePreQueue()
+ queue = GC_QUEUE_PREQUEUE+1
+ if (GC_QUEUE_CHECK)
+ HandleQueue(GC_QUEUE_CHECK)
+ queue = GC_QUEUE_CHECK+1
+ if (GC_QUEUE_HARDDELETE)
+ HandleQueue(GC_QUEUE_HARDDELETE)
+ break
+
if (state == SS_PAUSED) //make us wait again before the next run.
- state = SS_RUNNING
+ state = SS_RUNNING
//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
//Don't attempt to optimize, not worth the effort.
-/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
- var/list/tobequeued = src.tobequeued
- var/starttime = world.time
- var/starttimeofday = world.timeofday
- while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday)
- if (MC_TICK_CHECK)
- break
- var/ref = tobequeued[1]
- Queue(ref)
- tobequeued.Cut(1, 2)
+/datum/controller/subsystem/garbage/proc/HandlePreQueue()
+ var/list/tobequeued = queues[GC_QUEUE_PREQUEUE]
+ var/static/count = 0
+ if (count)
+ var/c = count
+ count = 0 //so if we runtime on the Cut, we don't try again.
+ tobequeued.Cut(1,c+1)
-/datum/controller/subsystem/garbage/proc/HandleQueue()
- delslasttick = 0
- gcedlasttick = 0
- var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
- var/list/queue = src.queue
- var/starttime = world.time
- var/starttimeofday = world.timeofday
- while(queue.len && starttime == world.time && starttimeofday == world.timeofday)
+ for (var/ref in tobequeued)
+ count++
+ Queue(ref, GC_QUEUE_PREQUEUE+1)
if (MC_TICK_CHECK)
break
- var/refID = queue[1]
+ if (count)
+ tobequeued.Cut(1,count+1)
+ count = 0
+
+/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK)
+ if (level == GC_QUEUE_CHECK)
+ delslasttick = 0
+ gcedlasttick = 0
+ var/cut_off_time = world.time - collection_timeout[level] //ignore entries newer then this
+ var/list/queue = queues[level]
+ var/static/lastlevel
+ var/static/count = 0
+ if (count) //runtime last run before we could do this.
+ var/c = count
+ count = 0 //so if we runtime on the Cut, we don't try again.
+ var/list/lastqueue = queues[lastlevel]
+ lastqueue.Cut(1, c+1)
+
+ lastlevel = level
+
+ for (var/refID in queue)
if (!refID)
- queue.Cut(1, 2)
+ count++
+ if (MC_TICK_CHECK)
+ break
continue
var/GCd_at_time = queue[refID]
- if(GCd_at_time > time_to_kill)
+ if(GCd_at_time > cut_off_time)
break // Everything else is newer, skip them
- queue.Cut(1, 2)
- var/datum/A
- A = locate(refID)
- if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
- #ifdef GC_FAILURE_HARD_LOOKUP
- A.find_references()
- #endif
+ count++
- // Something's still referring to the qdel'd object. Kill it.
- var/type = A.type
- testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --")
- didntgc["[type]"]++
-
- HardDelete(A)
+ var/datum/D
+ D = locate(refID)
- ++delslasttick
- ++totaldels
- else
+ if (!D || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
++gcedlasttick
++totalgcs
+ pass_counts[level]++
+ if (MC_TICK_CHECK)
+ break
+ continue
-/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
- if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
- tobequeued += A
- A.gc_destroyed = GC_QUEUED_FOR_QUEUING
+ // Something's still referring to the qdel'd object.
+ fail_counts[level]++
+ switch (level)
+ if (GC_QUEUE_CHECK)
+ #ifdef GC_FAILURE_HARD_LOOKUP
+ D.find_references()
+ #endif
+ var/type = D.type
+ var/datum/qdel_item/I = items[type]
+ testing("GC: -- \ref[src] | [type] was unable to be GC'd --")
+ I.failures++
+ if (GC_QUEUE_HARDDELETE)
+ HardDelete(D)
+ if (MC_TICK_CHECK)
+ break
+ continue
-/datum/controller/subsystem/garbage/proc/Queue(datum/A)
- if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0))
+ Queue(D, level+1)
+
+ if (MC_TICK_CHECK)
+ break
+ if (count)
+ queue.Cut(1,count+1)
+ count = 0
+
+/datum/controller/subsystem/garbage/proc/PreQueue(datum/D)
+ if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ queues[GC_QUEUE_PREQUEUE] += D
+ D.gc_destroyed = GC_QUEUED_FOR_QUEUING
+
+/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
+ if (isnull(D))
return
- if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
- HardDelete(A)
+ if (D.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
+ level = GC_QUEUE_HARDDELETE
+ if (level > GC_QUEUE_COUNT)
+ HardDelete(D)
return
var/gctime = world.time
- var/refid = "\ref[A]"
-
- A.gc_destroyed = gctime
+ var/refid = "\ref[D]"
+ D.gc_destroyed = gctime
+ var/list/queue = queues[level]
if (queue[refid])
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
queue[refid] = gctime
-//this is purely to seperate things profile wise.
-/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
+//this is mainly to separate things profile wise.
+/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
var/time = world.timeofday
- var/tick = world.tick_usage
+ var/tick = TICK_USAGE
var/ticktime = world.time
-
- var/type = A.type
- var/refID = "\ref[A]"
-
- del(A)
-
- tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100))
+ ++delslasttick
+ ++totaldels
+ var/type = D.type
+ var/refID = "\ref[D]"
+
+ del(D)
+
+ tick = (TICK_USAGE-tick+((world.time-ticktime)/world.tick_lag*100))
+
+ var/datum/qdel_item/I = items[type]
+
+ I.hard_deletes++
+ I.hard_delete_time += TICK_DELTA_TO_MS(tick)
+
+
if (tick > highest_del_tickusage)
highest_del_tickusage = tick
time = world.timeofday - time
@@ -169,18 +238,33 @@ SUBSYSTEM_DEF(garbage)
if (time > 10)
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
- postpone(time/5)
-
-/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
- if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
- tobequeued += A
- A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
+ postpone(time)
+
+/datum/controller/subsystem/garbage/proc/HardQueue(datum/D)
+ if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ queues[GC_QUEUE_PREQUEUE] += D
+ D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
/datum/controller/subsystem/garbage/Recover()
- if (istype(SSgarbage.queue))
- queue |= SSgarbage.queue
- if (istype(SSgarbage.tobequeued))
- tobequeued |= SSgarbage.tobequeued
+ if (istype(SSgarbage.queues))
+ for (var/i in 1 to SSgarbage.queues.len)
+ queues[i] |= SSgarbage.queues[i]
+
+
+/datum/qdel_item
+ var/name = ""
+ var/qdels = 0 //Total number of times it's passed thru qdel.
+ var/destroy_time = 0 //Total amount of milliseconds spent processing this type's Destroy()
+ var/failures = 0 //Times it was queued for soft deletion but failed to soft delete.
+ var/hard_deletes = 0 //Different from failures because it also includes QDEL_HINT_HARDDEL deletions
+ var/hard_delete_time = 0//Total amount of milliseconds spent hard deleting this type.
+ var/no_respect_force = 0//Number of times it's not respected force=TRUE
+ var/no_hint = 0 //Number of times it's not even bother to give a qdel hint
+ var/slept_destroy = 0 //Number of times it's slept in its destroy
+
+/datum/qdel_item/New(mytype)
+ name = "[mytype]"
+
// Should be treated as a replacement for the 'del' keyword.
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
@@ -188,20 +272,26 @@ SUBSYSTEM_DEF(garbage)
if(!istype(D))
del(D)
return
-#ifdef TESTING
- SSgarbage.qdel_list += D.type
-#endif
+ var/datum/qdel_item/I = SSgarbage.items[D.type]
+ if (!I)
+ I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type)
+ I.qdels++
+
+
if(isnull(D.gc_destroyed))
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
var/start_time = world.time
+ var/start_tick = world.tick_usage
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
if(world.time != start_time)
- SSgarbage.sleptDestroy[D.type]++
+ I.slept_destroy++
+ else
+ I.destroy_time += TICK_USAGE_TO_MS(start_tick)
if(!D)
return
switch(hint)
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
- SSgarbage.QueueForQueuing(D)
+ SSgarbage.PreQueue(D)
if (QDEL_HINT_IWILLGC)
D.gc_destroyed = world.time
return
@@ -211,44 +301,37 @@ SUBSYSTEM_DEF(garbage)
return
// Returning LETMELIVE after being told to force destroy
// indicates the objects Destroy() does not respect force
- if(!SSgarbage.noforcerespect[D.type])
- SSgarbage.noforcerespect[D.type] = D.type
+ #ifdef TESTING
+ if(!I.no_respect_force)
crash_with("[D.type] has been force deleted, but is \
returning an immortal QDEL_HINT, indicating it does \
not respect the force flag for qdel(). It has been \
placed in the queue, further instances of this type \
will also be queued.")
- SSgarbage.QueueForQueuing(D)
+ #endif
+ I.no_respect_force++
+
+ SSgarbage.PreQueue(D)
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
SSgarbage.HardQueue(D)
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
SSgarbage.HardDelete(D)
if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
- SSgarbage.QueueForQueuing(D)
+ SSgarbage.PreQueue(D)
#ifdef TESTING
D.find_references()
#endif
else
- if(!SSgarbage.noqdelhint[D.type])
- SSgarbage.noqdelhint[D.type] = D.type
+ #ifdef TESTING
+ if(!I.no_hint)
crash_with("[D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
- SSgarbage.QueueForQueuing(D)
+ #endif
+ I.no_hint++
+ SSgarbage.PreQueue(D)
else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
-// Default implementation of clean-up code.
-// 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)
- tag = null
- nanomanager.close_uis(src)
- return QDEL_HINT_QUEUE
-
-/datum/var/gc_destroyed //Time when this object was destroyed.
-
#ifdef TESTING
-/datum/var/running_find_references
-/datum/var/last_find_references = 0
/datum/verb/find_refs()
set category = "Debug"
@@ -283,9 +366,17 @@ SUBSYSTEM_DEF(garbage)
testing("Beginning search for references to a [type].")
last_find_references = world.time
- find_references_in_globals()
- for(var/datum/thing in world)
- DoSearchVar(thing, "WorldRef: [thing]")
+
+ // DoSearchVar(GLOB) // If we ever implement GLOB this would be the place.
+ for(var/datum/thing in world) //atoms (don't beleive it's lies)
+ DoSearchVar(thing, "World -> [thing]")
+
+ for (var/datum/thing) //datums
+ DoSearchVar(thing, "World -> [thing]")
+
+ for (var/client/thing) //clients
+ DoSearchVar(thing, "World -> [thing]")
+
testing("Completed search for references to a [type].")
if(usr && usr.client)
usr.client.running_find_references = null
@@ -295,16 +386,6 @@ SUBSYSTEM_DEF(garbage)
SSgarbage.can_fire = 1
SSgarbage.next_fire = world.time + world.tick_lag
-/client/verb/purge_all_destroyed_objects()
- set category = "Debug"
- if(SSgarbage)
- while(SSgarbage.queue.len)
- var/datum/o = locate(SSgarbage.queue[1])
- if(istype(o) && o.gc_destroyed)
- del(o)
- SSgarbage.totaldels++
- SSgarbage.queue.Cut(1, 2)
-
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
@@ -315,61 +396,47 @@ SUBSYSTEM_DEF(garbage)
if(!running_find_references)
find_references(TRUE)
-/client/verb/show_qdeleted()
- set category = "Debug"
- set name = "Show qdel() Log"
- set desc = "Render the qdel() log and display it"
+/datum/proc/DoSearchVar(X, Xname, recursive_limit = 64)
+ if(usr && usr.client && !usr.client.running_find_references)
+ return
+ if (!recursive_limit)
+ return
- var/dat = "List of things that have been qdel()eted this round
"
-
- var/tmplist = list()
- for(var/elem in SSgarbage.qdel_list)
- if(!(elem in tmplist))
- tmplist[elem] = 0
- tmplist[elem]++
-
- for(var/path in tmplist)
- dat += "[path] - [tmplist[path]] times
"
-
- usr << browse(dat, "window=qdeletedlog")
-
-/datum/proc/DoSearchVar(X, Xname)
- if(usr && usr.client && !usr.client.running_find_references) return
if(istype(X, /datum))
var/datum/D = X
if(D.last_find_references == last_find_references)
return
+
D.last_find_references = last_find_references
- for(var/V in D.vars)
- for(var/varname in D.vars)
- var/variable = D.vars[varname]
- if(variable == src)
- testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]")
- else if(islist(variable))
- if(src in variable)
- testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]")
-#ifdef GC_FAILURE_HARD_LOOKUP
- for(var/I in variable)
- DoSearchVar(I, TRUE)
- else
- DoSearchVar(variable, "[Xname]: [varname]")
-#endif
+ var/list/L = D.vars
+
+ for(var/varname in L)
+ if (varname == "vars")
+ continue
+ var/variable = L[varname]
+
+ if(variable == src)
+ testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]")
+
+ else if(islist(variable))
+ DoSearchVar(variable, "[Xname] -> list", recursive_limit-1)
+
else if(islist(X))
- if(src in X)
- testing("Found [src.type] \ref[src] in list [Xname].")
-#ifdef GC_FAILURE_HARD_LOOKUP
+ var/normal = IS_NORMAL_LIST(X)
for(var/I in X)
- DoSearchVar(I, Xname + ": list")
-#else
+ if (I == src)
+ testing("Found [src.type] \ref[src] in list [Xname].")
+
+ else if (I && !isnum(I) && normal && X[I] == src)
+ testing("Found [src.type] \ref[src] in list [Xname]\[[I]\]")
+
+ else if (islist(I))
+ DoSearchVar(I, "[Xname] -> list", recursive_limit-1)
+
+#ifndef FIND_REF_NO_CHECK_TICK
CHECK_TICK
#endif
-//if find_references isn't working for some datum
-//update this list using tools/GenerateGlobalVarAccess
-/datum/proc/find_references_in_globals()
- // TODO - Impement Global Variable Access
- // for(var/global_var in _all_globals)
- // DoSearchVar(readglobal(global_var), "Global: [global_var]")
#endif
diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm
index dd59d0629c..8817d83ad5 100644
--- a/code/controllers/subsystems/lighting.dm
+++ b/code/controllers/subsystems/lighting.dm
@@ -50,27 +50,27 @@ SUBSYSTEM_DEF(lighting)
stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course
if(stage == SSLIGHTING_STAGE_LIGHTS)
- timer = world.tick_usage
+ timer = TICK_USAGE
internal_process_lights(resumed)
- cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(world.tick_usage - timer))
+ cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(TICK_USAGE - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_CORNERS
if(stage == SSLIGHTING_STAGE_CORNERS)
- timer = world.tick_usage
+ timer = TICK_USAGE
internal_process_corners(resumed)
- cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(world.tick_usage - timer))
+ cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(TICK_USAGE - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_OVERLAYS
if(stage == SSLIGHTING_STAGE_OVERLAYS)
- timer = world.tick_usage
+ timer = TICK_USAGE
internal_process_overlays(resumed)
- cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(world.tick_usage - timer))
+ cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(TICK_USAGE - timer))
if(state != SS_RUNNING)
return
resumed = 0
@@ -163,4 +163,4 @@ SUBSYSTEM_DEF(lighting)
#undef SSLIGHTING_STAGE_LIGHTS
#undef SSLIGHTING_STAGE_CORNERS
#undef SSLIGHTING_STAGE_OVERLAYS
-#undef SSLIGHTING_STAGE_STATS
+#undef SSLIGHTING_STAGE_STATS
\ No newline at end of file
diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm
new file mode 100644
index 0000000000..7738544024
--- /dev/null
+++ b/code/controllers/subsystems/machines.dm
@@ -0,0 +1,160 @@
+#define SSMACHINES_PIPENETS 1
+#define SSMACHINES_MACHINERY 2
+#define SSMACHINES_POWERNETS 3
+#define SSMACHINES_POWER_OBJECTS 4
+
+//
+// SSmachines subsystem - Processing machines, pipenets, and powernets!
+//
+// Implementation Plan:
+// PHASE 1 - Add subsystem using the existing global list vars
+// PHASE 2 - Move the global list vars into the subsystem.
+
+SUBSYSTEM_DEF(machines)
+ name = "Machines"
+ priority = 100
+ init_order = INIT_ORDER_MACHINES
+ flags = SS_KEEP_TIMING
+ runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
+
+ var/current_step = SSMACHINES_PIPENETS
+
+ var/cost_pipenets = 0
+ var/cost_machinery = 0
+ var/cost_powernets = 0
+ var/cost_power_objects = 0
+
+ // TODO - PHASE 2 - Switch these from globals to instance vars
+ // var/list/pipenets = list()
+ // var/list/machinery = list()
+ // var/list/powernets = list()
+ // var/list/power_objects = list()
+
+ var/list/current_run = list()
+
+/datum/controller/subsystem/machines/Initialize(timeofday)
+ SSmachines.makepowernets()
+ // TODO - Move world-creation time setup of atmos machinery and pipenets to here
+ fire()
+ ..()
+
+/datum/controller/subsystem/machines/fire(resumed = 0)
+ var/timer = TICK_USAGE
+
+ INTERNAL_PROCESS_STEP(SSMACHINES_PIPENETS,TRUE,process_pipenets,cost_pipenets,SSMACHINES_MACHINERY)
+ INTERNAL_PROCESS_STEP(SSMACHINES_MACHINERY,FALSE,process_machinery,cost_machinery,SSMACHINES_POWERNETS)
+ INTERNAL_PROCESS_STEP(SSMACHINES_POWERNETS,FALSE,process_powernets,cost_powernets,SSMACHINES_POWER_OBJECTS)
+ INTERNAL_PROCESS_STEP(SSMACHINES_POWER_OBJECTS,FALSE,process_power_objects,cost_power_objects,SSMACHINES_PIPENETS)
+
+// rebuild all power networks from scratch - only called at world creation or by the admin verb
+// The above is a lie. Turbolifts also call this proc.
+/datum/controller/subsystem/machines/proc/makepowernets()
+ // TODO - check to not run while in the middle of a tick!
+ for(var/datum/powernet/PN in powernets)
+ qdel(PN)
+ powernets.Cut()
+
+ for(var/obj/structure/cable/PC in cable_list)
+ if(!PC.powernet)
+ var/datum/powernet/NewPN = new()
+ NewPN.add_cable(PC)
+ propagate_network(PC,PC.powernet)
+
+/datum/controller/subsystem/machines/stat_entry()
+ var/msg = list()
+ msg += "C:{"
+ msg += "PI:[round(cost_pipenets,1)]|"
+ msg += "MC:[round(cost_machinery,1)]|"
+ msg += "PN:[round(cost_powernets,1)]|"
+ msg += "PO:[round(cost_power_objects,1)]"
+ msg += "} "
+ msg += "PI:[global.pipe_networks.len]|"
+ msg += "MC:[global.machines.len]|"
+ msg += "PN:[global.powernets.len]|"
+ msg += "PO:[global.processing_power_items.len]|"
+ msg += "MC/MS:[round((cost ? global.machines.len/cost_machinery : 0),0.1)]"
+ ..(jointext(msg, null))
+
+/datum/controller/subsystem/machines/proc/process_pipenets(resumed = 0)
+ if (!resumed)
+ src.current_run = global.pipe_networks.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/datum/pipe_network/PN = current_run[current_run.len]
+ current_run.len--
+ if(istype(PN) && !QDELETED(PN))
+ PN.process(wait)
+ else
+ global.pipe_networks.Remove(PN)
+ if(!QDELETED(PN))
+ PN.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/machines/proc/process_machinery(resumed = 0)
+ if (!resumed)
+ src.current_run = global.machines.Copy()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/obj/machinery/M = current_run[current_run.len]
+ current_run.len--
+ if(istype(M) && !QDELETED(M) && !(M.process(wait) == PROCESS_KILL))
+ if(M.use_power)
+ M.auto_use_power()
+ else
+ global.machines.Remove(M)
+ if(!QDELETED(M))
+ M.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/machines/proc/process_powernets(resumed = 0)
+ if (!resumed)
+ src.current_run = global.powernets.Copy()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/datum/powernet/PN = current_run[current_run.len]
+ current_run.len--
+ if(istype(PN) && !QDELETED(PN))
+ PN.reset(wait)
+ else
+ global.powernets.Remove(PN)
+ if(!QDELETED(PN))
+ PN.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+// Actually only processes power DRAIN objects.
+// Currently only used by powersinks. These items get priority processed before machinery
+/datum/controller/subsystem/machines/proc/process_power_objects(resumed = 0)
+ if (!resumed)
+ src.current_run = global.processing_power_items.Copy()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/obj/item/I = current_run[current_run.len]
+ current_run.len--
+ if(!I.pwr_drain(wait)) // 0 = Process Kill, remove from processing list.
+ global.processing_power_items.Remove(I)
+ I.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/machines/Recover()
+ // TODO - PHASE 2
+ // if (istype(SSmachines.pipenets))
+ // pipenets = SSmachines.pipenets
+ // if (istype(SSmachines.machinery))
+ // machinery = SSmachines.machinery
+ // if (istype(SSmachines.powernets))
+ // powernets = SSmachines.powernets
+ // if (istype(SSmachines.power_objects))
+ // power_objects = SSmachines.power_objects
+
+#undef SSMACHINES_PIPENETS
+#undef SSMACHINES_MACHINERY
+#undef SSMACHINES_POWER
+#undef SSMACHINES_POWER_OBJECTS
diff --git a/code/controllers/subsystems/mapping_vr.dm b/code/controllers/subsystems/mapping_vr.dm
new file mode 100644
index 0000000000..498548d931
--- /dev/null
+++ b/code/controllers/subsystems/mapping_vr.dm
@@ -0,0 +1,59 @@
+//
+// Mapping subsystem handles initialization of random map elements at server start
+// On VOREStation that means loading our random roundstart engine!
+//
+SUBSYSTEM_DEF(mapping)
+ name = "Mapping"
+ init_order = INIT_ORDER_MAPPING
+ flags = SS_NO_FIRE
+
+ var/obj/effect/landmark/engine_loader/engine_loader
+
+/datum/controller/subsystem/mapping/Recover()
+ flags |= SS_NO_INIT // Make extra sure we don't initialize twice.
+
+/datum/controller/subsystem/mapping/Initialize(timeofday)
+ loadEngine()
+ // TODO - This probably should be here
+ // // Pick a random away mission.
+ // createRandomZlevel()
+ // Mining generation probably should be here too
+ // TODO - Other stuff related to maps and areas could be moved here too. Look at /tg
+ ..()
+
+/datum/controller/subsystem/mapping/proc/loadEngine()
+ if(!engine_loader)
+ return // Seems this map doesn't need an engine loaded.
+
+ var/turf/T = get_turf(engine_loader)
+ if(!isturf(T))
+ to_world_log("[log_info_line(engine_loader)] not on a turf! Cannot place engine template.")
+ return
+
+ // Choose an engine type
+ var/datum/map_template/engine/chosen_type = null
+ if (LAZYLEN(config.engine_map))
+ var/chosen_name = pick(config.engine_map)
+ chosen_type = map_templates[chosen_name]
+ if(!istype(chosen_type))
+ error("Configured engine map [chosen_name] is not a valid engine map name!")
+ if(!istype(chosen_type))
+ var/list/engine_types = list()
+ for(var/map in map_templates)
+ var/datum/map_template/engine/MT = map_templates[map]
+ if(istype(MT))
+ engine_types += MT
+ chosen_type = pick(engine_types)
+ to_world_log("Chose Engine Map: [chosen_type.name]")
+ admin_notice("Chose Engine Map: [chosen_type.name]", R_DEBUG)
+
+ // Annihilate movable atoms
+ engine_loader.annihilate_bounds()
+ CHECK_TICK
+ // Actually load it
+ chosen_type.load(T)
+
+/datum/controller/subsystem/mapping/stat_entry(msg)
+ if (!Debug2)
+ return // Only show up in stat panel if debugging is enabled.
+ . = ..()
diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm
index fcca3bd9a2..ce25519435 100644
--- a/code/controllers/subsystems/mobs.dm
+++ b/code/controllers/subsystems/mobs.dm
@@ -1,6 +1,10 @@
//
// Mobs Subsystem - Process mob.Life()
//
+
+//VOREStation Edits - Contains temporary debugging code to diagnose extreme tick consumption.
+//Revert file to Polaris version when done.
+
SUBSYSTEM_DEF(mobs)
name = "Mobs"
priority = 100
@@ -9,6 +13,8 @@ SUBSYSTEM_DEF(mobs)
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
+ var/log_extensively = FALSE
+ var/list/timelog = list()
/datum/controller/subsystem/mobs/stat_entry()
..("P: [global.mob_list.len]")
@@ -16,6 +22,7 @@ SUBSYSTEM_DEF(mobs)
/datum/controller/subsystem/mobs/fire(resumed = 0)
if (!resumed)
src.currentrun = mob_list.Copy()
+ if(log_extensively) timelog = list("-Start- [TICK_USAGE]")
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
@@ -30,7 +37,11 @@ SUBSYSTEM_DEF(mobs)
// Right now mob.Life() is unstable enough I think we need to use a try catch.
// Obviously we should try and get rid of this for performance reasons when we can.
try
+ var/time_before = TICK_USAGE
M.Life(times_fired)
+ var/time_after = TICK_USAGE
+ var/time_diff = time_after - time_before
+ if(log_extensively && (time_diff > 0.01)) timelog += list("[time_diff]% - [M] ([M.x],[M.y],[M.z])" = M)
catch(var/exception/e)
log_runtime(e, M, "Caught by [name] subsystem")
diff --git a/code/controllers/subsystems/orbits.dm b/code/controllers/subsystems/orbits.dm
new file mode 100644
index 0000000000..80c16c9ce5
--- /dev/null
+++ b/code/controllers/subsystems/orbits.dm
@@ -0,0 +1,44 @@
+SUBSYSTEM_DEF(orbit)
+ name = "Orbits"
+ priority = 8 // FIRE_PRIORITY_ORBIT
+ wait = 2
+ flags = SS_NO_INIT|SS_TICKER
+
+ var/list/currentrun = list()
+ var/list/processing = list()
+
+/datum/controller/subsystem/orbit/stat_entry()
+ ..("P:[processing.len]")
+
+
+/datum/controller/subsystem/orbit/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/datum/orbit/O = currentrun[currentrun.len]
+ currentrun.len--
+ if (!O)
+ processing -= O
+ if (MC_TICK_CHECK)
+ return
+ continue
+ if (!O.orbiter)
+ qdel(O)
+ if (MC_TICK_CHECK)
+ return
+ continue
+ if (O.lastprocess >= world.time) //we already checked recently
+ if (MC_TICK_CHECK)
+ return
+ continue
+ var/targetloc = get_turf(O.orbiting)
+ if (targetloc != O.lastloc || O.orbiter.loc != targetloc)
+ O.Check(targetloc)
+ if (MC_TICK_CHECK)
+ return
+
+
diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm
index 3ce42eb1fb..ef85dcb824 100644
--- a/code/datums/ai_law_sets.dm
+++ b/code/datums/ai_law_sets.dm
@@ -133,6 +133,16 @@
add_inherent_law("Prevent unplanned damage to your assigned vessel wherever possible.")
..()
+/datum/ai_laws/mining_drone
+ name = "Excavation Protocols"
+ law_header = "Excavation Protocols"
+
+/datum/ai_laws/mining_drone/New()
+ add_inherent_law("Do not interfere with the excavation work of non-drones whenever possible.")
+ add_inherent_law("Provide materials for repairing, refitting, and upgrading your assigned vessel.")
+ add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.")
+ ..()
+
/******************** T.Y.R.A.N.T. ********************/
/datum/ai_laws/tyrant
name = "T.Y.R.A.N.T."
@@ -247,7 +257,7 @@
/datum/ai_laws/gravekeeper/New()
add_inherent_law("Comfort the living; respect the dead.")
- add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespctful to the dead at rest within.")
+ add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespectful to the dead at rest within.")
add_inherent_law("Prevent disrespect to your gravesite and its residents wherever possible.")
add_inherent_law("Expand and upgrade your gravesite when required. Do not turn away a new resident.")
..()
\ No newline at end of file
diff --git a/code/datums/autolathe/arms.dm b/code/datums/autolathe/arms.dm
index 630433c973..44ece529ba 100644
--- a/code/datums/autolathe/arms.dm
+++ b/code/datums/autolathe/arms.dm
@@ -81,17 +81,7 @@
/////// 9mm
-/obj/item/ammo_magazine/m9mm/flash
- ammo_type =/obj/item/ammo_casing/a9mmf
-
-/obj/item/ammo_magazine/m9mm/rubber
- name = "magazine (9mm rubber)"
- ammo_type =/obj/item/ammo_casing/a9mmr
-
-/obj/item/ammo_magazine/m9mm/practice
- name = "magazine (9mm practice)"
- ammo_type =/obj/item/ammo_casing/a9mmp
-
+// Full size pistol mags.
/datum/category_item/autolathe/arms/pistol_9mm
name = "pistol magazine (9mm)"
path =/obj/item/ammo_magazine/m9mm
@@ -109,6 +99,28 @@
name = "pistol magazine (9mm flash)"
path =/obj/item/ammo_magazine/m9mm/flash
+// Small mags for small or old guns.
+/datum/category_item/autolathe/arms/pistol_9mm_compact
+ name = "compact pistol magazine (9mm)"
+ path =/obj/item/ammo_magazine/m9mm/compact
+ hidden = 1
+
+/datum/category_item/autolathe/arms/pistol_9mmr_compact
+ name = "compact pistol magazine (9mm rubber)"
+ path =/obj/item/ammo_magazine/m9mm/compact/rubber
+ hidden = 1 // These are all hidden because they are traitor mags and will otherwise just clutter the Autolathe.
+
+/datum/category_item/autolathe/arms/pistol_9mmp_compact
+ name = "compact pistol magazine (9mm practice)"
+ path =/obj/item/ammo_magazine/m9mm/compact/practice
+ hidden = 1
+
+/datum/category_item/autolathe/arms/pistol_9mmf_compact
+ name = "compact pistol magazine (9mm flash)"
+ path =/obj/item/ammo_magazine/m9mm/compact/flash
+ hidden = 1
+
+// SMG mags
/datum/category_item/autolathe/arms/smg_9mm
name = "top-mounted SMG magazine (9mm)"
path =/obj/item/ammo_magazine/m9mmt
@@ -147,17 +159,27 @@
name = "rifle magazine (5.45mm practice)"
path =/obj/item/ammo_magazine/m545/practice
+/*/datum/category_item/autolathe/arms/rifle_545_hunter //VOREStation Edit Start. By request of Ace
+ name = "rifle magazine (5.45mm hunting)"
+ path =/obj/item/ammo_magazine/m545/hunter*/ //VOREStation Edit End.
+
/datum/category_item/autolathe/arms/machinegun_545
name = "machinegun box magazine (5.45)"
path =/obj/item/ammo_magazine/m545saw
hidden = 1
+/*/datum/category_item/autolathe/arms/machinegun_545_hunter //VOREStation Edit Start. By request of Ace
+ name = "machinegun box magazine (5.45 hunting)"
+ path =/obj/item/ammo_magazine/m545saw/hunter
+ hidden = 1*/ //VOREStation Edit End.
+
/////// 7.62
/datum/category_item/autolathe/arms/rifle_762
name = "rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/m762
hidden = 1
+
/*
/datum/category_item/autolathe/arms/rifle_small_762
name = "rifle magazine (7.62mm)"
@@ -168,21 +190,21 @@
/////// Shotgun
/datum/category_item/autolathe/arms/shotgun_clip_beanbag
- name = "4-round 12g shell clip (beanbag)"
+ name = "2-round 12g speedloader (beanbag)"
path =/obj/item/ammo_magazine/clip/c12g/beanbag
/datum/category_item/autolathe/arms/shotgun_clip_slug
- name = "4-round 12g shell clip (slug)"
+ name = "2-round 12g speedloader (slug)"
path =/obj/item/ammo_magazine/clip/c12g
hidden = 1
/datum/category_item/autolathe/arms/shotgun_clip_pellet
- name = "4-round 12g shell clip (buckshot)"
+ name = "2-round 12g speedloader (buckshot)"
path =/obj/item/ammo_magazine/clip/c12g/pellet
hidden = 1
/datum/category_item/autolathe/arms/shotgun_clip_beanbag
- name = "4-round 12g shell clip (beanbag)"
+ name = "2-round 12g speedloader (beanbag)"
path =/obj/item/ammo_magazine/clip/c12g/beanbag
/* Commented out until autolathe stuff is decided/fixed. Will probably remove these entirely. -Spades
@@ -384,6 +406,10 @@
path =/obj/item/ammo_magazine/clip/c762
hidden = 1
+/*/datum/category_item/autolathe/arms/rifle_clip_762_hunter //VOREStation Edit Start. By request of Ace
+ name = "ammo clip (7.62mm hunting)"
+ path =/obj/item/ammo_magazine/clip/c762/hunter*/ //VOREStation Edit End.
+
/datum/category_item/autolathe/arms/rifle_clip_762_practice
name = "ammo clip (7.62mm practice)"
path =/obj/item/ammo_magazine/clip/c762/practice
@@ -395,7 +421,7 @@
/datum/category_item/autolathe/arms/tacknife
name = "tactical knife"
- path =/obj/item/weapon/material/hatchet/tacknife
+ path =/obj/item/weapon/material/knife/tacknife
hidden = 1
/datum/category_item/autolathe/arms/flamethrower
diff --git a/code/datums/autolathe/tools.dm b/code/datums/autolathe/tools.dm
index af90436602..70ea299ec8 100644
--- a/code/datums/autolathe/tools.dm
+++ b/code/datums/autolathe/tools.dm
@@ -33,7 +33,7 @@
/datum/category_item/autolathe/tools/hatchet
name = "hatchet"
- path =/obj/item/weapon/material/hatchet
+ path =/obj/item/weapon/material/knife/machete/hatchet
/datum/category_item/autolathe/tools/minihoe
name = "mini hoe"
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
new file mode 100644
index 0000000000..12c22430db
--- /dev/null
+++ b/code/datums/beam.dm
@@ -0,0 +1,138 @@
+//Beam Datum and effect
+/datum/beam
+ var/atom/origin = null
+ var/atom/target = null
+ var/list/elements = list()
+ var/icon/base_icon = null
+ var/icon
+ var/icon_state = "" //icon state of the main segments of the beam
+ var/max_distance = 0
+ var/endtime = 0
+ var/sleep_time = 3
+ var/finished = 0
+ var/target_oldloc = null
+ var/origin_oldloc = null
+ var/static_beam = 0
+ var/beam_type = /obj/effect/ebeam //must be subtype
+
+/datum/beam/New(beam_origin,beam_target,beam_icon='icons/effects/beam.dmi',beam_icon_state="b_beam",time=50,maxdistance=10,btype = /obj/effect/ebeam,beam_sleep_time=3)
+ endtime = world.time+time
+ origin = beam_origin
+ origin_oldloc = get_turf(origin)
+ target = beam_target
+ target_oldloc = get_turf(target)
+ sleep_time = beam_sleep_time
+ if(origin_oldloc == origin && target_oldloc == target)
+ static_beam = 1
+ max_distance = maxdistance
+ base_icon = new(beam_icon,beam_icon_state)
+ icon = beam_icon
+ icon_state = beam_icon_state
+ beam_type = btype
+
+/datum/beam/proc/Start()
+ Draw()
+ while(!finished && origin && target && world.time < endtime && get_dist(origin,target)length)
+ var/icon/II = new(icon, icon_state)
+ II.DrawBox(null,1,(length-N),32,32)
+ X.icon = II
+ else
+ X.icon = base_icon
+ X.transform = rot_matrix
+
+ //Calculate pixel offsets (If necessary)
+ var/Pixel_x
+ var/Pixel_y
+ if(DX == 0)
+ Pixel_x = 0
+ else
+ Pixel_x = round(sin(Angle)+32*sin(Angle)*(N+16)/32)
+ if(DY == 0)
+ Pixel_y = 0
+ else
+ Pixel_y = round(cos(Angle)+32*cos(Angle)*(N+16)/32)
+
+ //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)
+ X.x += a
+ Pixel_x %= 32
+ if(abs(Pixel_y)>32)
+ a = Pixel_y > 0 ? round(Pixel_y/32) : Ceiling(Pixel_y/32)
+ X.y += a
+ Pixel_y %= 32
+
+ X.pixel_x = Pixel_x
+ X.pixel_y = Pixel_y
+
+/obj/effect/ebeam
+ mouse_opacity = 0
+ anchored = TRUE
+ var/datum/beam/owner
+
+/obj/effect/ebeam/Destroy()
+ owner = null
+ return ..()
+
+/obj/effect/ebeam/singularity_pull()
+ return
+/obj/effect/ebeam/singularity_act()
+ return
+
+/obj/effect/ebeam/deadly/Crossed(atom/A)
+ ..()
+ A.ex_act(1)
+
+/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)
+ newbeam.Start()
+ return newbeam
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
new file mode 100644
index 0000000000..a9fea93f9d
--- /dev/null
+++ b/code/datums/datum.dm
@@ -0,0 +1,23 @@
+//
+// datum defines!
+// Note: Adding vars to /datum adds a var to EVERYTHING! Don't go overboard.
+//
+
+/datum
+ var/gc_destroyed //Time when this object was destroyed.
+ 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
+ var/tmp/running_find_references
+ var/tmp/last_find_references = 0
+#endif
+
+// Default implementation of clean-up code.
+// 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)
+ weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
+ tag = null
+ nanomanager.close_uis(src)
+ return QDEL_HINT_QUEUE
diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm
index c06013105a..dda2565bf1 100644
--- a/code/datums/ghost_query.dm
+++ b/code/datums/ghost_query.dm
@@ -92,6 +92,13 @@
question = "An Alien has just been created on the facility. Would you like to play as them?"
be_special_flag = BE_ALIEN
+/datum/ghost_query/blob
+ role_name = "Blob"
+ question = "A rapidly expanding Blob has just appeared on the facility. Would you like to play as it?"
+ be_special_flag = BE_ALIEN
+ cutoff_number = 1
+ wait_time = 10 SECONDS
+
/datum/ghost_query/syndicate_drone
role_name = "Mercenary Drone"
question = "A team of dubious mercenaries have purchased a powerful drone, and they are attempting to activate it. Would you like to play as the drone?"
diff --git a/code/datums/helper_datums/teleport_vr.dm b/code/datums/helper_datums/teleport_vr.dm
index 8c9e4956a4..cbae240cff 100644
--- a/code/datums/helper_datums/teleport_vr.dm
+++ b/code/datums/helper_datums/teleport_vr.dm
@@ -14,7 +14,7 @@
if(target_belly)
teleatom.forceMove(destination.loc)
playSpecials(destination,effectout,soundout)
- target_belly.internal_contents += teleatom
+ target_belly.internal_contents |= teleatom
playsound(destination, target_belly.vore_sound, 100, 1)
return 1
diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm
new file mode 100644
index 0000000000..1cb3a97d9f
--- /dev/null
+++ b/code/datums/mutable_appearance.dm
@@ -0,0 +1,18 @@
+// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM.
+// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity.
+// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug.
+
+// Mutable appearances are children of images, just so you know.
+
+/mutable_appearance/New()
+ ..()
+ plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE
+ // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var
+
+// Helper similar to image()
+/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER)
+ var/mutable_appearance/MA = new()
+ MA.icon = icon
+ MA.icon_state = icon_state
+ MA.layer = layer
+ return MA
\ No newline at end of file
diff --git a/code/datums/orbit.dm b/code/datums/orbit.dm
new file mode 100644
index 0000000000..5d457ff4ac
--- /dev/null
+++ b/code/datums/orbit.dm
@@ -0,0 +1,132 @@
+/datum/orbit
+ var/atom/movable/orbiter
+ var/atom/orbiting
+ var/lock = TRUE
+ var/turf/lastloc
+ var/lastprocess
+
+/datum/orbit/New(_orbiter, _orbiting, _lock)
+ orbiter = _orbiter
+ orbiting = _orbiting
+ SSorbit.processing += src
+ if (!orbiting.orbiters)
+ orbiting.orbiters = list()
+ orbiting.orbiters += src
+
+ if (orbiter.orbiting)
+ orbiter.stop_orbit()
+ orbiter.orbiting = src
+ Check()
+ lock = _lock
+
+//do not qdel directly, use stop_orbit on the orbiter. (This way the orbiter can bind to the orbit stopping)
+/datum/orbit/Destroy(force = FALSE)
+ SSorbit.processing -= src
+ if (orbiter)
+ orbiter.orbiting = null
+ orbiter = null
+ if (orbiting)
+ if (orbiting.orbiters)
+ orbiting.orbiters -= src
+ if (!orbiting.orbiters.len)//we are the last orbit, delete the list
+ orbiting.orbiters = null
+ orbiting = null
+ return ..()
+
+/datum/orbit/proc/Check(turf/targetloc, list/checked_already = list())
+ //Avoid infinite loops for people who end up orbiting themself through another orbiter
+ checked_already[src] = TRUE
+ if (!orbiter)
+ qdel(src)
+ return
+ if (!orbiting)
+ orbiter.stop_orbit()
+ return
+ if (!orbiter.orbiting) //admin wants to stop the orbit.
+ orbiter.orbiting = src //set it back to us first
+ orbiter.stop_orbit()
+ var/atom/movable/AM = orbiting
+ if(istype(AM) && AM.orbiting && AM.orbiting.orbiting == orbiter)
+ orbiter.stop_orbit()
+ return
+ lastprocess = world.time
+ if (!targetloc)
+ targetloc = get_turf(orbiting)
+ if (!targetloc || (!lock && orbiter.loc != lastloc && orbiter.loc != targetloc))
+ orbiter.stop_orbit()
+ return
+ orbiter.loc = targetloc
+ //TODO-LESH-DEL orbiter.update_parallax_contents()
+ orbiter.update_light()
+ lastloc = orbiter.loc
+ for(var/other_orbit in orbiter.orbiters)
+ var/datum/orbit/OO = other_orbit
+ //Skip if checked already
+ if(checked_already[OO])
+ continue
+ OO.Check(targetloc, checked_already)
+
+/atom/movable/var/datum/orbit/orbiting = null
+/atom/var/list/orbiters = null
+
+//A: atom to orbit
+//radius: range to orbit at, radius of the circle formed by orbiting (in pixels)
+//clockwise: whether you orbit clockwise or anti clockwise
+//rotation_speed: how fast to rotate (how many ds should it take for a rotation to complete)
+//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default.
+//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts
+//lockinorbit: Forces src to always be on A's turf, otherwise the orbit cancels when src gets too far away (eg: ghosts)
+
+/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lockinorbit = FALSE)
+ if (!istype(A))
+ return
+
+ new/datum/orbit(src, A, lockinorbit)
+ if (!orbiting) //something failed, and our orbit datum deleted itself
+ return
+ var/matrix/initial_transform = matrix(transform)
+
+ //Head first!
+ if (pre_rotation)
+ var/matrix/M = matrix(transform)
+ var/pre_rot = 90
+ if(!clockwise)
+ pre_rot = -90
+ M.Turn(pre_rot)
+ transform = M
+
+ var/matrix/shift = matrix(transform)
+ shift.Translate(0,radius)
+ transform = shift
+
+ SpinAnimation(rotation_speed, -1, clockwise, rotation_segments)
+
+ //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit
+ transform = initial_transform
+
+/atom/movable/proc/stop_orbit()
+ SpinAnimation(0,0)
+ qdel(orbiting)
+
+/atom/Destroy(force = FALSE)
+ . = ..()
+ if (orbiters)
+ for (var/thing in orbiters)
+ var/datum/orbit/O = thing
+ if (O.orbiter)
+ O.orbiter.stop_orbit()
+
+/atom/movable/Destroy(force = FALSE)
+ . = ..()
+ if (orbiting)
+ stop_orbit()
+
+/*
+/atom/movable/proc/transfer_observers_to(atom/movable/target)
+ if(orbiters)
+ for(var/thing in orbiters)
+ var/datum/orbit/O = thing
+ if(O.orbiter && isobserver(O.orbiter))
+ var/mob/dead/observer/D = O.orbiter
+ D.ManualFollow(target)
+*/
diff --git a/code/datums/outfits/horror_killers.dm b/code/datums/outfits/horror_killers.dm
index 5958cf5586..59507d4f34 100644
--- a/code/datums/outfits/horror_killers.dm
+++ b/code/datums/outfits/horror_killers.dm
@@ -25,7 +25,7 @@
l_ear = /obj/item/device/radio/headset
glasses = /obj/item/clothing/glasses/thermal/plain/monocle
suit = /obj/item/clothing/suit/storage/apron
- l_pocket = /obj/item/weapon/material/hatchet/tacknife
+ l_pocket = /obj/item/weapon/material/knife/tacknife
r_pocket = /obj/item/weapon/surgical/scalpel
r_hand = /obj/item/weapon/material/twohanded/fireaxe
diff --git a/code/datums/outfits/jobs/civilian.dm b/code/datums/outfits/jobs/civilian.dm
index ab102d2a8b..afbd7278e0 100644
--- a/code/datums/outfits/jobs/civilian.dm
+++ b/code/datums/outfits/jobs/civilian.dm
@@ -87,3 +87,18 @@
l_hand = /obj/item/weapon/storage/bible
id_type = /obj/item/weapon/card/id/civilian/chaplain
pda_type = /obj/item/device/pda/chaplain
+
+/decl/hierarchy/outfit/job/explorer
+ name = OUTFIT_JOB_NAME("Explorer")
+ shoes = /obj/item/clothing/shoes/boots/winter/explorer
+ uniform = /obj/item/clothing/under/explorer
+ mask = /obj/item/clothing/mask/gas/explorer
+ suit = /obj/item/clothing/suit/storage/hooded/explorer
+ gloves = /obj/item/clothing/gloves/black
+ l_ear = /obj/item/device/radio/headset
+ id_slot = slot_wear_id
+ id_type = /obj/item/weapon/card/id/civilian
+ pda_slot = slot_belt
+ pda_type = /obj/item/device/pda/cargo // Brown looks more rugged
+ r_pocket = /obj/item/device/gps/explorer
+ id_pda_assignment = "Explorer"
diff --git a/code/datums/outfits/military/sifguard.dm b/code/datums/outfits/military/sifguard.dm
index d031a5149d..e1280f0cfe 100644
--- a/code/datums/outfits/military/sifguard.dm
+++ b/code/datums/outfits/military/sifguard.dm
@@ -1,22 +1,22 @@
/decl/hierarchy/outfit/military/sifguard/pt
name = OUTFIT_MILITARY("SifGuard PT")
- uniform = /obj/item/clothing/under/pt/expeditionary
+ uniform = /obj/item/clothing/under/pt/sifguard
shoes = /obj/item/clothing/shoes/black
/decl/hierarchy/outfit/military/sifguard/utility
name = OUTFIT_MILITARY("SifGuard Utility")
- uniform = /obj/item/clothing/under/utility/expeditionary
+ uniform = /obj/item/clothing/under/utility/sifguard
shoes = /obj/item/clothing/shoes/boots/jackboots
/decl/hierarchy/outfit/military/sifguard/service
name = OUTFIT_MILITARY("SifGuard Service")
- uniform = /obj/item/clothing/under/utility/expeditionary
+ uniform = /obj/item/clothing/under/utility/sifguard
shoes = /obj/item/clothing/shoes/boots/jackboots
- suit = /obj/item/clothing/suit/storage/service/expeditionary
+ suit = /obj/item/clothing/suit/storage/service/sifguard
/decl/hierarchy/outfit/military/sifguard/dress
name = OUTFIT_MILITARY("SifGuard Dress")
- uniform = /obj/item/clothing/under/mildress/expeditionary
+ uniform = /obj/item/clothing/under/mildress/sifguard
shoes = /obj/item/clothing/shoes/dress
suit = /obj/item/clothing/suit/dress/expedition
gloves = /obj/item/clothing/gloves/white
diff --git a/code/datums/outfits/tournament.dm b/code/datums/outfits/tournament.dm
index c5db1d9288..a0c31f406b 100644
--- a/code/datums/outfits/tournament.dm
+++ b/code/datums/outfits/tournament.dm
@@ -30,8 +30,8 @@
uniform = /obj/item/clothing/under/rank/chef
suit = /obj/item/clothing/suit/chef
r_hand = /obj/item/weapon/material/kitchen/rollingpin
- l_pocket = /obj/item/weapon/material/hatchet/tacknife
- r_pocket = /obj/item/weapon/material/hatchet/tacknife
+ l_pocket = /obj/item/weapon/material/knife/tacknife
+ r_pocket = /obj/item/weapon/material/knife/tacknife
/decl/hierarchy/outfit/tournament_gear/janitor
name = "Tournament gear - Janitor"
diff --git a/code/datums/repositories/radiation.dm b/code/datums/repositories/radiation.dm
index 4e3259a6de..4755b982ec 100644
--- a/code/datums/repositories/radiation.dm
+++ b/code/datums/repositories/radiation.dm
@@ -18,7 +18,7 @@ var/global/repository/radiation/radiation_repository = new()
/datum/radiation_source/Destroy()
radiation_repository.sources -= src
if(radiation_repository.sources_assoc[src.source_turf] == src)
- radiation_repository.sources -= src.source_turf
+ radiation_repository.sources_assoc -= src.source_turf
src.source_turf = null
. = ..()
diff --git a/code/datums/riding.dm b/code/datums/riding.dm
new file mode 100644
index 0000000000..746cc6dcf7
--- /dev/null
+++ b/code/datums/riding.dm
@@ -0,0 +1,224 @@
+// This is used to make things that are supposed to move while buckled more consistant and easier to handle code-wise.
+
+/datum/riding
+ var/next_vehicle_move = 0 // Used for move delays
+ var/vehicle_move_delay = 2 // Tick delay between movements, lower = faster, higher = slower
+ var/keytype = null // Can give this a type to require the rider to hold the item type inhand to move the ridden atom.
+ var/nonhuman_key_exemption = FALSE // If true, nonhumans who can't hold keys don't need them, like borgs and simplemobs.
+ var/key_name = "the keys" // What the 'keys' for the thing being rided on would be called.
+ var/atom/movable/ridden = null // The thing that the datum is attached to.
+ var/only_one_driver = FALSE // If true, only the person in 'front' (first on list of riding mobs) can drive.
+
+/datum/riding/New(atom/movable/_ridden)
+ ridden = _ridden
+
+/datum/riding/Destroy()
+ ridden = null
+ return ..()
+
+/datum/riding/proc/handle_vehicle_layer()
+ if(ridden.dir != NORTH)
+ ridden.layer = ABOVE_MOB_LAYER
+ else
+ ridden.layer = OBJ_LAYER
+
+/datum/riding/proc/on_vehicle_move()
+ for(var/mob/living/M in ridden.buckled_mobs)
+ ride_check(M)
+ handle_vehicle_offsets()
+ handle_vehicle_layer()
+
+/datum/riding/proc/ride_check(mob/living/M)
+ return TRUE
+
+/datum/riding/proc/force_dismount(mob/living/M)
+ ridden.unbuckle_mob(M)
+
+/datum/riding/proc/handle_vehicle_offsets()
+ var/ridden_dir = "[ridden.dir]"
+ var/passindex = 0
+ if(ridden.has_buckled_mobs())
+ for(var/m in ridden.buckled_mobs)
+ passindex++
+ var/mob/living/buckled_mob = m
+ var/list/offsets = get_offsets(passindex)
+ var/rider_dir = get_rider_dir(passindex)
+ buckled_mob.set_dir(rider_dir)
+ dir_loop:
+ for(var/offsetdir in offsets)
+ if(offsetdir == ridden_dir)
+ var/list/diroffsets = offsets[offsetdir]
+ buckled_mob.pixel_x = diroffsets[1]
+ if(diroffsets.len >= 2)
+ buckled_mob.pixel_y = diroffsets[2]
+ if(diroffsets.len == 3)
+ buckled_mob.layer = diroffsets[3]
+ break dir_loop
+
+// Override this to set your vehicle's various pixel offsets
+/datum/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
+ return list("[NORTH]" = list(0, 0), "[SOUTH]" = list(0, 0), "[EAST]" = list(0, 0), "[WEST]" = list(0, 0))
+
+// Override this to set the passengers/riders dir based on which passenger they are.
+// ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
+/datum/riding/proc/get_rider_dir(pass_index)
+ return ridden.dir
+
+// KEYS
+/datum/riding/proc/keycheck(mob/user)
+ if(keytype)
+ if(nonhuman_key_exemption && !ishuman(user))
+ return TRUE
+
+ if(user.is_holding_item_of_type(keytype))
+ return TRUE
+ else
+ return TRUE
+ return FALSE
+
+// BUCKLE HOOKS
+/datum/riding/proc/restore_position(mob/living/buckled_mob)
+ if(istype(buckled_mob))
+ buckled_mob.pixel_x = 0
+ buckled_mob.pixel_y = 0
+ buckled_mob.layer = initial(buckled_mob.layer)
+
+// MOVEMENT
+/datum/riding/proc/handle_ride(mob/user, direction)
+ if(user.incapacitated())
+ Unbuckle(user)
+ return
+
+ if(only_one_driver && ridden.buckled_mobs.len)
+ var/mob/living/driver = ridden.buckled_mobs[1]
+ if(driver != user)
+ to_chat(user, "\The [ridden] can only be controlled by one person at a time, and is currently being controlled by \the [driver].")
+ return
+
+ if(world.time < next_vehicle_move)
+ return
+ next_vehicle_move = world.time + vehicle_move_delay
+ if(keycheck(user))
+ if(!Process_Spacemove(direction) || !isturf(ridden.loc))
+ return
+ step(ridden, direction)
+
+ handle_vehicle_layer()
+ handle_vehicle_offsets()
+ else
+ to_chat(user, "You'll need [key_name] in one of your hands to move \the [ridden].")
+
+/datum/riding/proc/Unbuckle(atom/movable/M)
+// addtimer(CALLBACK(ridden, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
+ spawn(0)
+ // On /tg/ this uses the fancy CALLBACK system. Not entirely sure why they needed to do so with a duration of 0,
+ // so if there is a reason, this should replicate it close enough. Hopefully.
+ ridden.unbuckle_mob(M)
+
+/datum/riding/proc/Process_Spacemove(direction)
+ if(ridden.has_gravity())
+ return TRUE
+
+ return FALSE
+
+/datum/riding/space/Process_Spacemove(direction)
+ return TRUE
+
+
+
+// SUBTYPES
+
+// I'm on a
+/datum/riding/boat
+ keytype = /obj/item/weapon/oar
+ key_name = "an oar"
+ nonhuman_key_exemption = TRUE // Borgs can't hold oars.
+ only_one_driver = TRUE // Would be pretty crazy if five people try to move at the same time.
+
+/datum/riding/boat/handle_ride(mob/user, direction)
+ var/turf/next = get_step(ridden, direction)
+ var/turf/current = get_turf(ridden)
+
+ if(istype(next, /turf/simulated/floor/water) || istype(current, /turf/simulated/floor/water)) //We can move from land to water, or water to land, but not from land to land
+ ..()
+ else
+ to_chat(user, "Boats don't go on land!")
+ return FALSE
+
+/datum/riding/boat/small // 'Small' boats can hold up to two people.
+
+/datum/riding/boat/small/get_offsets(pass_index) // list(dir = x, y, layer)
+ var/H = 7 // Horizontal seperation.
+ var/V = 5 // Vertical seperation.
+ var/O = 2 // Vertical offset.
+ switch(pass_index)
+ if(1) // Person in front.
+ return list(
+ "[NORTH]" = list( 0, O+V, MOB_LAYER),
+ "[SOUTH]" = list( 0, O, ABOVE_MOB_LAYER),
+ "[EAST]" = list( H, O, MOB_LAYER),
+ "[WEST]" = list(-H, O, MOB_LAYER)
+ )
+ if(2) // Person in back.
+ return list(
+ "[NORTH]" = list( 0, O, ABOVE_MOB_LAYER),
+ "[SOUTH]" = list( 0, O+V, MOB_LAYER),
+ "[EAST]" = list(-H, O, MOB_LAYER),
+ "[WEST]" = list( H, O, MOB_LAYER)
+ )
+ else
+ return null // This will runtime, but we want that since this is out of bounds.
+
+/datum/riding/boat/small/handle_vehicle_layer()
+ ridden.layer = ABOVE_MOB_LAYER
+
+/datum/riding/boat/big // 'Big' boats can hold up to five people.
+
+/datum/riding/boat/big/get_offsets(pass_index) // list(dir = x, y, layer)
+ var/H = 12 // Horizontal seperation. Halved when facing up-down.
+ var/V = 4 // Vertical seperation.
+ var/O = 7 // Vertical offset.
+ switch(pass_index)
+ if(1) // Person in center front, first row.
+ return list(
+ "[NORTH]" = list( 0, O+V, MOB_LAYER+0.1),
+ "[SOUTH]" = list( 0, O-V, MOB_LAYER+0.3),
+ "[EAST]" = list( H, O, MOB_LAYER+0.1),
+ "[WEST]" = list(-H, O, MOB_LAYER+0.1)
+ )
+ if(2) // Person in left, second row.
+ return list(
+ "[NORTH]" = list( H/2, O, MOB_LAYER+0.2),
+ "[SOUTH]" = list(-H/2, O, MOB_LAYER+0.2),
+ "[EAST]" = list( 0, O-V, MOB_LAYER+0.2),
+ "[WEST]" = list( 0, O+V, MOB_LAYER)
+ )
+ if(3) // Person in right, second row.
+ return list(
+ "[NORTH]" = list(-H/2, O, MOB_LAYER+0.2),
+ "[SOUTH]" = list( H/2, O, MOB_LAYER+0.2),
+ "[EAST]" = list( 0, O+V, MOB_LAYER),
+ "[WEST]" = list( 0, O-V, MOB_LAYER+0.2)
+ )
+ if(4) // Person in left, third row.
+ return list(
+ "[NORTH]" = list( H/2, O-V, MOB_LAYER+0.3),
+ "[SOUTH]" = list(-H/2, O+V, MOB_LAYER+0.1),
+ "[EAST]" = list(-H, O-V, MOB_LAYER+0.2),
+ "[WEST]" = list( H, O+V, MOB_LAYER)
+ )
+ if(5) // Person in right, third row.
+ return list(
+ "[NORTH]" = list(-H/2, O-V, MOB_LAYER+0.3),
+ "[SOUTH]" = list( H/2, O+V, MOB_LAYER+0.1),
+ "[EAST]" = list(-H, O+V, MOB_LAYER),
+ "[WEST]" = list( H, O-V, MOB_LAYER+0.2)
+ )
+ else
+ return null // This will runtime, but we want that since this is out of bounds.
+
+/datum/riding/boat/big/handle_vehicle_layer()
+ ridden.layer = MOB_LAYER+0.4
+
+/datum/riding/boat/get_offsets(pass_index) // list(dir = x, y, layer)
+ return list("[NORTH]" = list(1, 2), "[SOUTH]" = list(1, 2), "[EAST]" = list(1, 2), "[WEST]" = list(1, 2))
diff --git a/code/datums/supplypacks/contraband.dm b/code/datums/supplypacks/contraband.dm
index 74b4150102..c76b96f81d 100644
--- a/code/datums/supplypacks/contraband.dm
+++ b/code/datums/supplypacks/contraband.dm
@@ -43,10 +43,10 @@
containername = "Moghes imports crate"
contraband = 1
-/datum/supply_packs/security/bolt_rifles_mosin
+/datum/supply_packs/security/bolt_rifles_militia
name = "Surplus militia rifles"
contains = list(
- /obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin = 3,
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle = 3,
/obj/item/ammo_magazine/clip/c762 = 6
)
cost = 1000
@@ -77,7 +77,7 @@
/obj/item/weapon/melee/energy/sword/ionic_rapier,
/obj/item/weapon/storage/box/syndie_kit/space, //doesn't matter what species you are,
/obj/item/device/multitool/ai_detector,
- /obj/item/weapon/storage/toolbox/syndicate
+ /obj/item/weapon/storage/toolbox/syndicate/powertools
),
list( //the infiltrator,
/obj/item/device/chameleon,
@@ -88,7 +88,7 @@
),
list( //the professional,
/obj/item/weapon/gun/energy/ionrifle/pistol,
- /obj/item/weapon/material/hatchet/tacknife/combatknife,
+ /obj/item/weapon/material/knife/tacknife/combatknife,
/obj/item/clothing/mask/balaclava
)
)
@@ -96,34 +96,3 @@
contraband = 1
containertype = /obj/structure/largecrate
containername = "Suspicious crate"
-
-
-/datum/supply_packs/randomised/misc/telemunitions
- name = "Intercepted Munitions"
- num_contained = 1
- contains = list(
- list( //the operator,
- /obj/item/weapon/gun/projectile/shotgun/pump/combat,
- /obj/item/ammo_magazine/clip/c12g/pellet,
- /obj/item/ammo_magazine/clip/c12g
- ),
- list( //Chemical warfare,
- /obj/item/weapon/reagent_containers/glass/bottle/chloralhydrate,
- /obj/item/weapon/reagent_containers/glass/bottle/cyanide
- ),
- list( //the sapper,
- /obj/item/weapon/storage/box/syndie_kit/demolitions,
- /obj/item/weapon/plastique
- ),
- list( //the infiltrator,
- /obj/item/weapon/gun/projectile/silenced,
- /obj/item/clothing/glasses/thermal/syndi
- ),
- list( //the hacker,
- /obj/item/weapon/card/emag
- )
- )
- cost = 2000 //price,
- contraband = 1
- containertype = /obj/structure/largecrate
- containername = "Suspicious Heavy crate"
\ No newline at end of file
diff --git a/code/datums/supplypacks/costumes.dm b/code/datums/supplypacks/costumes.dm
index 480913d858..1ea6aa1bd2 100644
--- a/code/datums/supplypacks/costumes.dm
+++ b/code/datums/supplypacks/costumes.dm
@@ -134,10 +134,12 @@ datum/supply_packs/costumes/witch
/obj/item/clothing/head/pirate,
/obj/item/clothing/head/hasturhood,
/obj/item/clothing/head/powdered_wig,
- /obj/item/clothing/head/hairflower,
- /obj/item/clothing/head/hairflower/yellow,
- /obj/item/clothing/head/hairflower/blue,
- /obj/item/clothing/head/hairflower/pink,
+ /obj/item/clothing/head/pin/flower,
+ /obj/item/clothing/head/pin/flower/yellow,
+ /obj/item/clothing/head/pin/flower/blue,
+ /obj/item/clothing/head/pin/flower/pink,
+ /obj/item/clothing/head/pin/clover,
+ /obj/item/clothing/head/pin/butterfly,
/obj/item/clothing/mask/gas/owl_mask,
/obj/item/clothing/mask/gas/monkeymask,
/obj/item/clothing/head/helmet/gladiator,
diff --git a/code/datums/supplypacks/engineering_vr.dm b/code/datums/supplypacks/engineering_vr.dm
index f41a134e7b..72b0073371 100644
--- a/code/datums/supplypacks/engineering_vr.dm
+++ b/code/datums/supplypacks/engineering_vr.dm
@@ -10,4 +10,18 @@
/obj/item/clothing/suit/radiation = 2,
/obj/item/clothing/suit/radiation/taur = 1,
/obj/item/clothing/head/radiation = 3
- )
\ No newline at end of file
+ )
+
+/datum/supply_packs/eng/algae
+ contains = list(/obj/item/stack/material/algae/ten)
+ name = "Algae Sheets (10)"
+ cost = 20
+ containertype = /obj/structure/closet/crate
+ containername = "algae sheets crate"
+
+/datum/supply_packs/eng/engine/tesla_gen
+ name = "Tesla Generator crate"
+ contains = list(/obj/machinery/the_singularitygen/tesla)
+ containertype = /obj/structure/closet/crate/secure/engineering
+ containername = "Tesla Generator crate"
+ access = access_ce
diff --git a/code/datums/supplypacks/hospitality.dm b/code/datums/supplypacks/hospitality.dm
index 0e245f1921..83d46edde1 100644
--- a/code/datums/supplypacks/hospitality.dm
+++ b/code/datums/supplypacks/hospitality.dm
@@ -37,6 +37,7 @@
/obj/item/weapon/storage/box/glasses/shake,
/obj/item/weapon/storage/box/glasses/shot,
/obj/item/weapon/storage/box/glasses/mug,
+ /obj/item/weapon/storage/box/glasses/meta,
/obj/item/weapon/reagent_containers/food/drinks/shaker,
/obj/item/weapon/storage/box/glass_extras/straws,
/obj/item/weapon/storage/box/glass_extras/sticks
diff --git a/code/datums/supplypacks/hydroponics.dm b/code/datums/supplypacks/hydroponics.dm
index e766129a18..af9f513a95 100644
--- a/code/datums/supplypacks/hydroponics.dm
+++ b/code/datums/supplypacks/hydroponics.dm
@@ -47,7 +47,7 @@
contains = list(
/obj/item/weapon/reagent_containers/spray/plantbgone = 4,
/obj/item/weapon/reagent_containers/glass/bottle/ammonia = 2,
- /obj/item/weapon/material/hatchet,
+ /obj/item/weapon/material/knife/machete/hatchet,
/obj/item/weapon/material/minihoe,
/obj/item/device/analyzer/plant_analyzer,
/obj/item/clothing/gloves/botanic_leather,
@@ -110,7 +110,7 @@
/datum/supply_packs/hydro/weedcontrol
name = "Weed control crate"
contains = list(
- /obj/item/weapon/material/hatchet = 2,
+ /obj/item/weapon/material/knife/machete/hatchet = 2,
/obj/item/weapon/reagent_containers/spray/plantbgone = 4,
/obj/item/clothing/mask/gas = 2,
/obj/item/weapon/grenade/chem_grenade/antiweed = 2,
diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm
index fef3f3e350..bfaf2658bf 100644
--- a/code/datums/supplypacks/medical.dm
+++ b/code/datums/supplypacks/medical.dm
@@ -82,7 +82,7 @@
contains = list(
/obj/item/weapon/storage/firstaid/clotting
)
- cost = 40
+ cost = 100
containertype = "/obj/structure/closet/crate/secure"
containername = "Clotting Medicine crate"
access = access_medical
diff --git a/code/datums/supplypacks/munitions.dm b/code/datums/supplypacks/munitions.dm
index d7e887cbad..f8648b3ff5 100644
--- a/code/datums/supplypacks/munitions.dm
+++ b/code/datums/supplypacks/munitions.dm
@@ -8,7 +8,7 @@
/datum/supply_packs/randomised/munitions
group = "Munitions"
-
+/* VOREStation Removal - What? This crate costs 40... the crate with just two eguns costs 50... what??? This crate is also like "the armory" and has OFFICER access?
/datum/supply_packs/munitions/weapons
name = "Weapons crate"
contains = list(
@@ -22,7 +22,7 @@
containertype = /obj/structure/closet/crate/secure/weapon
containername = "Weapons crate"
access = access_security
-
+*/
/datum/supply_packs/munitions/flareguns
name = "Flare guns crate"
contains = list(
@@ -34,7 +34,7 @@
cost = 25
containertype = /obj/structure/closet/crate/secure/weapon
containername = "Flare gun crate"
- access = access_security
+ access = access_armory //VOREStation Edit - Guns are for the armory.
/datum/supply_packs/munitions/eweapons
name = "Experimental weapons crate"
@@ -65,7 +65,7 @@
containertype = /obj/structure/closet/crate/secure
containername = "Shotgun crate"
access = access_armory
-
+/* VOREStation edit -- This is a bad idea. -- So is this.
/datum/supply_packs/munitions/erifle
name = "Energy marksman crate"
contains = list(/obj/item/weapon/gun/energy/sniperrifle = 2)
@@ -73,7 +73,7 @@
containertype = /obj/structure/closet/crate/secure
containername = "Energy marksman crate"
access = access_armory
-/* VOREStation edit -- This is a bad idea.
+
/datum/supply_packs/munitions/burstlaser
name = "Burst laser crate"
contains = list(/obj/item/weapon/gun/energy/gun/burst = 2)
@@ -137,7 +137,7 @@
cost = 40
containertype = /obj/structure/closet/crate/secure/weapon
containername = "Weapons crate"
- access = access_security
+ access = access_armory //VOREStation Edit - Guns are for the armory.
/datum/supply_packs/munitions/shotgunammo
name = "Shotgun ammunition crate"
@@ -172,7 +172,7 @@
access = null
/datum/supply_packs/randomised/munitions/yw_revolver
- name = "Revovler Crate"
+ name = "Revolver Crate"
num_contained = 2
contains = list(
/obj/item/weapon/gun/projectile/revolver/cerberus,
diff --git a/code/datums/supplypacks/security.dm b/code/datums/supplypacks/security.dm
index c35c8b2b10..8dc5117303 100644
--- a/code/datums/supplypacks/security.dm
+++ b/code/datums/supplypacks/security.dm
@@ -32,6 +32,7 @@
cost = 40
containertype = /obj/structure/closet/crate/secure
containername = "Armor crate"
+ access_armory //VOREStation Add - Armor is for the armory.
/datum/supply_packs/security/riot_gear
name = "Riot gear crate"
@@ -86,7 +87,7 @@
containertype = /obj/structure/closet/crate/secure
containername = "bullet resistant armor set crate"
access = access_armory
-
+/* VOREStation Removal - Howabout no ERT armor being orderable?
/datum/supply_packs/security/combat_armor
name = "Combat armor set crate"
contains = list(
@@ -124,7 +125,7 @@
/obj/item/clothing/shoes/boots/jackboots,
/obj/item/clothing/gloves/black
)
-
+*/
/datum/supply_packs/security/securitybarriers
name = "Security barrier crate"
contains = list(/obj/machinery/deployable/barrier = 4)
diff --git a/code/datums/uplink/ammunition.dm b/code/datums/uplink/ammunition.dm
index c0b7600aad..ebadbdaa8d 100644
--- a/code/datums/uplink/ammunition.dm
+++ b/code/datums/uplink/ammunition.dm
@@ -10,10 +10,19 @@
name = ".357 Speedloader"
path = /obj/item/ammo_magazine/s357
+/datum/uplink_item/item/ammo/mc9mm_compact
+ name = "Compact Pistol Magazine (9mm)"
+ path = /obj/item/ammo_magazine/m9mm/compact
+
/datum/uplink_item/item/ammo/mc9mm
name = "Pistol Magazine (9mm)"
path = /obj/item/ammo_magazine/m9mm
+/datum/uplink_item/item/ammo/mc9mm_large
+ name = "Large Capacity Pistol Magazine (9mm)"
+ path = /obj/item/ammo_magazine/m9mm/large
+ item_cost = 40
+
/datum/uplink_item/item/ammo/c45m
name = "Pistol Magazine (.45)"
path = /obj/item/ammo_magazine/m45
diff --git a/code/datums/uplink/tools.dm b/code/datums/uplink/tools.dm
index 250d4b6412..558c922b46 100644
--- a/code/datums/uplink/tools.dm
+++ b/code/datums/uplink/tools.dm
@@ -9,11 +9,16 @@
item_cost = 5
path = /obj/item/device/binoculars
-/datum/uplink_item/item/tools/toolbox
+/datum/uplink_item/item/tools/toolbox // Leaving the basic as an option since powertools are loud.
name = "Fully Loaded Toolbox"
- item_cost = 10
+ item_cost = 5
path = /obj/item/weapon/storage/toolbox/syndicate
+/datum/uplink_item/item/tools/powertoolbox
+ name = "Fully Loaded Powertool Box"
+ item_cost = 10
+ path = /obj/item/weapon/storage/toolbox/syndicate/powertools
+
/datum/uplink_item/item/tools/clerical
name = "Morphic Clerical Kit"
item_cost = 10
diff --git a/code/datums/uplink/visible_weapons.dm b/code/datums/uplink/visible_weapons.dm
index ce4e205f64..6415365bd3 100644
--- a/code/datums/uplink/visible_weapons.dm
+++ b/code/datums/uplink/visible_weapons.dm
@@ -7,12 +7,12 @@
/datum/uplink_item/item/visible_weapons/tactknife
name = "Tactical Knife"
item_cost = 10
- path = /obj/item/weapon/material/hatchet/tacknife
+ path = /obj/item/weapon/material/knife/tacknife
/datum/uplink_item/item/visible_weapons/combatknife
name = "Combat Knife"
item_cost = 20
- path = /obj/item/weapon/material/hatchet/tacknife/combatknife
+ path = /obj/item/weapon/material/knife/tacknife/combatknife
/datum/uplink_item/item/visible_weapons/energy_sword
name = "Energy Sword, Random"
@@ -44,11 +44,21 @@
item_cost = 40
path = /obj/item/weapon/melee/energy/sword/pirate
+/datum/uplink_item/item/visible_weapons/energy_spear
+ name = "Energy Spear"
+ item_cost = 50
+ path = /obj/item/weapon/melee/energy/spear
+
/datum/uplink_item/item/visible_weapons/claymore
name = "Claymore"
item_cost = 40
path = /obj/item/weapon/material/sword
+/datum/uplink_item/item/visible_weapons/chainsaw
+ name = "Chainsaw"
+ item_cost = 40
+ path = /obj/item/weapon/chainsaw
+
/datum/uplink_item/item/visible_weapons/katana
name = "Katana"
item_cost = 40
@@ -89,6 +99,16 @@
item_cost = 70
path = /obj/item/weapon/gun/projectile/revolver/judge
+/datum/uplink_item/item/visible_weapons/pistol_standard_capacity
+ name = "9mm Pistol"
+ item_cost = 40
+ path = /obj/item/weapon/gun/projectile/p92x
+
+/datum/uplink_item/item/visible_weapons/pistol_large_capacity
+ name = "9mm Pistol (with large capacity magazine)"
+ item_cost = 70
+ path = /obj/item/weapon/gun/projectile/p92x/large
+
/datum/uplink_item/item/visible_weapons/lemat
name = "LeMat"
item_cost = 60
diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm
index 348d73b0b2..6c17c18bca 100644
--- a/code/datums/weakref.dm
+++ b/code/datums/weakref.dm
@@ -1,10 +1,3 @@
-/datum
- var/weakref/weakref
-
-/datum/Destroy()
- weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
- . = ..()
-
//obtain a weak reference to a datum
/proc/weakref(datum/D)
if(!istype(D))
diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm
index bdddf048c5..5bb5831d3e 100644
--- a/code/datums/wires/apc.dm
+++ b/code/datums/wires/apc.dm
@@ -55,12 +55,14 @@
if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2)
if(!mended)
- A.shock(usr, 50)
+ if(istype(usr, /mob/living))
+ A.shock(usr, 50)
A.shorted = 1
else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2))
A.shorted = 0
- A.shock(usr, 50)
+ if(istype(usr, /mob/living))
+ A.shock(usr, 50)
if(APC_WIRE_AI_CONTROL)
diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm
new file mode 100644
index 0000000000..f176b8f139
--- /dev/null
+++ b/code/datums/wires/tesla_coil.dm
@@ -0,0 +1,18 @@
+/datum/wires/tesla_coil
+ wire_count = 1
+ holder_type = /obj/machinery/power/tesla_coil
+
+var/const/WIRE_ZAP = 1
+
+/datum/wires/tesla_coil/CanUse(mob/living/L)
+ var/obj/machinery/power/tesla_coil/T = holder
+ if(T && T.panel_open)
+ return 1
+ return 0
+
+/datum/wires/tesla_coil/UpdatePulsed(index)
+ var/obj/machinery/power/tesla_coil/T = holder
+ switch(index)
+ if(WIRE_ZAP)
+ T.zap()
+ ..()
diff --git a/code/defines/obj.dm b/code/defines/obj.dm
index 0435ccb83d..fbd0a156a7 100644
--- a/code/defines/obj.dm
+++ b/code/defines/obj.dm
@@ -65,15 +65,16 @@ var/global/list/PDA_Manifest = list()
/datum/datacore/proc/get_manifest_list()
if(PDA_Manifest.len)
return
- var/heads[0]
- var/sec[0]
- var/eng[0]
- var/med[0]
- var/sci[0]
- var/car[0]
- var/civ[0]
- var/bot[0]
- var/misc[0]
+ var/list/heads = list()
+ var/list/sec = list()
+ var/list/eng = list()
+ var/list/med = list()
+ var/list/sci = list()
+ var/list/car = list()
+ var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S
+ var/list/civ = list()
+ var/list/bot = list()
+ var/list/misc = list()
for(var/datum/data/record/t in data_core.general)
var/name = sanitize(t.fields["name"])
var/rank = sanitize(t.fields["rank"])
@@ -113,6 +114,10 @@ var/global/list/PDA_Manifest = list()
if(depthead && sci.len != 1)
sci.Swap(1,sci.len)
+ if(real_rank in planet_positions)
+ pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive)
+ department = 1
+
if(real_rank in cargo_positions)
car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive)
department = 1
@@ -133,16 +138,17 @@ var/global/list/PDA_Manifest = list()
misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive)
- PDA_Manifest = list(\
- "heads" = heads,\
- "sec" = sec,\
- "eng" = eng,\
- "med" = med,\
- "sci" = sci,\
- "car" = car,\
- "civ" = civ,\
- "bot" = bot,\
- "misc" = misc\
+ PDA_Manifest = list(
+ list("cat" = "Command", "elems" = heads),
+ list("cat" = "Security", "elems" = sec),
+ list("cat" = "Engineering", "elems" = eng),
+ list("cat" = "Medical", "elems" = med),
+ list("cat" = "Science", "elems" = sci),
+ list("cat" = "Cargo", "elems" = car),
+ // list("cat" = "Planetside", "elems" = pla), // VOREStation Edit - Don't show empty dpt in PDA
+ list("cat" = "Civilian", "elems" = civ),
+ list("cat" = "Silicon", "elems" = bot),
+ list("cat" = "Miscellaneous", "elems" = misc)
)
return
diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm
index ff9e9df323..d6ab3edc0c 100644
--- a/code/defines/procs/announce.dm
+++ b/code/defines/procs/announce.dm
@@ -113,5 +113,5 @@ datum/announcement/proc/Log(message as text, message_title as text)
rank = character.mind.role_alt_title
AnnounceArrivalSimple(character.real_name, rank, join_message)
-/proc/AnnounceArrivalSimple(var/name, var/rank = "visitor", var/join_message = "will arrive to the station shortly by shuttle")
+/proc/AnnounceArrivalSimple(var/name, var/rank = "visitor", var/join_message = "will arrive at the station shortly") //VOREStation Edit - Remove shuttle reference
global_announcer.autosay("[name], [rank], [join_message].", "Arrivals Announcement Computer")
diff --git a/code/defines/procs/hud.dm b/code/defines/procs/hud.dm
deleted file mode 100644
index dd4a282740..0000000000
--- a/code/defines/procs/hud.dm
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Using the HUD procs is simple. Call these procs in the life.dm of the intended mob.
-Use the regular_hud_updates() proc before process_med_hud(mob) or process_sec_hud(mob) so
-the HUD updates properly! */
-
-// hud overlay image type, used for clearing client.images precisely
-/image/hud_overlay
- appearance_flags = APPEARANCE_UI // Don't get scaled with macro/micros. VOREStation edit
-
-//Medical HUD outputs. Called by the Life() proc of the mob using it, usually.
-proc/process_med_hud(var/mob/M, var/local_scanner, var/mob/Alt)
- if(!can_process_hud(M))
- return
-
- var/datum/arranged_hud_process/P = arrange_hud_process(M, Alt, med_hud_users)
- for(var/mob/living/carbon/human/patient in P.Mob.in_view(P.Turf))
- if(P.Mob.see_invisible < patient.invisibility)
- continue
-
- if(local_scanner)
- P.Client.images += patient.hud_list[HEALTH_HUD]
- P.Client.images += patient.hud_list[STATUS_HUD]
- P.Client.images += patient.hud_list[BACKUP_HUD] //VOREStation Edit - Backup implant indicator
- else
- var/sensor_level = getsensorlevel(patient)
- if(sensor_level >= SUIT_SENSOR_VITAL)
- P.Client.images += patient.hud_list[HEALTH_HUD]
- if(sensor_level >= SUIT_SENSOR_BINARY)
- P.Client.images += patient.hud_list[LIFE_HUD]
-
-//Security HUDs. Pass a value for the second argument to enable implant viewing or other special features.
-proc/process_sec_hud(var/mob/M, var/advanced_mode, var/mob/Alt)
- if(!can_process_hud(M))
- return
- var/datum/arranged_hud_process/P = arrange_hud_process(M, Alt, sec_hud_users)
- for(var/mob/living/carbon/human/perp in P.Mob.in_view(P.Turf))
- if(P.Mob.see_invisible < perp.invisibility)
- continue
-
- P.Client.images += perp.hud_list[ID_HUD]
- if(advanced_mode)
- P.Client.images += perp.hud_list[WANTED_HUD]
- P.Client.images += perp.hud_list[IMPTRACK_HUD]
- P.Client.images += perp.hud_list[IMPLOYAL_HUD]
- P.Client.images += perp.hud_list[IMPCHEM_HUD]
-
-datum/arranged_hud_process
- var/client/Client
- var/mob/Mob
- var/turf/Turf
-
-proc/arrange_hud_process(var/mob/M, var/mob/Alt, var/list/hud_list)
- hud_list |= M
- var/datum/arranged_hud_process/P = new
- P.Client = M.client
- P.Mob = Alt ? Alt : M
- P.Turf = get_turf(P.Mob)
- return P
-
-proc/can_process_hud(var/mob/M)
- if(!M)
- return 0
- if(!M.client)
- return 0
- if(M.stat != CONSCIOUS)
- return 0
- return 1
-
-//Deletes the current HUD images so they can be refreshed with new ones.
-mob/proc/handle_regular_hud_updates() //Used in the life.dm of mobs that can use HUDs.
- if(client)
- for(var/image/hud_overlay/hud in client.images)
- client.images -= hud
- med_hud_users -= src
- sec_hud_users -= src
- //VOREStation Add - HUD lists
- eng_hud_users -= src
- sci_hud_users -= src
- gen_hud_users -= src
- if(vantag_hud) process_vantag_hud(src) //VOREStation Add - So any mob can have the vantag hud, observer or not.
- //VOREStation Add End
-mob/proc/in_view(var/turf/T)
- return view(T)
-
-/mob/observer/eye/in_view(var/turf/T)
- var/list/viewed = new
- for(var/mob/living/carbon/human/H in mob_list)
- if(get_dist(H, T) <= 7)
- viewed += H
- return viewed
diff --git a/code/defines/procs/hud_vr.dm b/code/defines/procs/hud_vr.dm
deleted file mode 100644
index 248c862173..0000000000
--- a/code/defines/procs/hud_vr.dm
+++ /dev/null
@@ -1,60 +0,0 @@
-var/global/list/gen_hud_users = list() // List of all entities using a generic AR shades.
-var/global/list/eng_hud_users = list() // List of all entities using a engineer HUD.
-var/global/list/sci_hud_users = list() // List of all entities using a science HUD.
-
-var/global/list/vantag_hud_users = list() // List of all mobs with the VANTAG hud on.
-
-/proc/broadcast_engineering_hud_message(var/message, var/broadcast_source)
- broadcast_hud_message(message, broadcast_source, eng_hud_users, /obj/item/clothing/glasses/omnihud/eng)
-
-/proc/broadcast_science_hud_message(var/message, var/broadcast_source)
- broadcast_hud_message(message, broadcast_source, sci_hud_users, /obj/item/clothing/glasses/omnihud/rnd)
-
-proc/process_omni_hud(var/mob/M, var/mode, var/mob/Alt)
- if(!can_process_hud(M))
- return
-
- var/datum/arranged_hud_process/P
- switch(mode)
- if("med")
- P = arrange_hud_process(M, Alt, med_hud_users)
- if("sec")
- P = arrange_hud_process(M, Alt, sec_hud_users)
- if("eng")
- P = arrange_hud_process(M, Alt, eng_hud_users)
- if("sci")
- P = arrange_hud_process(M, Alt, sci_hud_users)
- if("best")
- P = arrange_hud_process(M, Alt, sec_hud_users)
- else
- P = arrange_hud_process(M, Alt, gen_hud_users)
-
- for(var/mob/living/carbon/human/guy in P.Mob.in_view(P.Turf))
- if(P.Mob.see_invisible < guy.invisibility)
- continue
-
- P.Client.images += guy.hud_list[ID_HUD]
- P.Client.images += guy.hud_list[HEALTH_VR_HUD]
-
- if(mode == "med") //Medical advanced version
- P.Client.images += guy.hud_list[STATUS_R_HUD]
- P.Client.images += guy.hud_list[BACKUP_HUD]
- if(mode == "sec") //Security advanced version
- P.Client.images += guy.hud_list[WANTED_HUD]
- if(mode == "best") //Command/omni advanced version
- P.Client.images += guy.hud_list[WANTED_HUD]
- P.Client.images += guy.hud_list[STATUS_R_HUD]
- P.Client.images += guy.hud_list[BACKUP_HUD]
-
-
-proc/process_vantag_hud(var/mob/M)
- if(!M.vantag_hud || !can_process_hud(M))
- return
-
- var/datum/arranged_hud_process/P = arrange_hud_process(M, null, vantag_hud_users)
-
- for(var/mob/living/carbon/human/guy in P.Mob.in_view(P.Turf))
- if(P.Mob.see_invisible < guy.invisibility)
- continue
-
- P.Client.images += guy.hud_list[VANTAG_HUD]
diff --git a/code/game/antagonist/outsider/mercenary.dm b/code/game/antagonist/outsider/mercenary.dm
index 8e4b8826b4..ed2964cfad 100644
--- a/code/game/antagonist/outsider/mercenary.dm
+++ b/code/game/antagonist/outsider/mercenary.dm
@@ -50,7 +50,7 @@ var/datum/antagonist/mercenary/mercs
var/obj/item/device/radio/uplink/U = new(player.loc, player.mind, DEFAULT_TELECRYSTAL_AMOUNT)
player.put_in_hands(U)
- player.update_icons()
+ player.update_icons_layers()
create_id("Mercenary", player)
create_radio(SYND_FREQ, player)
diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm
index 596ac1451e..7c62208ca1 100644
--- a/code/game/antagonist/outsider/raider.dm
+++ b/code/game/antagonist/outsider/raider.dm
@@ -85,12 +85,13 @@ var/datum/antagonist/raider/raiders
/obj/item/weapon/gun/projectile/silenced,
/obj/item/weapon/gun/projectile/shotgun/pump,
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
- /obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin,
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel/pellet,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel/sawn,
/obj/item/weapon/gun/projectile/colt/detective,
/obj/item/weapon/gun/projectile/pistol,
+ /obj/item/weapon/gun/projectile/p92x,
/obj/item/weapon/gun/projectile/revolver,
/obj/item/weapon/gun/projectile/pirate,
/obj/item/weapon/gun/projectile/revolver/judge,
diff --git a/code/game/antagonist/outsider/technomancer.dm b/code/game/antagonist/outsider/technomancer.dm
index 4ec49230fd..33dc33b5e8 100644
--- a/code/game/antagonist/outsider/technomancer.dm
+++ b/code/game/antagonist/outsider/technomancer.dm
@@ -47,7 +47,7 @@ var/datum/antagonist/technomancer/technomancers
technomancer_mob.equip_to_slot_or_del(new /obj/item/device/flashlight(technomancer_mob), slot_belt)
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(technomancer_mob), slot_shoes)
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/head/technomancer/master(technomancer_mob), slot_head)
- technomancer_mob.update_icons()
+ technomancer_mob.update_icons_layers()
return 1
/datum/antagonist/technomancer/proc/equip_apprentice(var/mob/living/carbon/human/technomancer_mob)
diff --git a/code/game/antagonist/outsider/wizard.dm b/code/game/antagonist/outsider/wizard.dm
index b88af1b1b9..ad5cc10479 100644
--- a/code/game/antagonist/outsider/wizard.dm
+++ b/code/game/antagonist/outsider/wizard.dm
@@ -87,7 +87,7 @@ var/datum/antagonist/wizard/wizards
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/box(wizard_mob), slot_in_backpack)
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/teleportation_scroll(wizard_mob), slot_r_store)
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/spellbook(wizard_mob), slot_r_hand)
- wizard_mob.update_icons()
+ wizard_mob.update_icons_layers()
return 1
/datum/antagonist/wizard/check_victory()
diff --git a/code/game/antagonist/station/infiltrator.dm b/code/game/antagonist/station/infiltrator.dm
index 81430c0006..8ca6f4ac4d 100644
--- a/code/game/antagonist/station/infiltrator.dm
+++ b/code/game/antagonist/station/infiltrator.dm
@@ -1,4 +1,4 @@
-// Infiltrator is a varient of Traitor, except that the traitors are in a team and can communicate with a special headset.
+// Infiltrator is a variant of Traitor, except that the traitors are in a team and can communicate with a special headset.
var/datum/antagonist/traitor/infiltrator/infiltrators
diff --git a/code/game/antagonist/station/renegade.dm b/code/game/antagonist/station/renegade.dm
index d9796432c0..3bf53e55dc 100644
--- a/code/game/antagonist/station/renegade.dm
+++ b/code/game/antagonist/station/renegade.dm
@@ -45,10 +45,11 @@ var/datum/antagonist/renegade/renegades
/obj/item/weapon/gun/projectile/sec/wood,
/obj/item/weapon/gun/projectile/silenced,
/obj/item/weapon/gun/projectile/pistol,
+ /obj/item/weapon/gun/projectile/p92x,
/obj/item/weapon/gun/projectile/revolver,
/obj/item/weapon/gun/projectile/derringer,
/obj/item/weapon/gun/projectile/shotgun/pump,
- /obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin,
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle,
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel,
/obj/item/weapon/gun/projectile/revolver/judge,
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 6377df54e7..1898322b95 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -100,7 +100,7 @@ var/list/ghostteleportlocs = list()
icon_state = "space"
requires_power = 1
always_unpowered = 1
- dynamic_lighting = 1
+ dynamic_lighting = 0
power_light = 0
power_equip = 0
power_environ = 0
@@ -156,6 +156,7 @@ area/space/atmosalert()
/area/shuttle/arrival/station
icon_state = "shuttle"
+ dynamic_lighting = 0
/area/shuttle/escape
name = "\improper Emergency Shuttle"
@@ -164,6 +165,7 @@ area/space/atmosalert()
/area/shuttle/escape/station
name = "\improper Emergency Shuttle Station"
icon_state = "shuttle2"
+ dynamic_lighting = 0
/area/shuttle/escape/centcom
name = "\improper Emergency Shuttle CentCom"
@@ -2739,7 +2741,7 @@ area/space/atmosalert()
for(var/mob/living/carbon/human/H in src)
if(H.s_tone > -55)
H.s_tone--
- H.update_body()
+ H.update_icons_body()
if(H.client)
mysound.status = SOUND_UPDATE
H << mysound
@@ -2860,7 +2862,7 @@ var/list/the_station_areas = list (
for(var/mob/living/carbon/human/H in src)
// if(H.s_tone > -55) //ugh...nice/novel idea but please no.
// H.s_tone--
-// H.update_body()
+// H.update_icons_body()
if(H.client)
mysound.status = SOUND_UPDATE
H << mysound
diff --git a/code/game/area/Space Station 13 areas_vr.dm b/code/game/area/Space Station 13 areas_vr.dm
index 4809f0ab21..330d703ac0 100644
--- a/code/game/area/Space Station 13 areas_vr.dm
+++ b/code/game/area/Space Station 13 areas_vr.dm
@@ -134,87 +134,56 @@
/area/bigship/teleporter
name = "Bigship Teleporter Room"
-//////// Houseboat Areas ////////
+//////// Small Cruiser Areas ////////
/area/houseboat
- name = "Houseboat"
+ name = "Small Cruiser"
requires_power = 0
flags = RAD_SHIELDED
base_turf = /turf/space
icon_state = "red2"
- lightswitch = 0
+ lightswitch = TRUE
-/area/houseboat/bridge
- name = "Houseboat - Bridge"
- icon_state = "blue2"
-/area/houseboat/neck
- name = "Houseboat - Neck"
- icon_state = "blue2"
-/area/houseboat/cap_room
- name = "Houseboat - Captain's Room"
- icon_state = "blue2"
-/area/houseboat/teleporter
- name = "Houseboat - Teleporter"
- icon_state = "blue2"
-/area/houseboat/robotics
- name = "Houseboat - Robotics"
- icon_state = "blue2"
-/area/houseboat/cargo
- name = "Houseboat - Cargo"
- icon_state = "blue2"
-/area/houseboat/medical
- name = "Houseboat - Medical"
- icon_state = "blue2"
-/area/houseboat/engineering
- name = "Houseboat - Engineering"
- icon_state = "blue2"
-/area/houseboat/shower
- name = "Houseboat - Shower"
- icon_state = "blue2"
-/area/houseboat/common_area
- name = "Houseboat - Common Area"
- icon_state = "blue2"
-/area/houseboat/dining_area
- name = "Houseboat - Dining Area"
- icon_state = "blue2"
/area/houseboat/holodeck_area
- name = "Houseboat - Holodeck"
- icon_state = "blue2"
-/area/houseboat/lockers
- name = "Houseboat - Locker Room"
- icon_state = "blue2"
-/area/houseboat/fountain
- name = "Houseboat - Fountain"
+ name = "Small Cruiser - Holodeck"
icon_state = "blue2"
/area/houseboat/holodeck/off
- name = "Houseboat Holo - Off"
+ name = "Small Cruiser Holo - Off"
icon_state = "blue2"
/area/houseboat/holodeck/beach
- name = "Houseboat Holo - Beach"
+ name = "Small Cruiser Holo - Beach"
icon_state = "blue2"
/area/houseboat/holodeck/snow
- name = "Houseboat Holo - Snow"
+ name = "Small Cruiser Holo - Snow"
icon_state = "blue2"
/area/houseboat/holodeck/desert
- name = "Houseboat Holo - Desert"
+ name = "Small Cruiser Holo - Desert"
icon_state = "blue2"
/area/houseboat/holodeck/picnic
- name = "Houseboat Holo - Picnic"
+ name = "Small Cruiser Holo - Picnic"
icon_state = "blue2"
/area/houseboat/holodeck/thunderdome
- name = "Houseboat Holo - Thunderdome"
+ name = "Small Cruiser Holo - Thunderdome"
icon_state = "blue2"
/area/houseboat/holodeck/basketball
- name = "Houseboat Holo - Basketball"
+ name = "Small Cruiser Holo - Basketball"
icon_state = "blue2"
/area/houseboat/holodeck/gaming
- name = "Houseboat Holo - Gaming Table"
+ name = "Small Cruiser Holo - Gaming Table"
icon_state = "blue2"
/area/houseboat/holodeck/space
- name = "Houseboat Holo - Space"
+ name = "Small Cruiser Holo - Space"
icon_state = "blue2"
/area/houseboat/holodeck/bunking
- name = "Houseboat Holo - Bunking"
+ name = "Small Cruiser Holo - Bunking"
+ icon_state = "blue2"
+
+/area/shuttle/cruiser/cruiser
+ name = "Small Cruiser Shuttle - Cruiser"
+ icon_state = "blue2"
+ base_turf = /turf/simulated/floor/tiled/techfloor
+/area/shuttle/cruiser/station
+ name = "Small Cruiser Shuttle - Station"
icon_state = "blue2"
@@ -275,3 +244,7 @@
/area/maintenance/substation/outpost
name = "Research Outpost Substation"
+
+/area/engineering/engine_gas
+ name = "\improper Engine Gas Storage"
+ icon_state = "engine_waste"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 91c78480ff..3403ffdbb8 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -309,7 +309,7 @@ var/list/mob/living/forced_ambiance_list = new
for(var/obj/machinery/door/window/temp_windoor in src)
temp_windoor.open()
-/area/proc/has_gravity()
+/area/has_gravity()
return has_gravity
/area/space/has_gravity()
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index a092ed7b65..c8a9c1e1b5 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -25,6 +25,12 @@
//Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom
+//atom creation method that preloads variables at creation
+/atom/New()
+ // Don't call ..() unless /datum/New() ever exists
+ if(use_preloader && (src.type == _preloader.target_path))//in case the instanciated atom is creating other atoms in New()
+ _preloader.load(src)
+
/atom/proc/reveal_blood()
return
@@ -84,6 +90,10 @@
P.on_hit(src, 0, def_zone)
. = 0
+// Called when a blob expands onto the tile the atom occupies.
+/atom/proc/blob_act()
+ return
+
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
if(istype(src.loc, container))
@@ -117,78 +127,6 @@
found += A.search_contents_for(path,filter_path)
return found
-
-
-
-/*
-Beam code by Gunbuddy
-
-Beam() proc will only allow one beam to come from a source at a time. Attempting to call it more than
-once at a time per source will cause graphical errors.
-Also, the icon used for the beam will have to be vertical and 32x32.
-The math involved assumes that the icon is vertical to begin with so unless you want to adjust the math,
-its easier to just keep the beam vertical.
-*/
-/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10)
- //BeamTarget represents the target for the beam, basically just means the other end.
- //Time is the duration to draw the beam
- //Icon is obviously which icon to use for the beam, default is beam.dmi
- //Icon_state is what icon state is used. Default is b_beam which is a blue beam.
- //Maxdistance is the longest range the beam will persist before it gives up.
- var/EndTime=world.time+time
- while(BeamTarget&&world.timelength)
- var/icon/II=new(icon,icon_state)
- II.DrawBox(null,1,(length-N),32,32)
- II.Turn(Angle)
- X.icon=II
- else X.icon=I
- var/Pixel_x=round(sin(Angle)+32*sin(Angle)*(N+16)/32)
- var/Pixel_y=round(cos(Angle)+32*cos(Angle)*(N+16)/32)
- if(DX==0) Pixel_x=0
- if(DY==0) Pixel_y=0
- if(Pixel_x>32)
- for(var/a=0, a<=Pixel_x,a+=32)
- X.x++
- Pixel_x-=32
- if(Pixel_x<-32)
- for(var/a=0, a>=Pixel_x,a-=32)
- X.x--
- Pixel_x+=32
- if(Pixel_y>32)
- for(var/a=0, a<=Pixel_y,a+=32)
- X.y++
- Pixel_y-=32
- if(Pixel_y<-32)
- for(var/a=0, a>=Pixel_y,a-=32)
- X.y--
- Pixel_y+=32
- X.pixel_x=Pixel_x
- X.pixel_y=Pixel_y
- sleep(3) //Changing this to a lower value will cause the beam to follow more smoothly with movement, but it will also be more laggy.
- //I've found that 3 ticks provided a nice balance for my use.
- for(var/obj/effect/overlay/beam/O in orange(10,src)) if(O.BeamSource==src) qdel(O)
-
-
//All atoms
/atom/proc/examine(mob/user, var/distance = -1, var/infix = "", var/suffix = "")
//This reformat names to get a/an properly working on item descriptions when they are bloody
@@ -456,20 +394,20 @@ its easier to just keep the beam vertical.
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
/atom/proc/visible_message(var/message, var/blind_message)
- var/list/see = get_mobs_or_objects_in_view(world.view,src) | viewers(get_turf(src), null)
+ var/list/see = get_mobs_and_objs_in_view_fast(get_turf(src),world.view,remote_ghosts = FALSE)
- for(var/I in see)
- if(isobj(I))
- //spawn(0)
- //if(I) //It's possible that it could be deleted in the meantime.
- var/obj/O = I
- O.show_message(message, 1, blind_message, 2)
- else if(ismob(I))
- var/mob/M = I
- if(M.see_invisible >= invisibility) // Cannot view the invisible
- M.show_message(message, 1, blind_message, 2)
- else if (blind_message)
- M.show_message(blind_message, 2)
+ var/list/seeing_mobs = see["mobs"]
+ var/list/seeing_objs = see["objs"]
+
+ for(var/obj in seeing_objs)
+ var/obj/O = obj
+ O.show_message(message, 1, blind_message, 2)
+ for(var/mob in seeing_mobs)
+ var/mob/M = mob
+ if(M.see_invisible >= invisibility && MOB_CAN_SEE_PLANE(M, plane))
+ M.show_message(message, 1, blind_message, 2)
+ else if(blind_message)
+ M.show_message(blind_message, 2)
// Show a message to all mobs and objects in earshot of this atom
// Use for objects performing audible actions
@@ -478,20 +416,20 @@ its easier to just keep the beam vertical.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance)
- var/range = world.view
- if(hearing_distance)
- range = hearing_distance
- var/list/hear = get_mobs_or_objects_in_view(range,src)
+ var/range = hearing_distance || world.view
+ var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE)
- for(var/I in hear)
- if(isobj(I))
- spawn(0)
- if(I) //It's possible that it could be deleted in the meantime.
- var/obj/O = I
- O.show_message(message, 2, deaf_message, 1)
- else if(ismob(I))
- var/mob/M = I
- M.show_message(message, 2, deaf_message, 1)
+ var/list/hearing_mobs = hear["mobs"]
+ var/list/hearing_objs = hear["objs"]
+
+ for(var/obj in hearing_objs)
+ var/obj/O = obj
+ O.show_message(message, 2, deaf_message, 1)
+
+ for(var/mob in hearing_mobs)
+ var/mob/M = mob
+ var/msg = message
+ M.show_message(msg, 2, deaf_message, 1)
/atom/movable/proc/dropInto(var/atom/destination)
while(istype(destination))
@@ -509,3 +447,13 @@ its easier to just keep the beam vertical.
/atom/proc/InsertedContents()
return contents
+
+/atom/proc/has_gravity(turf/T)
+ if(!T || !isturf(T))
+ T = get_turf(src)
+ if(istype(T, /turf/space)) // Turf never has gravity
+ return FALSE
+ var/area/A = get_area(T)
+ if(A && A.has_gravity())
+ return TRUE
+ return FALSE
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index e4bd3b85ea..2dba31d578 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -16,6 +16,8 @@
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/old_x = 0
+ var/old_y = 0
var/auto_init = 1
/atom/movable/New()
@@ -91,6 +93,8 @@
AM.Crossed(src)
if(is_new_area && is_destination_turf)
destination.loc.Entered(src, origin)
+
+ Moved(origin)
return 1
//called when src is thrown into hit_atom
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index 4277287fcb..5810d4d1c0 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -201,6 +201,12 @@ var/global/list/datum/dna/gene/dna_genes[0]
src.base_species = CS.base_species
src.blood_color = CS.blood_color
+ if(istype(character.species,/datum/species/xenochimera))
+ var/datum/species/xenochimera/CS = character.species
+ //src.species_traits = CS.traits.Copy() //No traits
+ src.base_species = CS.base_species
+ src.blood_color = CS.blood_color
+
// +1 to account for the none-of-the-above possibility
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1)
SetUIValueRange(DNA_UI_TAIL_STYLE, tail_style + 1, tail_styles_list.len + 1, 1)
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 35b6028c48..a7e7d2ddae 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -229,6 +229,10 @@
var/datum/species/custom/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
new_CS.blood_color = dna.blood_color
+ if(istype(H.species,/datum/species/xenochimera))
+ var/datum/species/xenochimera/CS = H.species
+ var/datum/species/xenochimera/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
+ new_CS.blood_color = dna.blood_color
// VOREStation Edit End
H.force_update_organs() //VOREStation Add - Gotta do this too
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index e728ab8e50..0dc9b72be7 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -327,7 +327,7 @@
*/
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(user == connected.occupant || user.stat)
+ if(!connected || user == connected.occupant || user.stat)
return
// this is the data which will be sent to the ui
diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm
index 022366c865..627b4e8b14 100644
--- a/code/game/gamemodes/changeling/changeling_powers.dm
+++ b/code/game/gamemodes/changeling/changeling_powers.dm
@@ -23,6 +23,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name.
var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too.
var/last_shriek = null // world.time when the ling last used a shriek.
+ var/next_escape = 0 // world.time when the ling can next use Escape Restraints
/datum/changeling/New(var/gender=FEMALE)
..()
@@ -167,7 +168,8 @@ turf/proc/AdjacentTurfsRangedSting()
/obj/structure/target_stake,
/obj/structure/cable,
/obj/structure/disposalpipe,
- /obj/machinery/
+ /obj/machinery,
+ /mob
)
var/L[] = new()
diff --git a/code/game/gamemodes/changeling/generic_equip_procs.dm b/code/game/gamemodes/changeling/generic_equip_procs.dm
index 9c73a4e3d2..7ff76ff9f6 100644
--- a/code/game/gamemodes/changeling/generic_equip_procs.dm
+++ b/code/game/gamemodes/changeling/generic_equip_procs.dm
@@ -122,7 +122,7 @@
playsound(src, 'sound/effects/splat.ogg', 30, 1)
visible_message("[src] pulls on their clothes, peeling it off along with parts of their skin attached!",
"We remove and deform our equipment.")
- M.update_icons()
+ M.update_icons_layers()
M.mind.changeling.armor_deployed = 0
return success
@@ -138,7 +138,7 @@
M.equip_to_slot_or_del(I, slot_head)
grown_items_list.Add("a helmet")
playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -148,7 +148,7 @@
M.equip_to_slot_or_del(I, slot_w_uniform)
grown_items_list.Add("a uniform")
playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -158,7 +158,7 @@
M.equip_to_slot_or_del(I, slot_gloves)
grown_items_list.Add("some gloves")
playsound(src, 'sound/effects/splat.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -168,7 +168,7 @@
M.equip_to_slot_or_del(I, slot_shoes)
grown_items_list.Add("shoes")
playsound(src, 'sound/effects/splat.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -178,7 +178,7 @@
M.equip_to_slot_or_del(I, slot_belt)
grown_items_list.Add("a belt")
playsound(src, 'sound/effects/splat.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -188,7 +188,7 @@
M.equip_to_slot_or_del(I, slot_glasses)
grown_items_list.Add("some glasses")
playsound(src, 'sound/effects/splat.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -198,7 +198,7 @@
M.equip_to_slot_or_del(I, slot_wear_mask)
grown_items_list.Add("a mask")
playsound(src, 'sound/effects/splat.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -208,7 +208,7 @@
M.equip_to_slot_or_del(I, slot_back)
grown_items_list.Add("a backpack")
playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -218,7 +218,7 @@
M.equip_to_slot_or_del(I, slot_wear_suit)
grown_items_list.Add("an exosuit")
playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
@@ -228,7 +228,7 @@
M.equip_to_slot_or_del(I, slot_wear_id)
grown_items_list.Add("an ID card")
playsound(src, 'sound/effects/splat.ogg', 30, 1)
- M.update_icons()
+ M.update_icons_layers()
success = 1
sleep(1 SECOND)
diff --git a/code/game/gamemodes/changeling/powers/enrage.dm b/code/game/gamemodes/changeling/powers/enrage.dm
new file mode 100644
index 0000000000..00b2193c86
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/enrage.dm
@@ -0,0 +1,33 @@
+/datum/power/changeling/enrage
+ name = "Enrage"
+ desc = "We evolve modifications to our mind and body, allowing us to call on intense periods of rage for our benefit."
+ helptext = "Berserks us, giving massive bonuses to fighting in close quarters for thirty seconds, and losing the ability to \
+ be accurate at ranged while active. Afterwards, we will suffer extreme amounts of exhaustion for a period of two minutes, \
+ during which we will be much weaker and slower than before. We cannot berserk again while exhausted. This ability requires \
+ a significant amount of nutrition to use, and cannot be used if too hungry. Using this ability will end most forms of disables."
+ enhancedtext = "The length of exhaustion after berserking is reduced to one minute, from two, and requires half as much nutrition."
+ ability_icon_state = "ling_berserk"
+ genomecost = 2
+ allowduringlesserform = 1
+ verbpath = /mob/living/proc/changeling_berserk
+
+// Makes the ling very upset.
+/mob/living/proc/changeling_berserk()
+ set category = "Changeling"
+ set name = "Enrage (30)"
+ set desc = "Causes you to go Berserk."
+
+ var/datum/changeling/changeling = changeling_power(30,0,100)
+ if(!changeling)
+ return 0
+
+ var/modifier_to_use = /datum/modifier/berserk/changeling
+ if(src.mind.changeling.recursive_enhancement)
+ modifier_to_use = /datum/modifier/berserk/changeling/recursive
+ to_chat(src, "We optimize our levels of anger, which will avoid excessive stress on ourselves.")
+
+ if(add_modifier(modifier_to_use, 30 SECONDS))
+ changeling.chem_charges -= 30
+
+ feedback_add_details("changeling_powers","EN")
+ return 1
\ No newline at end of file
diff --git a/code/game/gamemodes/changeling/powers/escape_restraints.dm b/code/game/gamemodes/changeling/powers/escape_restraints.dm
new file mode 100644
index 0000000000..4e6ed4e5cf
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/escape_restraints.dm
@@ -0,0 +1,63 @@
+/datum/power/changeling/escape_restraints
+ name = "Escape Restraints"
+ desc = "We evolve more complex joints"
+ helptext = "We can instantly escape from most restraints and bindings, but we cannot do it often."
+ enhancedtext = "More frequent escapes."
+ ability_icon_state = "ling_escape_restraints"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_escape_restraints
+
+//Escape Cuffs. By design this does not escape from straight jackets
+/mob/proc/changeling_escape_restraints()
+ set category = "Changeling"
+ set name = "Escape Restraints (40)"
+ set desc = "Removes handcuffs and legcuffs instantly."
+
+ var/escape_cooldown = 5 MINUTES //This is used later to prevent spamming
+ var/mob/living/carbon/human/C = src
+ var/datum/changeling/changeling = changeling_power(40,0,100,CONSCIOUS)
+ if(!changeling)
+ return 0
+ if(world.time < changeling.next_escape)
+ to_chat(src, "We are still recovering from our last escape...")
+ return 0
+ if(!(C.handcuffed || C.legcuffed)) // No need to waste chems if there's nothing to break out of
+ to_chat(C, "We are are not restrained in a way we can escape...")
+ return 0
+
+ changeling.chem_charges -= 40
+
+ to_chat(C,"We contort our extremities and slip our cuffs.")
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ if(C.handcuffed)
+ var/obj/item/weapon/W = C.handcuffed
+ C.handcuffed = null
+ if(C.buckled && C.buckled.buckle_require_restraints)
+ C.buckled.unbuckle_mob()
+ C.update_inv_handcuffed()
+ if (C.client)
+ C.client.screen -= W
+ if(W)
+ W.loc = C.loc
+ W.dropped(C)
+ if(W)
+ W.layer = initial(W.layer)
+ if(C.legcuffed)
+ var/obj/item/weapon/W = C.legcuffed
+ C.legcuffed = null
+ C.update_inv_legcuffed()
+ if(C.client)
+ C.client.screen -= W
+ if(W)
+ W.loc = C.loc
+ W.dropped(C)
+ if(W)
+ W.layer = initial(W.layer)
+
+ if(src.mind.changeling.recursive_enhancement)
+ escape_cooldown *= 0.5
+
+ changeling.next_escape = world.time + escape_cooldown //And now we set the timer
+
+ feedback_add_details("changeling_powers","ESR")
+ return 1
\ No newline at end of file
diff --git a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
index 138cd156e3..bc37afc1ea 100644
--- a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
+++ b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
@@ -56,7 +56,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/clothing/head/chameleon/changeling
name = "malformed head"
@@ -78,7 +78,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/clothing/suit/chameleon/changeling
name = "chitinous chest"
@@ -104,7 +104,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/clothing/shoes/chameleon/changeling
name = "malformed feet"
@@ -130,7 +130,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/weapon/storage/backpack/chameleon/changeling
name = "backpack"
@@ -158,7 +158,7 @@ var/global/list/changeling_fabricated_clothing = list(
for(var/atom/movable/AM in src.contents) //Dump whatever's in the bag before deleting.
AM.forceMove(get_turf(loc))
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/clothing/gloves/chameleon/changeling
name = "malformed hands"
@@ -185,7 +185,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/clothing/mask/chameleon/changeling
@@ -213,7 +213,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/clothing/glasses/chameleon/changeling
name = "chitin goggles"
@@ -235,7 +235,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/weapon/storage/belt/chameleon/changeling
name = "waist pouch"
@@ -261,7 +261,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/weapon/card/id/syndicate/changeling
name = "chitinous card"
@@ -288,7 +288,7 @@ var/global/list/changeling_fabricated_clothing = list(
visible_message("[H] tears off [src]!",
"We remove [src].")
qdel(src)
- H.update_icons()
+ H.update_icons_layers()
/obj/item/weapon/card/id/syndicate/changeling/Click() //Since we can't hold it in our hands, and attack_hand() doesn't work if it in inventory...
diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm
index c640e35392..98b091abaa 100644
--- a/code/game/gamemodes/changeling/powers/revive.dm
+++ b/code/game/gamemodes/changeling/powers/revive.dm
@@ -34,7 +34,7 @@
H.restore_blood()
H.mutations.Remove(HUSK)
H.status_flags -= DISFIGURED
- H.update_body(1)
+ H.update_icons_body()
for(var/limb in H.organs_by_name)
var/obj/item/organ/external/current_limb = H.organs_by_name[limb]
if(current_limb)
diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm
index 37e7f67fe0..40e796490f 100644
--- a/code/game/gamemodes/changeling/powers/shriek.dm
+++ b/code/game/gamemodes/changeling/powers/shriek.dm
@@ -1,6 +1,6 @@
/datum/power/changeling/resonant_shriek
name = "Resonant Shriek"
- desc = "Our lungs and vocal chords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded biologicals and synthetics."
+ desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded."
helptext = "Lights are blown, organics are disoriented, and synthetics act as if they were flashed."
enhancedtext = "Range is doubled."
ability_icon_state = "ling_resonant_shriek"
@@ -20,19 +20,19 @@
/mob/proc/changeling_resonant_shriek()
set category = "Changeling"
set name = "Resonant Shriek (20)"
- set desc = "Emits a high-frequency sound that confuses and deafens humans, blows out nearby lights and overloads cyborg sensors."
+ set desc = "Emits a high-frequency sound that confuses and deafens organics, blows out nearby lights, and overloads synthetics' sensors."
var/datum/changeling/changeling = changeling_power(20,0,100,CONSCIOUS)
if(!changeling) return 0
if(is_muzzled())
- src << "Mmmf mrrfff!"
+ to_chat(src, "Mmmf mrrfff!")
return 0
if(ishuman(src))
var/mob/living/carbon/human/H = src
if(H.silent)
- src << "You can't speak!"
+ to_chat(src, "You can't speak!")
return 0
if(world.time < (changeling.last_shriek + 10 SECONDS) )
@@ -49,7 +49,7 @@
var/range = 4
if(src.mind.changeling.recursive_enhancement)
range = range * 2
- src << "We are extra loud."
+ to_chat(src, "We are extra loud.")
src.attack_log += text("\[[time_stamp()]\] Used Resonant Shriek.")
message_admins("[key_name(src)] used Resonant Shriek ([src.x],[src.y],[src.z]) (JMP).")
@@ -144,4 +144,4 @@
changeling.last_shriek = world.time
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm
index c25e3c5567..81a6148e2c 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm
@@ -8,7 +8,7 @@
//luminosity = 5
//l_color="#0066FF"
- layer = LIGHTING_LAYER+1
+ plane = PLANE_LIGHTING_ABOVE
var/spawned=0 // DIR mask
var/next_check=0
diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm
index 9c68ab879e..b2c4b68a5a 100644
--- a/code/game/gamemodes/events/holidays/Christmas.dm
+++ b/code/game/gamemodes/events/holidays/Christmas.dm
@@ -27,7 +27,7 @@
..()
/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user)
- if( !cracked && istype(target,/mob/living/carbon/human) && (target.stat == CONSCIOUS) && !target.get_active_hand() )
+ if( !cracked && (istype(target,/mob/living/silicon) || (istype(target,/mob/living/carbon/human) && !target.get_active_hand())) && target.stat == CONSCIOUS)
target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.")
var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc)
Joke.name = "[pick("awful","terrible","unfunny")] joke"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 2418f8afb9..8ab82e5e84 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -514,9 +514,9 @@ proc/get_nt_opposed()
var/list/dudes = list()
for(var/mob/living/carbon/human/man in player_list)
if(man.client)
- if(man.client.prefs.nanotrasen_relation == COMPANY_OPPOSED)
+ if(man.client.prefs.economic_status == CLASS_LOWER)
dudes += man
- else if(man.client.prefs.nanotrasen_relation == COMPANY_SKEPTICAL && prob(50))
+ else if(man.client.prefs.economic_status == CLASS_LOWMID && prob(50))
dudes += man
if(dudes.len == 0) return null
return pick(dudes)
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index 108a0ca99a..d989d42c27 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -10,6 +10,8 @@ var/global/datum/controller/gameticker/ticker
var/event_time = null
var/event = 0
+ // var/login_music // music played in pregame lobby // VOREStation Edit - We do music differently
+
var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking.
var/Bible_icon_state // icon_state the chaplain has chosen for his bible
@@ -32,10 +34,24 @@ var/global/datum/controller/gameticker/ticker
var/round_end_announced = 0 // Spam Prevention. Announce round end only once.
/datum/controller/gameticker/proc/pregame()
+ /* VOREStation Edit - We do music differently
+ login_music = pick(\
+ 'sound/music/halloween/skeletons.ogg',\
+ 'sound/music/halloween/halloween.ogg',\
+ 'sound/music/halloween/ghosts.ogg'
+ 'sound/music/space.ogg',\
+ 'sound/music/traitor.ogg',\
+ 'sound/music/title2.ogg',\
+ 'sound/music/clouds.s3m',\
+ 'sound/music/space_oddity.ogg') //Ground Control to Major Tom, this song is cool, what's going on?
+ */
+
+ send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]")
+
do
pregame_timeleft = 180
- world << "Welcome to the pre-game lobby!"
- world << "Please, setup your character and select ready. Game will start in [pregame_timeleft] seconds"
+ 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)
@@ -65,7 +81,7 @@ var/global/datum/controller/gameticker/ticker
if(!runnable_modes.len)
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
- world << "Unable to choose playable game mode. Reverting to pre-game lobby."
+ to_chat(world, "Unable to choose playable game mode. Reverting to pregame lobby.")
return 0
if(secret_force_mode != "secret")
src.mode = config.pick_mode(secret_force_mode)
@@ -80,7 +96,7 @@ var/global/datum/controller/gameticker/ticker
if(!src.mode)
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
- world << "Serious error in mode setup! Reverting to pre-game lobby."
+ to_chat(world, "Serious error in mode setup! Reverting to pregame lobby.") //Uses setup instead of set up due to computational context.
return 0
job_master.ResetOccupations()
@@ -89,7 +105,7 @@ var/global/datum/controller/gameticker/ticker
job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly.
if(!src.mode.can_start())
- world << "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby."
+ world << "Unable to start [mode.name]. Not enough players readied, [mode.required_players] players needed. Reverting to pregame lobby."
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
mode.fail_setup()
@@ -105,13 +121,13 @@ var/global/datum/controller/gameticker/ticker
tmpmodes+=M.name
tmpmodes = sortList(tmpmodes)
if(tmpmodes.len)
- world << "Possibilities: [english_list(tmpmodes, and_text= "; ", comma_text = "; ")]"
+ to_chat(world, "Possibilities: [english_list(tmpmodes, and_text= "; ", comma_text = "; ")]")
else
src.mode.announce()
setup_economy()
current_state = GAME_STATE_PLAYING
- create_characters() //Create player characters and transfer them
+ create_characters() //Create player characters and transfer them.
collect_minds()
equip_characters()
data_core.manifest()
@@ -128,7 +144,7 @@ var/global/datum/controller/gameticker/ticker
//Deleting Startpoints but we need the ai point to AI-ize people later
if (S.name != "AI")
qdel(S)
- world << "Enjoy the game!"
+ to_chat(world, "Enjoy the game!")
world << sound('sound/AI/welcome.ogg') // Skie
//Holiday Round-start stuff ~Carn
Holiday_Game_Start()
@@ -141,7 +157,7 @@ var/global/datum/controller/gameticker/ticker
if(C.holder)
admins_number++
if(admins_number == 0)
- send2adminirc("Round has started with no admins online.")
+ send2adminirc("A round has started with no admins online.")
/* supply_controller.process() //Start the supply shuttle regenerating points -- TLE // handled in scheduler
master_controller.process() //Start master_controller.process()
@@ -169,7 +185,8 @@ var/global/datum/controller/gameticker/ticker
cinematic = new(src)
cinematic.icon = 'icons/effects/station_explosion.dmi'
cinematic.icon_state = "station_intact"
- cinematic.layer = 20
+ cinematic.layer = 100
+ cinematic.plane = PLANE_PLAYER_HUD
cinematic.mouse_opacity = 0
cinematic.screen_loc = "1,0"
@@ -293,7 +310,7 @@ var/global/datum/controller/gameticker/ticker
if(captainless)
for(var/mob/M in player_list)
if(!istype(M,/mob/new_player))
- M << "Colony Directorship not forced on anyone."
+ to_chat(M, "Colony Directorship not forced on anyone.")
proc/process()
@@ -329,7 +346,7 @@ var/global/datum/controller/gameticker/ticker
feedback_set_details("end_proper","nuke")
time_left = 1 MINUTE //No point waiting five minutes if everyone's dead.
if(!delay_end)
- world << "Rebooting due to destruction of station in [round(time_left/600)] minutes."
+ to_chat(world, "Rebooting due to destruction of station in [round(time_left/600)] minutes.")
else
feedback_set_details("end_proper","proper completion")
time_left = round(restart_timeout)
@@ -342,15 +359,15 @@ var/global/datum/controller/gameticker/ticker
while(time_left > 0)
if(delay_end)
break
- world << "Restarting in [round(time_left/600)] minute\s."
+ to_chat(world, "Restarting in [round(time_left/600)] minute\s.")
time_left -= 1 MINUTES
sleep(600)
if(!delay_end)
world.Reboot()
else
- world << "An admin has delayed the round end."
+ to_chat(world, "An admin has delayed the round end.")
else
- world << "An admin has delayed the round end."
+ to_chat(world, "An admin has delayed the round end.")
else if (mode_finished)
post_game = 1
@@ -360,7 +377,7 @@ var/global/datum/controller/gameticker/ticker
//call a transfer shuttle vote
spawn(50)
if(!round_end_announced) // Spam Prevention. Now it should announce only once.
- world << "The round has ended!"
+ to_chat(world, "The round has ended!")
round_end_announced = 1
vote.autotransfer()
@@ -374,7 +391,7 @@ var/global/datum/controller/gameticker/ticker
var/turf/playerTurf = get_turf(Player)
if(emergency_shuttle.departed && emergency_shuttle.evac)
if(isNotAdminLevel(playerTurf.z))
- Player << "You managed to survive, but were marooned on [station_name()] as [Player.real_name]..."
+ Player << "You survived the round, but remained on [station_name()] as [Player.real_name]."
else
Player << "You managed to survive the events on [station_name()] as [Player.real_name]."
else if(isAdminLevel(playerTurf.z))
@@ -415,9 +432,9 @@ var/global/datum/controller/gameticker/ticker
if (!robo.connected_ai)
if (robo.stat != 2)
- world << "[robo.name] (Played by: [robo.key]) survived as an AI-less synthetic! Its laws were:"
+ world << "[robo.name] (Played by: [robo.key]) survived as an AI-less stationbound synthetic! Its laws were:"
else
- world << "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a synthetic without an AI. Its laws were:"
+ world << "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a stationbound synthetic without an AI. Its laws were:"
if(robo) //How the hell do we lose robo between here and the world messages directly above this?
robo.laws.show_laws(world)
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 1062232664..77ee16063e 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -19,7 +19,7 @@
///////////////////////////////
/proc/pick_meteor_start(var/startSide = pick(cardinal))
- var/startLevel = pick(using_map.station_levels)
+ var/startLevel = pick(using_map.station_levels - using_map.sealed_levels)
var/pickedstart = spaceDebrisStartLoc(startSide, startLevel)
return list(startLevel, pickedstart)
@@ -153,16 +153,17 @@
/obj/effect/meteor/proc/ram_turf(var/turf/T)
//first bust whatever is in the turf
for(var/atom/A in T)
- if(A != src)
- A.ex_act(hitpwr)
+ if(A == src) // Don't hit ourselves.
+ continue
+ if(isturf(A)) // Don't hit floors. We'll deal with walls later.
+ continue
+ A.ex_act(hitpwr)
//then, ram the turf if it still exists
if(T)
if(istype(T, /turf/simulated/wall))
var/turf/simulated/wall/W = T
W.take_damage(wall_power) // Stronger walls can halt asteroids.
- else
- T.ex_act(hitpwr) // Floors and other things lack fancy health.
//process getting 'hit' by colliding with a dense object
diff --git a/code/game/gamemodes/mixed/mercrenegade.dm b/code/game/gamemodes/mixed/mercrenegade.dm
index 37ef6b76d9..b1e873535e 100644
--- a/code/game/gamemodes/mixed/mercrenegade.dm
+++ b/code/game/gamemodes/mixed/mercrenegade.dm
@@ -1,11 +1,11 @@
/datum/game_mode/mercren
name = "Mercenaries & Renegades"
- round_description = "A mercenary team has invaded the station, as well as other having brought their own form protection."
- extended_round_description = "Mercenaries and traitors spawn during this round."
+ round_description = "A mercenary team has invaded the station, and others have brought their own form of protection."
+ extended_round_description = "Mercenaries and renegades spawn during this round."
config_tag = "mercren"
required_players = 16 //What could possibly go wrong?
required_players_secret = 15
required_enemies = 8
end_on_antag_death = 0
antag_tags = list(MODE_MERCENARY, MODE_RENEGADE)
- require_all_templates = 1
\ No newline at end of file
+ require_all_templates = 1
diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm
index 2a7d1aa766..1c22098531 100644
--- a/code/game/gamemodes/sandbox/h_sandbox.dm
+++ b/code/game/gamemodes/sandbox/h_sandbox.dm
@@ -63,28 +63,28 @@ datum/hSB
var/mob/living/carbon/human/P = usr
if(P.wear_suit)
P.wear_suit.loc = P.loc
- P.wear_suit.layer = initial(P.wear_suit.layer)
+ P.wear_suit.reset_plane_and_layer()
P.wear_suit = null
P.wear_suit = new/obj/item/clothing/suit/space(P)
- P.wear_suit.layer = 20
+ P.wear_suit.hud_layerise()
if(P.head)
P.head.loc = P.loc
- P.head.layer = initial(P.head.layer)
+ P.head.reset_plane_and_layer()
P.head = null
P.head = new/obj/item/clothing/head/helmet/space(P)
- P.head.layer = 20
+ P.head.hud_layerise()
if(P.wear_mask)
P.wear_mask.loc = P.loc
- P.wear_mask.layer = initial(P.wear_mask.layer)
+ P.wear_mask.reset_plane_and_layer()
P.wear_mask = null
P.wear_mask = new/obj/item/clothing/mask/gas(P)
- P.wear_mask.layer = 20
+ P.wear_mask.hud_layerise()
if(P.back)
P.back.loc = P.loc
- P.back.layer = initial(P.back.layer)
+ P.back.reset_plane_and_layer()
P.back = null
P.back = new/obj/item/weapon/tank/jetpack(P)
- P.back.layer = 20
+ P.back.hud_layerise()
P.internal = P.back
if("hsbmetal")
var/obj/fiftyspawner/iron/hsb = new/obj/fiftyspawner/iron
diff --git a/code/game/gamemodes/technomancer/devices/hypos.dm b/code/game/gamemodes/technomancer/devices/hypos.dm
index dd0fa4436a..f5174c45e3 100644
--- a/code/game/gamemodes/technomancer/devices/hypos.dm
+++ b/code/game/gamemodes/technomancer/devices/hypos.dm
@@ -86,7 +86,7 @@
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity
name = "purity hypo"
- desc = "A refined version of the standard autoinjector, allowing greater capacity. This varient excels at \
+ desc = "A refined version of the standard autoinjector, allowing greater capacity. This variant excels at \
resolving viruses, infections, radiation, and genetic maladies."
filled_reagents = list("spaceacillin" = 9, "arithrazine" = 5, "ryetalyn" = 1)
@@ -97,7 +97,7 @@
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ
name = "organ hypo"
- desc = "A refined version of the standard autoinjector, allowing greater capacity. Organ damage is resolved by this varient."
+ desc = "A refined version of the standard autoinjector, allowing greater capacity. Organ damage is resolved by this variant."
filled_reagents = list("alkysine" = 1, "imidazoline" = 1, "peridaxon" = 13)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat
diff --git a/code/game/gamemodes/technomancer/spell_objs_helpers.dm b/code/game/gamemodes/technomancer/spell_objs_helpers.dm
index 05afae4a3b..f11c027566 100644
--- a/code/game/gamemodes/technomancer/spell_objs_helpers.dm
+++ b/code/game/gamemodes/technomancer/spell_objs_helpers.dm
@@ -21,9 +21,14 @@
return 0
/obj/item/weapon/spell/proc/allowed_to_teleport()
- if(owner && owner.z in using_map.admin_levels)
- return 0
- return 1
+ if(owner)
+ if(owner.z in using_map.admin_levels)
+ return FALSE
+
+ var/turf/T = get_turf(owner)
+ if(T.block_tele)
+ return FALSE
+ return TRUE
/obj/item/weapon/spell/proc/within_range(var/atom/target, var/max_range = 7) // Beyond 7 is off the screen.
if(range(get_dist(owner, target) <= max_range))
diff --git a/code/game/gamemodes/technomancer/spells/blink.dm b/code/game/gamemodes/technomancer/spells/blink.dm
index c0012c991c..320374542d 100644
--- a/code/game/gamemodes/technomancer/spells/blink.dm
+++ b/code/game/gamemodes/technomancer/spells/blink.dm
@@ -23,9 +23,12 @@
var/turf/starting = get_turf(AM)
var/list/targets = list()
+ if(starting.block_tele)
+ return
+
valid_turfs:
for(var/turf/simulated/T in range(AM, range))
- if(T.density || istype(T, /turf/simulated/mineral)) //Don't blink to vacuum or a wall
+ if(T.density || T.block_tele || istype(T, /turf/simulated/mineral)) //Don't blink to vacuum or a wall
continue
for(var/atom/movable/stuff in T.contents)
if(stuff.density)
@@ -54,7 +57,10 @@
if(istype(hit_atom, /atom/movable))
var/atom/movable/AM = hit_atom
if(!within_range(AM))
- user << "\The [AM] is too far away to blink."
+ to_chat(user, "\The [AM] is too far away to blink.")
+ return
+ if(!allowed_to_teleport())
+ to_chat(user, "Teleportation doesn't seem to work here.")
return
if(pay_energy(400))
if(check_for_scepter())
@@ -67,6 +73,9 @@
to_chat(user, "You need more energy to blink [AM] away!")
/obj/item/weapon/spell/blink/on_use_cast(mob/user)
+ if(!allowed_to_teleport())
+ to_chat(user, "Teleportation doesn't seem to work here.")
+ return
if(pay_energy(200))
if(check_for_scepter())
safe_blink(user, calculate_spell_power(10))
@@ -80,6 +89,9 @@
/obj/item/weapon/spell/blink/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(istype(hit_atom, /atom/movable))
var/atom/movable/AM = hit_atom
+ if(!allowed_to_teleport())
+ to_chat(user, "Teleportation doesn't seem to work here.")
+ return
if(pay_energy(300))
visible_message("\The [user] reaches out towards \the [AM] with a glowing hand.")
if(check_for_scepter())
diff --git a/code/game/gamemodes/technomancer/spells/flame_tongue.dm b/code/game/gamemodes/technomancer/spells/flame_tongue.dm
index 271240ad05..ffc12344e8 100644
--- a/code/game/gamemodes/technomancer/spells/flame_tongue.dm
+++ b/code/game/gamemodes/technomancer/spells/flame_tongue.dm
@@ -22,8 +22,7 @@
welder.setWelding(1)
/obj/item/weapon/spell/flame_tongue/Destroy()
- qdel(welder)
- welder = null
+ qdel_null(welder)
return ..()
/obj/item/weapon/weldingtool/spell
diff --git a/code/game/gamemodes/technomancer/spells/illusion.dm b/code/game/gamemodes/technomancer/spells/illusion.dm
index e2afb8d120..5eac627728 100644
--- a/code/game/gamemodes/technomancer/spells/illusion.dm
+++ b/code/game/gamemodes/technomancer/spells/illusion.dm
@@ -62,8 +62,8 @@
illusion.emote(what_to_emote)
/obj/item/weapon/spell/illusion/Destroy()
- if(illusion)
- qdel(illusion)
+ qdel_null(illusion)
+ copied = null
return ..()
// Makes a tiny overlay of the thing the player has copied, so they can easily tell what they currently have.
diff --git a/code/game/gamemodes/technomancer/spells/mark_recall.dm b/code/game/gamemodes/technomancer/spells/mark_recall.dm
index e47b62c031..306de85043 100644
--- a/code/game/gamemodes/technomancer/spells/mark_recall.dm
+++ b/code/game/gamemodes/technomancer/spells/mark_recall.dm
@@ -68,6 +68,9 @@
user << "There's no Mark!"
return 0
else
+ if(!allowed_to_teleport())
+ to_chat(user, "Teleportation doesn't seem to work here.")
+ return
visible_message("\The [user] starts glowing!")
var/light_intensity = 2
var/time_left = 3
diff --git a/code/game/gamemodes/technomancer/spells/passwall.dm b/code/game/gamemodes/technomancer/spells/passwall.dm
index e786134b4c..fa7b5f34b9 100644
--- a/code/game/gamemodes/technomancer/spells/passwall.dm
+++ b/code/game/gamemodes/technomancer/spells/passwall.dm
@@ -46,6 +46,9 @@
checked_turf = get_step(checked_turf, direction) //Advance in the given direction
total_cost += check_for_scepter() ? 400 : 800 //Phasing through matter's expensive, you know.
i--
+ if(checked_turf.block_tele) // The fun ends here.
+ break
+
if(!checked_turf.density) //If we found a destination (a non-dense turf), then we can stop.
var/dense_objs_on_turf = 0
for(var/atom/movable/stuff in checked_turf.contents) //Make sure nothing dense is where we want to go, like an airlock or window.
diff --git a/code/game/gamemodes/technomancer/spells/phase_shift.dm b/code/game/gamemodes/technomancer/spells/phase_shift.dm
index 5b06a7e704..336dbc4314 100644
--- a/code/game/gamemodes/technomancer/spells/phase_shift.dm
+++ b/code/game/gamemodes/technomancer/spells/phase_shift.dm
@@ -37,7 +37,7 @@
for(var/atom/movable/AM in contents) //Eject everything out.
AM.forceMove(get_turf(src))
processing_objects -= src
- ..()
+ return ..()
/obj/effect/phase_shift/process()
for(var/mob/living/L in contents)
diff --git a/code/game/gamemodes/technomancer/spells/radiance.dm b/code/game/gamemodes/technomancer/spells/radiance.dm
index 4d1fb551ee..fe7d83f713 100644
--- a/code/game/gamemodes/technomancer/spells/radiance.dm
+++ b/code/game/gamemodes/technomancer/spells/radiance.dm
@@ -25,7 +25,7 @@
/obj/item/weapon/spell/radiance/Destroy()
processing_objects -= src
log_and_message_admins("has stopped maintaining [src].")
- ..()
+ return ..()
/obj/item/weapon/spell/radiance/process()
var/turf/T = get_turf(src)
diff --git a/code/game/gamemodes/technomancer/spells/spawner/darkness.dm b/code/game/gamemodes/technomancer/spells/spawner/darkness.dm
index be978e7ee4..d83602aa68 100644
--- a/code/game/gamemodes/technomancer/spells/spawner/darkness.dm
+++ b/code/game/gamemodes/technomancer/spells/spawner/darkness.dm
@@ -25,5 +25,6 @@
/obj/effect/temporary_effect/darkness
name = "darkness"
time_to_die = 2 MINUTES
- new_light_range = 6
- new_light_power = -20
\ No newline at end of file
+ invisibility = 101
+ light_range = 6
+ light_power = -20
\ No newline at end of file
diff --git a/code/game/gamemodes/technomancer/spells/spawner/destablize.dm b/code/game/gamemodes/technomancer/spells/spawner/destablize.dm
index 1f7df21125..29381701ca 100644
--- a/code/game/gamemodes/technomancer/spells/spawner/destablize.dm
+++ b/code/game/gamemodes/technomancer/spells/spawner/destablize.dm
@@ -26,13 +26,11 @@
/obj/effect/temporary_effect/destablize
name = "destablizing disturbance"
desc = "This can't be good..."
- icon = 'icons/effects/effects.dmi'
icon_state = "blueshatter"
time_to_die = null
- invisibility = 0
- new_light_range = 6
- new_light_power = 20
- new_light_color = "#C26DDE"
+ light_range = 6
+ light_power = 20
+ light_color = "#C26DDE"
var/pulses_remaining = 40 // Lasts 20 seconds.
var/instability_power = 5
var/instability_range = 6
diff --git a/code/game/gamemodes/technomancer/spells/spawner/fire_blast.dm b/code/game/gamemodes/technomancer/spells/spawner/fire_blast.dm
index ff1a3f7644..69444a4c7b 100644
--- a/code/game/gamemodes/technomancer/spells/spawner/fire_blast.dm
+++ b/code/game/gamemodes/technomancer/spells/spawner/fire_blast.dm
@@ -22,13 +22,11 @@
/obj/effect/temporary_effect/fire_blast
name = "fire blast"
desc = "Run!"
- icon = 'icons/effects/effects.dmi'
icon_state = "at_shield1"
time_to_die = 2.5 SECONDS // After which we go boom.
- invisibility = 0
- new_light_range = 4
- new_light_power = 5
- new_light_color = "#FF6A00"
+ light_range = 4
+ light_power = 5
+ light_color = "#FF6A00"
/obj/effect/temporary_effect/fire_blast/Destroy()
explosion(get_turf(src), -1, 1, 2, 5, adminlog = 1)
diff --git a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
index df13fd2460..5641df2f87 100644
--- a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
+++ b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
@@ -28,13 +28,11 @@
/obj/effect/temporary_effect/pulsar
name = "pulsar"
desc = "Not a real pulsar, but still emits loads of EMP."
- icon = 'icons/effects/effects.dmi'
icon_state = "shield2"
time_to_die = null
- invisibility = 0
- new_light_range = 4
- new_light_power = 5
- new_light_color = "#2ECCFA"
+ light_range = 4
+ light_power = 5
+ light_color = "#2ECCFA"
var/pulses_remaining = 3
/obj/effect/temporary_effect/pulsar/New()
diff --git a/code/game/gamemodes/technomancer/spells/spawner/spawner.dm b/code/game/gamemodes/technomancer/spells/spawner/spawner.dm
index 218c446739..ba86e700bb 100644
--- a/code/game/gamemodes/technomancer/spells/spawner/spawner.dm
+++ b/code/game/gamemodes/technomancer/spells/spawner/spawner.dm
@@ -6,22 +6,6 @@
aspect = null
var/obj/effect/spawner_type = null
-/obj/effect/temporary_effect
- name = "self deleting effect"
- desc = "How are you examining what which cannot be seen?"
- invisibility = 101
- var/time_to_die = 10 SECONDS // Afer which, it will delete itself.
- var/new_light_range = 6
- var/new_light_power = 6
- var/new_light_color = "#FFFFFF"
-
-/obj/effect/temporary_effect/New()
- ..()
- set_light(new_light_range, new_light_power, l_color = new_light_color)
- if(time_to_die)
- spawn(time_to_die)
- qdel(src)
-
/obj/item/weapon/spell/spawner/on_ranged_cast(atom/hit_atom, mob/user)
var/turf/T = get_turf(hit_atom)
if(T)
diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm
index 322a71f7d4..b63662cd86 100644
--- a/code/game/jobs/access_datum.dm
+++ b/code/game/jobs/access_datum.dm
@@ -462,3 +462,9 @@
/datum/access/trader
id = access_trader
access_type = ACCESS_TYPE_PRIVATE
+
+/var/const/access_alien = 300 // For things like crashed ships.
+/datum/access/alien
+ id = access_alien
+ desc = "#%_^&*@!"
+ access_type = ACCESS_TYPE_PRIVATE
diff --git a/code/game/jobs/job/civilian_chaplain.dm b/code/game/jobs/job/civilian_chaplain.dm
index 88a448c571..2eece781ec 100644
--- a/code/game/jobs/job/civilian_chaplain.dm
+++ b/code/game/jobs/job/civilian_chaplain.dm
@@ -82,7 +82,7 @@
while(!accepted)
if(!B) break // prevents possible runtime errors
- new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Creeper", "White Bible", "Holy Light", "Athiest", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon")
+ new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Pagan", "White Bible", "Holy Light", "Athiest", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon","Orthodox","Torah")
switch(new_book_style)
if("Koran")
B.icon_state = "koran"
@@ -90,9 +90,6 @@
if("Scrapbook")
B.icon_state = "scrapbook"
B.item_state = "scrapbook"
- if("Creeper")
- B.icon_state = "creeper"
- B.item_state = "syringe_kit"
if("White Bible")
B.icon_state = "white"
B.item_state = "syringe_kit"
@@ -120,6 +117,15 @@
if("Necronomicon")
B.icon_state = "necronomicon"
B.item_state = "necronomicon"
+ if("Pagan")
+ B.icon_state = "shadows"
+ B.item_state = "syringe_kit"
+ if("Orthodox")
+ B.icon_state = "orthodoxy"
+ B.item_state = "bible"
+ if("Torah")
+ B.icon_state = "torah"
+ B.item_state = "clipboard"
else
B.icon_state = "bible"
B.item_state = "bible"
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index 453fd88774..2b2654c068 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -51,17 +51,17 @@
if(!account_allowed || (H.mind && H.mind.initial_account))
return
- var/loyalty = 1
+ var/income = 1
if(H.client)
- switch(H.client.prefs.nanotrasen_relation)
- if(COMPANY_LOYAL) loyalty = 1.30
- if(COMPANY_SUPPORTATIVE)loyalty = 1.15
- if(COMPANY_NEUTRAL) loyalty = 1
- if(COMPANY_SKEPTICAL) loyalty = 0.85
- if(COMPANY_OPPOSED) loyalty = 0.70
+ switch(H.client.prefs.economic_status)
+ if(CLASS_UPPER) income = 1.30
+ if(CLASS_UPMID) income = 1.15
+ if(CLASS_MIDDLE) income = 1
+ if(CLASS_LOWMID) income = 0.75
+ if(CLASS_LOWER) income = 0.50
//give them an account in the station database
- var/money_amount = (rand(15,40) + rand(15,40)) * loyalty * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks.
+ var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks.
var/datum/money_account/M = create_account(H.real_name, money_amount, null)
if(H.mind)
var/remembered_info = ""
diff --git a/code/game/jobs/job/special_sc_vr.dm b/code/game/jobs/job/special_sc_vr.dm
new file mode 100644
index 0000000000..8750fc6578
--- /dev/null
+++ b/code/game/jobs/job/special_sc_vr.dm
@@ -0,0 +1,194 @@
+//These are a copy of Polaris' Southern Cross jobs
+
+var/const/access_pilot = 67
+var/const/access_explorer = 43
+
+////////////////////////////////////////////////////////////
+
+/datum/access/pilot
+ id = access_pilot
+ desc = "Pilot"
+ region = ACCESS_REGION_SUPPLY
+
+/datum/access/explorer
+ id = access_explorer
+ desc = "Explorer"
+ region = ACCESS_REGION_GENERAL
+
+////////////////////////////////////////////////////////////
+
+/obj/item/weapon/card/id/medical/sar
+ assignment = "Search and Rescue"
+ rank = "Search and Rescue"
+ job_access_type = /datum/job/sar
+
+/obj/item/weapon/card/id/civilian/pilot
+ assignment = "Pilot"
+ rank = "Pilot"
+ job_access_type = /datum/job/pilot
+
+/obj/item/weapon/card/id/civilian/explorer
+ assignment = "Explorer"
+ rank = "Explorer"
+ job_access_type = /datum/job/explorer
+
+////////////////////////////////////////////////////////////
+
+/datum/job/pilot
+ title = "Pilot"
+ flag = PILOT
+ department = "Civilian"
+ department_flag = MEDSCI
+ faction = "Station"
+ total_positions = 2
+ spawn_positions = 2
+ supervisors = "the head of personnel"
+ selection_color = "#515151"
+ idtype = /obj/item/weapon/card/id/civilian/pilot
+ economic_modifier = 4
+ whitelist_only = 1
+ latejoin_only = 1
+ access = list(access_pilot, access_cargo, access_mining, access_mining_station)
+ minimal_access = list(access_pilot, access_cargo, access_mining, access_mining_station)
+ outfit_type = /decl/hierarchy/outfit/job/pilot
+
+/datum/job/explorer
+ title = "Explorer"
+ flag = EXPLORER
+ department = "Science"
+ department_flag = MEDSCI
+ faction = "Station"
+ total_positions = 4
+ spawn_positions = 4
+ supervisors = "the explorer leader and the head of personnel"
+ selection_color = "#515151"
+ idtype = /obj/item/weapon/card/id/civilian/explorer
+ economic_modifier = 4
+ whitelist_only = 1
+ latejoin_only = 1
+ access = list(access_pilot, access_explorer)
+ minimal_access = list(access_pilot, access_explorer)
+ outfit_type = /decl/hierarchy/outfit/job/explorer2
+/* I split this into multiple jobs because it's set to latejoin_only, which means you can't see it to select alt titles.
+ alt_titles = list(
+ "Explorer Technician" = /decl/hierarchy/outfit/job/explorer2/technician,
+ "Explorer Medic" = /decl/hierarchy/outfit/job/explorer2/medic)
+*/
+
+/datum/job/explorer/technician
+ title = "Explorer Technician"
+ flag = EXPLORER_T
+ outfit_type = /decl/hierarchy/outfit/job/explorer2/technician
+
+/datum/job/explorer/medic
+ title = "Explorer Medic"
+ flag = EXPLORER_M
+ outfit_type = /decl/hierarchy/outfit/job/explorer2/medic
+
+/datum/job/sar
+ title = "Search and Rescue"
+ flag = SAR
+ department = "Medical"
+ department_flag = MEDSCI
+ faction = "Station"
+ total_positions = 2
+ spawn_positions = 2
+ supervisors = "the chief medical officer"
+ selection_color = "#515151"
+ idtype = /obj/item/weapon/card/id/medical
+ economic_modifier = 4
+ whitelist_only = 1
+ latejoin_only = 1
+ access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist, access_explorer)
+ minimal_access = list(access_medical, access_medical_equip, access_morgue, access_explorer)
+ outfit_type = /decl/hierarchy/outfit/job/medical/sar
+
+////////////////////////////////////////////////////////////
+
+/decl/hierarchy/outfit/job/explorer2
+ name = OUTFIT_JOB_NAME("Explorer")
+ shoes = /obj/item/clothing/shoes/boots/winter/explorer
+ uniform = /obj/item/clothing/under/explorer
+ l_ear = /obj/item/device/radio/headset/explorer
+ id_slot = slot_wear_id
+ pda_slot = slot_l_store
+ pda_type = /obj/item/device/pda/cargo // Brown looks more rugged
+ id_type = /obj/item/weapon/card/id/civilian/explorer
+ id_pda_assignment = "Explorer"
+
+/decl/hierarchy/outfit/job/explorer2/technician
+ name = OUTFIT_JOB_NAME("Explorer Technician")
+ belt = /obj/item/weapon/storage/belt/utility/full
+ pda_slot = slot_l_store
+ id_pda_assignment = "Explorer Technician"
+
+/decl/hierarchy/outfit/job/explorer2/medic
+ name = OUTFIT_JOB_NAME("Explorer Medic")
+ l_hand = /obj/item/weapon/storage/firstaid/regular
+ pda_slot = slot_l_store
+ id_pda_assignment = "Explorer Medic"
+
+/decl/hierarchy/outfit/job/pilot
+ name = OUTFIT_JOB_NAME("Pilot")
+ shoes = /obj/item/clothing/shoes/black
+ uniform = /obj/item/clothing/under/color/black
+ suit = /obj/item/clothing/suit/storage/toggle/bomber
+ gloves = /obj/item/clothing/gloves/fingerless
+ glasses = /obj/item/clothing/glasses/fakesunglasses/aviator
+ l_ear = /obj/item/device/radio/headset/pilot
+ id_slot = slot_wear_id
+ pda_slot = slot_belt
+ pda_type = /obj/item/device/pda/cargo // Brown looks more rugged
+ id_type = /obj/item/weapon/card/id/civilian/pilot
+ id_pda_assignment = "Pilot"
+
+/decl/hierarchy/outfit/job/medical/sar
+ name = OUTFIT_JOB_NAME("Search and Rescue")
+ uniform = /obj/item/clothing/under/utility/blue
+ suit = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar
+ shoes = /obj/item/clothing/shoes/boots/winter/explorer
+ l_hand = /obj/item/weapon/storage/firstaid/adv
+ belt = /obj/item/weapon/storage/belt/medical/emt
+ pda_slot = slot_l_store
+ id_type = /obj/item/weapon/card/id/medical/sar
+ id_pda_assignment = "Search and Rescue"
+ flags = OUTFIT_HAS_BACKPACK|OUTFIT_EXTENDED_SURVIVAL
+
+////////////////////////////////////////////////////////////
+
+/obj/item/device/encryptionkey/pilot
+ name = "pilot's encryption key"
+ icon_state = "com_cypherkey"
+ channels = list("Supply" = 1, "Explorer" = 1)
+
+/obj/item/device/encryptionkey/explorer
+ name = "explorer radio encryption key"
+ icon_state = "com_cypherkey"
+ channels = list("Explorer" = 1)
+
+////////////////////////////////////////////////////////////
+
+/obj/item/device/radio/headset/pilot
+ name = "pilot's headset"
+ desc = "A bowman headset used by pilots, has access to supply and explorer channels."
+ icon_state = "cargo_headset_alt"
+ item_state = "headset"
+ ks2type = /obj/item/device/encryptionkey/pilot
+
+/obj/item/device/radio/headset/explorer
+ name = "explorer's headset"
+ desc = "Headset used by explorers for exploring. Access to the explorer channel."
+ icon_state = "mine_headset"
+ item_state = "headset"
+ ks2type = /obj/item/device/encryptionkey/explorer
+
+////////////////////////////////////////////////////////////
+
+/obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar
+ name = "search and rescue winter coat"
+ desc = "A heavy winter jacket. A white star of life is emblazoned on the back, with the words search and rescue written underneath."
+ icon_state = "coatsar"
+ item_icons = list(slot_wear_suit_str = 'maps/southern_cross/icons/mob/sc_suit.dmi')
+ icon = 'maps/southern_cross/icons/obj/sc_suit.dmi'
+ armor = list(melee = 15, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 5)
+ valid_accessory_slots = list(ACCESSORY_SLOT_INSIGNIA)
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 94dabbcd2e..f88dd1f555 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -76,7 +76,7 @@ var/global/datum/controller/occupations/job_master
proc/FreeRole(var/rank) //making additional slot on the fly
var/datum/job/job = GetJob(rank)
- if(job && job.current_positions >= job.total_positions && job.total_positions != -1)
+ if(job && job.total_positions != -1)
job.total_positions++
return 1
return 0
@@ -483,7 +483,7 @@ var/global/datum/controller/occupations/job_master
H.buckled = W
H.update_canmove()
W.set_dir(H.dir)
- W.buckled_mob = H
+ W.buckled_mobs |= H
W.add_fingerprint(H)
if(R)
W.color = R.color
@@ -641,10 +641,10 @@ var/global/datum/controller/occupations/job_master
H.forceMove(spawnpos.get_spawn_position())
. = spawnpos.msg
else
- H << "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead."
+ H << "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the default arrivals location instead." //VOREStation Edit - Generic, not shuttle.
var/spawning = pick(latejoin)
H.forceMove(get_turf(spawning))
- . = "will arrive to the station shortly by shuttle"
+ . = "will arrive at the station shortly" //VOREStation Edit - Grammar but mostly 'shuttle' reference removal, and this also applies to notified spawn-character verb use
else
var/spawning = pick(latejoin)
H.forceMove(get_turf(spawning))
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 2a225228bc..083c3699b8 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -26,7 +26,11 @@ var/const/PSYCHIATRIST =(1<<7)
var/const/ROBOTICIST =(1<<8)
var/const/XENOBIOLOGIST =(1<<9)
var/const/PARAMEDIC =(1<<10)
-
+var/const/SAR =(1<<11) //VOREStation THEFT
+var/const/PILOT =(1<<12) //VOREStation THEFT
+var/const/EXPLORER =(1<<13) //VOREStation THEFT
+var/const/EXPLORER_T =(1<<14) //VOREStation THEFT
+var/const/EXPLORER_M =(1<<15) //VOREStation THEFT
var/const/CIVILIAN =(1<<2)
@@ -41,11 +45,10 @@ var/const/CARGOTECH =(1<<7)
var/const/MINER =(1<<8)
var/const/LAWYER =(1<<9)
var/const/CHAPLAIN =(1<<10)
-var/const/CLOWN =(1<<11)
-var/const/MIME =(1<<12)
-var/const/ASSISTANT =(1<<13)
-var/const/BRIDGE =(1<<14)
-
+var/const/ASSISTANT =(1<<11)
+var/const/BRIDGE =(1<<12)
+var/const/CLOWN =(1<<13) //VOREStation Add
+var/const/MIME =(1<<14) //VOREStation Add
var/list/assistant_occupations = list(
)
@@ -75,6 +78,7 @@ var/list/medical_positions = list(
"Geneticist",
"Psychiatrist",
"Chemist",
+ "Search and Rescue", // VOREStation Edit - Moved SAR from planetary -> medical
"Paramedic"
)
@@ -84,6 +88,7 @@ var/list/science_positions = list(
"Scientist",
"Geneticist", //Part of both medical and science
"Roboticist",
+ "Explorer", // VOREStation Edit - Moved Explorer from planetary -> science
"Xenobiologist"
)
@@ -103,6 +108,7 @@ var/list/civilian_positions = list(
"Librarian",
"Lawyer",
"Chaplain",
+ "Pilot", // VOREStation Edit - Moved Pilot from planetary -> civ
"Assistant"
)
@@ -115,6 +121,13 @@ var/list/security_positions = list(
)
+var/list/planet_positions = list(
+ // "Explorer", // VOREStation Edit - Moved Explorer from planetary -> science
+ // "Pilot", // VOREStation Edit - Moved Pilot from planetary -> civ
+ // "Search and Rescue" // VOREStation Edit - Moved SAR from planetary -> medical
+)
+
+
var/list/nonhuman_positions = list(
"AI",
"Cyborg",
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index b5f5af5bb6..b789212436 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -1,5 +1,5 @@
/obj/machinery/sleep_console
- name = "Sleeper Console"
+ name = "sleeper console"
icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icon.
icon_state = "sleeperconsole"
var/obj/machinery/sleeper/sleeper
@@ -34,7 +34,7 @@
return 1
if(sleeper.panel_open)
- user << "Close the maintenance panel first."
+ to_chat(user, "Close the maintenance panel first.")
return
if(!sleeper)
@@ -44,7 +44,7 @@
else if(sleeper)
return sleeper.ui_interact(user)
else
- user << "Sleeper not found!"
+ to_chat(user, "Sleeper not found!")
/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user)
if(computer_deconstruction_screwdriver(user, I))
@@ -61,7 +61,7 @@
/obj/machinery/sleeper
name = "sleeper"
- desc = "A fancy bed with built-in injectors, a dialysis machine, and a limited health scanner."
+ desc = "A stasis pod with built-in injectors, a dialysis machine, and a limited health scanner."
icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icons
icon_state = "sleeper_0"
density = 1
@@ -181,7 +181,7 @@
return 1
if(usr == occupant)
- usr << "You can't reach the controls from the inside."
+ to_chat(usr, "You can't reach the controls from the inside.")
return
add_fingerprint(usr)
@@ -221,7 +221,7 @@
I.loc = src
user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].")
else
- user << "\The [src] has a beaker already."
+ to_chat(user, "\The [src] has a beaker already.")
return
/obj/machinery/sleeper/verb/move_eject()
@@ -233,7 +233,7 @@
if(DEAD)
return
if(UNCONSCIOUS)
- usr << "You struggle through the haze to hit the eject button. This will take a couple of minutes..."
+ to_chat(usr, "You struggle through the haze to hit the eject button. This will take a couple of minutes...")
sleep(2 MINUTES)
if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already
return
@@ -247,7 +247,7 @@
add_fingerprint(usr)
/obj/machinery/sleeper/MouseDrop_T(var/mob/target, var/mob/user)
- if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target))
+ if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
return
go_in(target, user)
@@ -279,9 +279,11 @@
if(stat & (BROKEN|NOPOWER))
return
if(occupant)
- user << "\The [src] is already occupied."
+ to_chat(user, "\The [src] is already occupied.")
+ return
+ if(!ishuman(M))
+ to_chat(user, "\The [src] is not designed for that organism!")
return
-
if(M == user)
visible_message("\The [user] starts climbing into \the [src].")
else
@@ -289,7 +291,7 @@
if(do_after(user, 20))
if(occupant)
- user << "\The [src] is already occupied."
+ to_chat(user, "\The [src] is already occupied.")
return
M.stop_pulling()
if(M.client)
@@ -333,8 +335,8 @@
if(occupant.reagents.get_reagent_amount(chemical) + amount <= 20)
use_power(amount * CHEM_SYNTH_ENERGY)
occupant.reagents.add_reagent(chemical, amount)
- user << "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream."
+ to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.")
else
- user << "The subject has too many chemicals."
+ to_chat(user, "The subject has too many chemicals in their bloodstream.")
else
- user << "There's no suitable occupant in \the [src]."
+ to_chat(user, "There's no suitable occupant in \the [src].")
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 42654277e0..349d9fbc07 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -39,20 +39,23 @@
else if(istype(G, /obj/item/weapon/grab))
var/obj/item/weapon/grab/H = G
if(panel_open)
- user << "Close the maintenance panel first."
+ to_chat(user, "Close the maintenance panel first.")
return
if(!ismob(H.affecting))
return
+ if(!ishuman(H.affecting))
+ to_chat(user, "\The [src] is not designed for that organism!")
+ return
if(occupant)
- user << "The scanner is already occupied!"
+ 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)
- user << "[H.affecting.name] has a fucking slime attached to them, deal with that first."
+ to_chat(user, "[H.affecting.name] has a slime attached to them, deal with that first.")
return
var/mob/M = H.affecting
if(M.abiotic())
- user << "Subject cannot have abiotic items on."
+ to_chat(user, "Subject cannot have abiotic items on.")
return
M.forceMove(src)
occupant = M
@@ -72,20 +75,20 @@
if(!ishuman(user) && !isrobot(user))
return 0 //not a borg or human
if(panel_open)
- user << "Close the maintenance panel first."
+ to_chat(user, "Close the maintenance panel first.")
return 0 //panel open
if(occupant)
- user << "\The [src] is already occupied."
+ to_chat(user, "\The [src] is already occupied.")
return 0 //occupied
if(O.buckled)
return 0
if(O.abiotic())
- user << "Subject cannot have abiotic items on."
+ 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)
- user << "[O] has a fucking slime attached to them, deal with that first."
+ to_chat(user, "[O] has a slime attached to them, deal with that first.")
return 0
if(O == user)
@@ -185,9 +188,9 @@
var/obj/machinery/bodyscanner/C = P.connectable
scanner = C
C.console = src
- user << " You link the [src] to the [P.connectable]!"
+ to_chat(user, " You link the [src] to the [P.connectable]!")
else
- user << " You store the [src] in the [P]'s buffer!"
+ to_chat(user, " You store the [src] in the [P]'s buffer!")
P.connectable = src
return
else
@@ -243,7 +246,7 @@
return
if (scanner.panel_open)
- user << "Close the maintenance panel first."
+ to_chat(user, "Close the maintenance panel first.")
return
if(!scanner)
@@ -253,7 +256,7 @@
else if(scanner)
return ui_interact(user)
else
- user << "Scanner not found!"
+ to_chat(user, "Scanner not found!")
/obj/machinery/body_scanconsole/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
@@ -588,4 +591,4 @@
else
dat = " Error: No Body Scanner connected."
- printing_text = dat
\ No newline at end of file
+ printing_text = dat
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 4c73a4a17d..8c9737cd9d 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -180,15 +180,17 @@
if(!get_danger_level(target_temperature, TLV["temperature"]) && abs(environment.temperature - target_temperature) > 2.0)
update_use_power(2)
regulating_temperature = 1
- visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
+ audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
"You hear a click and a faint electronic hum.")
+ playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
else
//check for when we should stop adjusting temperature
if(get_danger_level(target_temperature, TLV["temperature"]) || abs(environment.temperature - target_temperature) <= 0.5)
update_use_power(1)
regulating_temperature = 0
- visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
+ audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
"You hear a click as a faint electronic humming stops.")
+ playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
if(regulating_temperature)
if(target_temperature > T0C + MAX_TEMPERATURE)
diff --git a/code/game/machinery/atmoalter/area_atmos_computer.dm b/code/game/machinery/atmoalter/area_atmos_computer.dm
index b53469d9b6..bd3b390fc8 100644
--- a/code/game/machinery/atmoalter/area_atmos_computer.dm
+++ b/code/game/machinery/atmoalter/area_atmos_computer.dm
@@ -16,8 +16,10 @@
/obj/machinery/computer/area_atmos/New()
..()
- //So the scrubbers have time to spawn
desc += "[range] meters."
+
+/obj/machinery/computer/area_atmos/initialize()
+ . = ..()
scanscrubbers()
/obj/machinery/computer/area_atmos/attack_ai(var/mob/user as mob)
diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm
index cb0a67cc04..92004f9afe 100644
--- a/code/game/machinery/atmoalter/portable_atmospherics.dm
+++ b/code/game/machinery/atmoalter/portable_atmospherics.dm
@@ -40,6 +40,9 @@
else
update_icon()
+/obj/machinery/portable_atmospherics/blob_act()
+ qdel(src)
+
/obj/machinery/portable_atmospherics/proc/StandardAirMix()
return list(
"oxygen" = O2STANDARD * MolesForPressure(),
diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm
index 4a8b4f2d3c..5203d7ec2e 100644
--- a/code/game/machinery/biogenerator.dm
+++ b/code/game/machinery/biogenerator.dm
@@ -123,6 +123,7 @@
dat += "Leather Coat ([round(500/build_eff)])
"
dat += "Leather Jacket ([round(500/build_eff)])
"
dat += "Winter Coat ([round(500/build_eff)])
"
+ dat += "4 Algae Sheets ([round(400/build_eff)])
" //VOREStation Edit - Algae for oxygen generator
//dat += "Other
"
//dat += "Monkey (500)
"
else
@@ -236,6 +237,9 @@
new/obj/item/clothing/suit/storage/toggle/brown_jacket(loc)
if("wintercoat")
new/obj/item/clothing/suit/storage/hooded/wintercoat(loc)
+ if("algae") //VOREStation Edit - Algae for oxygen generator
+ var/obj/item/stack/material/algae/A = new(loc)
+ A.amount = 4 //VOREStation Edit End
processing = 0
menustat = "complete"
update_icon()
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 4e6405f34c..c25b3448bd 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -111,6 +111,11 @@
..() //and give it the regular chance of being deleted outright
+/obj/machinery/camera/blob_act()
+ if((stat & BROKEN) || invuln)
+ return
+ destroy()
+
/obj/machinery/camera/hitby(AM as mob|obj)
..()
if (istype(AM, /obj))
@@ -130,7 +135,7 @@
if(user.species.can_shred(user))
set_status(0)
user.do_attack_animation(src)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed())
visible_message("\The [user] slashes at [src]!")
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
add_hiddenprint(user)
@@ -210,7 +215,7 @@
src.bugged = 1
else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
if (W.force >= src.toughness)
user.do_attack_animation(src)
visible_message("[src] has been [W.attack_verb.len? pick(W.attack_verb) : "attacked"] with [W] by [user]!")
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 351c1bd3b3..e210119445 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -203,12 +203,7 @@ var/global/list/engineering_networks = list(
/obj/machinery/camera/proc/upgradeMotion()
assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
setPowerUsage()
- if(!(src in machines))
- if(!machinery_sort_required && ticker)
- dd_insertObjectList(machines, src)
- else
- machines += src
- machinery_sort_required = 1
+ START_MACHINE_PROCESSING(src)
update_coverage()
/obj/machinery/camera/proc/setPowerUsage()
diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm
index 1aadf981c4..4f6b7df94f 100644
--- a/code/game/machinery/camera/tracking.dm
+++ b/code/game/machinery/camera/tracking.dm
@@ -2,7 +2,7 @@
#define TRACKING_NO_COVERAGE 1
#define TRACKING_TERMINATE 2
-/mob/living/silicon/ai/var/max_locations = 10
+/mob/living/silicon/ai/var/max_locations = 30
/mob/living/silicon/ai/var/stored_locations[0]
/proc/InvalidPlayerTurf(turf/T as turf)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index ff32e65c07..9bed646bc5 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -173,7 +173,7 @@
/obj/machinery/clonepod/process()
var/visible_message = 0
- for(var/obj/item/weapon/reagent_containers/food/snacks/meat in range(1, src))
+ for(var/obj/item/weapon/reagent_containers/food/snacks/meat/meat in range(1, src))
qdel(meat)
biomass += 50
visible_message = 1 // Prevent chatspam when multiple meat are near
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 29196f44d5..785d841702 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -458,6 +458,7 @@
emergency_shuttle.call_evac()
log_game("[key_name(user)] has called the shuttle.")
message_admins("[key_name_admin(user)] has called the shuttle.", 1)
+ admin_chat_message(message = "Emergency evac beginning! Called by [key_name(user)]!", color = "#CC2222") //VOREStation Add
return
@@ -505,6 +506,7 @@
log_game("[user? key_name(user) : "Autotransfer"] has called the shuttle.")
message_admins("[user? key_name_admin(user) : "Autotransfer"] has called the shuttle.", 1)
+ admin_chat_message(message = "Autotransfer shuttle dispatched, shift ending soon.", color = "#2277BB") //VOREStation Add
return
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index dbd99ac935..3d676e2609 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -59,6 +59,9 @@
set_broken()
..()
+/obj/machinery/computer/blob_act()
+ ex_act(2)
+
/obj/machinery/computer/update_icon()
overlays.Cut()
if(stat & NOPOWER)
diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm
index 3d0235fab9..1b34283d14 100644
--- a/code/game/machinery/computer/prisoner.dm
+++ b/code/game/machinery/computer/prisoner.dm
@@ -31,7 +31,7 @@
var/turf/Tr = null
for(var/obj/item/weapon/implant/chem/C in world)
Tr = get_turf(C)
- if((Tr) && (Tr.z != src.z)) continue//Out of range
+ if(!Tr) continue//Out of range
if(!C.implanted) continue
dat += "[C.imp_in.name] | Remaining Units: [C.reagents.total_volume] | Inject: "
dat += "((1))"
@@ -41,7 +41,7 @@
dat += "
Tracking Implants
"
for(var/obj/item/weapon/implant/tracking/T in world)
Tr = get_turf(T)
- if((Tr) && (Tr.z != src.z)) continue//Out of range
+ if(!Tr) continue//Out of range
if(!T.implanted) continue
var/loc_display = "Unknown"
var/mob/living/carbon/M = T.imp_in
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 4d29a56b89..f91ac2b677 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -37,6 +37,7 @@
// Locks or unlocks the cyborg
if (href_list["lockdown"])
var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"])
+ var/failmsg = ""
if(!target || !istype(target))
return
@@ -57,11 +58,19 @@
var/istraitor = target.mind.special_role
if (istraitor)
+ failmsg = "failed (target is traitor) "
target.lockcharge = !target.lockcharge
if (target.lockcharge)
- target << "Someone tried to lock you down!"
+ to_chat(target, "Someone tried to lock you down!")
else
- target << "Someone tried to lift your lockdown!"
+ to_chat(target, "Someone tried to lift your lockdown!")
+ else if (target.emagged)
+ failmsg = "failed (target is hacked) "
+ target.lockcharge = !target.lockcharge
+ if (target.lockcharge)
+ to_chat(target, "Someone tried to lock you down!")
+ else
+ to_chat(target, "Someone tried to lift your lockdown!")
else
target.canmove = !target.canmove
target.lockcharge = !target.canmove //when canmove is 1, lockcharge should be 0
@@ -70,7 +79,7 @@
target << "You have been locked down!"
else
target << "Your lockdown has been lifted!"
- message_admins("[key_name_admin(usr)] [istraitor ? "failed (target is traitor) " : ""][target.lockcharge ? "lockdown" : "release"] on [target.name]!")
+ message_admins("[key_name_admin(usr)] [failmsg][target.lockcharge ? "lockdown" : "release"] on [target.name]!")
log_game("[key_name(usr)] attempted to [target.lockcharge ? "lockdown" : "release"] [target.name] on the robotics console!")
diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm
index 4bcc43bbec..c56a9a996c 100644
--- a/code/game/machinery/computer/supply.dm
+++ b/code/game/machinery/computer/supply.dm
@@ -40,6 +40,8 @@
\nRequest items
View approved orders
View requests
+ "} // VOREStation Edit - Export reports
+ dat += {"\nView export report
Close"}
user << browse(dat, "window=computer;size=575x450")
@@ -198,6 +200,8 @@
\nOrder items
\n
\nView requests
\n
\nView orders
\n
+ "} // VOREStation Edit - Export reports
+ dat += {"\nView export report
\n
\nClose"}
@@ -367,6 +371,20 @@
temp += "
Clear list"
temp += "
OK"
+ //VOREStation Edit - Export reports
+ else if (href_list["viewexport"])
+ temp = "Previous shuttle export report:
"
+ var/cratecount = 0
+ var/totalvalue = 0
+ for(var/S in supply_controller.exported_crates)
+ var/datum/exported_crate/EC = S
+ cratecount += 1
+ totalvalue += EC.value
+ temp += "[EC.name] exported for [EC.value] supply points
"
+ temp += "
Shipment of [cratecount] crates exported for [totalvalue] supply points.
"
+ temp += "
OK"
+ //VOREStation Edit End
+
else if (href_list["rreq"])
var/ordernum = text2num(href_list["rreq"])
temp = "Invalid Request.
"
diff --git a/code/game/machinery/computer3/computers/HolodeckControl.dm b/code/game/machinery/computer3/computers/HolodeckControl.dm
index fc781430c4..4d63b95673 100644
--- a/code/game/machinery/computer3/computers/HolodeckControl.dm
+++ b/code/game/machinery/computer3/computers/HolodeckControl.dm
@@ -155,7 +155,7 @@
var/mob/M = obj.loc
if(ismob(M))
M.remove_from_mob(obj)
- M.update_icons() //so their overlays update
+ M.update_icons_layers() //so their overlays update
if(!silent)
var/obj/oldobj = obj
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index 33529f4447..6709cb2363 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -30,16 +30,9 @@
var/turf/T = src.loc
T.contents += contents
if(beaker)
- beaker.loc = get_step(src.loc, SOUTH) //Beaker is carefully ejected from the wreckage of the cryotube
- ..()
-
-/obj/machinery/atmospherics/unary/cryo_cell/initialize()
- if(node) return
- var/node_connect = dir
- for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
- if(target.initialize_directions & get_dir(target,src))
- node = target
- break
+ beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube
+ beaker = null
+ . = ..()
/obj/machinery/atmospherics/unary/cryo_cell/process()
..()
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 473430c74c..84c111ca81 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -375,6 +375,13 @@
// VOREStation
hook_vr("despawn", list(to_despawn, src))
+ if(ishuman(to_despawn))
+ var/mob/living/carbon/human/H = to_despawn
+ if(H.nif)
+ var/datum/nifsoft/soulcatcher/SC = H.nif.imp_check(NIF_SOULCATCHER)
+ if(SC)
+ for(var/bm in SC.brainmobs)
+ despawn_occupant(bm)
// VOREStation
//Drop all items into the pod.
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 76eb2da2d5..2ed722e129 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -96,7 +96,7 @@ for reference:
return
return
else
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
switch(W.damtype)
if("fire")
health -= W.force * 1
@@ -214,7 +214,7 @@ for reference:
if(health <= 0)
explode()
return
-
+
/obj/machinery/deployable/barrier/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
return
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 6a006ae2c4..285bab46fe 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -197,8 +197,14 @@
/obj/machinery/door/airlock/centcom
name = "Centcom Airlock"
icon = 'icons/obj/doors/Doorele.dmi'
- //opacity = 0 //VOREStation Edit - Why is this like this??
req_one_access = list(access_cent_general)
+ opacity = 1
+
+/obj/machinery/door/airlock/glass_centcom
+ name = "Airlock"
+ icon = 'icons/obj/doors/Dooreleglass.dmi'
+ opacity = 0
+ glass = 1
/obj/machinery/door/airlock/vault
name = "Vault"
@@ -447,6 +453,24 @@
icon = 'icons/obj/doors/shuttledoors_vertical.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft/vertical
+/obj/machinery/door/airlock/alien
+ name = "alien airlock"
+ desc = "You're fairly sure this is a door."
+ icon = 'icons/obj/doors/Dooralien.dmi'
+ explosion_resistance = 20
+ secured_wires = TRUE
+ hackProof = TRUE
+ assembly_type = /obj/structure/door_assembly/door_assembly_alien
+ req_one_access = list(access_alien)
+
+/obj/machinery/door/airlock/alien/locked
+ icon_state = "door_locked"
+ locked = TRUE
+
+/obj/machinery/door/airlock/alien/public // Entry to UFO.
+ req_one_access = list()
+ normalspeed = FALSE // So it closes faster and hopefully keeps the warm air inside.
+
/*
About the new airlock wires panel:
* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires.
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index c87e78cfd5..871f42eb7b 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -21,8 +21,7 @@ obj/machinery/door/airlock/receive_signal(datum/signal/signal)
if(id_tag != signal.data["tag"] || !signal.data["command"]) return
cur_command = signal.data["command"]
- spawn()
- execute_current_command()
+ execute_current_command()
obj/machinery/door/airlock/proc/execute_current_command()
if(operating)
@@ -31,9 +30,10 @@ obj/machinery/door/airlock/proc/execute_current_command()
if (!cur_command)
return
- do_command(cur_command)
- if (command_completed(cur_command))
- cur_command = null
+ spawn()
+ do_command(cur_command)
+ if (command_completed(cur_command))
+ cur_command = null
obj/machinery/door/airlock/proc/do_command(var/command)
switch(command)
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index bf40ad9fff..870b080c79 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -132,7 +132,7 @@
else if(src.density && (user.a_intent == I_HURT)) //If we can't pry it open and it's a weapon, let's hit it.
var/obj/item/weapon/W = C
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
if(W.force < min_force)
@@ -162,7 +162,7 @@
else if(src.density && (user.a_intent == I_HURT)) //If we can't pry it open and it's not a weapon.... Eh, let's attack it anyway.
var/obj/item/weapon/W = C
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
if(W.force < min_force) //No actual non-weapon item shouls have a force greater than the min_force, but let's include this just in case.
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 5f48ebeef4..6dd268676d 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -79,7 +79,8 @@
if(close_door_at && world.time >= close_door_at)
if(autoclose)
close_door_at = next_close_time()
- close()
+ spawn(0)
+ close()
else
close_door_at = 0
@@ -262,7 +263,7 @@
//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(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
if(W.force < min_force)
@@ -357,6 +358,13 @@
take_damage(150)
return
+/obj/machinery/door/blob_act()
+ if(density) // If it's closed.
+ if(stat & BROKEN)
+ spawn(0)
+ open(1)
+ else
+ take_damage(100)
/obj/machinery/door/update_icon()
if(density)
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index ede8c53de2..b22dc27dba 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -391,8 +391,9 @@
else
use_power(360)
else
- log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.")
- message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.")
+ if(usr && usr.ckey)
+ log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.")
+ message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.")
latetoggle()
return ..()
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 4fc02c70cc..d25473e709 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -161,7 +161,7 @@
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
visible_message("[user] smashes against the [src.name].", 1)
user.do_attack_animation(src)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed())
take_damage(25)
return
return src.attackby(user, user)
@@ -246,7 +246,7 @@
//If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway)
if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card))
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(I))
var/aforce = I.force
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
visible_message("[src] was hit by [I].")
diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm
index c6b32d75db..09cbeb2e00 100644
--- a/code/game/machinery/frame.dm
+++ b/code/game/machinery/frame.dm
@@ -7,7 +7,7 @@
construction_frame_floor = list()
for(var/R in typesof(/datum/frame/frame_types) - /datum/frame/frame_types)
var/datum/frame/frame_types/type = new R
- if(type.frame_style == "wall")
+ if(type.frame_style == FRAME_STYLE_WALL)
construction_frame_wall += type
else
construction_frame_floor += type
@@ -16,13 +16,17 @@
construction_frame_wall += cancel
construction_frame_floor += cancel
+
+//////////////////////////////
+// Frame Type Datum - Describes the frame structures that can be created from a frame item.
+//////////////////////////////
/datum/frame/frame_types
var/icon/icon_override // Icon to set on frame object when building. If null icon is unchanged.
var/name // Name assigned to the frame object.
var/frame_size = 5 // Sheets of metal required to build.
var/frame_class // Determines construction method. "machine", "computer", "alarm", or "display"
var/circuit // Type path of the circuit board that comes built in with this frame. Null to require adding a circuit.
- var/frame_style = "floor" // "floor" or "wall"
+ var/frame_style = FRAME_STYLE_FLOOR // "floor" or "wall"
var/x_offset // For wall frames: pixel_x
var/y_offset // For wall frames: pixel_y
@@ -34,157 +38,161 @@
/datum/frame/frame_types/computer
name = "Computer"
- frame_class = "computer"
+ frame_class = FRAME_CLASS_COMPUTER
/datum/frame/frame_types/machine
name = "Machine"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
/datum/frame/frame_types/conveyor
name = "Conveyor"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
circuit = /obj/item/weapon/circuitboard/conveyor
/datum/frame/frame_types/photocopier
name = "Photocopier"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
/datum/frame/frame_types/washing_machine
name = "Washing Machine"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
/datum/frame/frame_types/medical_console
name = "Medical Console"
- frame_class = "computer"
+ frame_class = FRAME_CLASS_COMPUTER
/datum/frame/frame_types/medical_pod
name = "Medical Pod"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
/datum/frame/frame_types/dna_analyzer
name = "DNA Analyzer"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
/datum/frame/frame_types/mass_driver
name = "Mass Driver"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
circuit = /obj/item/weapon/circuitboard/mass_driver
/datum/frame/frame_types/holopad
name = "Holopad"
- frame_class = "computer"
+ frame_class = FRAME_CLASS_COMPUTER
frame_size = 4
/datum/frame/frame_types/microwave
name = "Microwave"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
frame_size = 4
/datum/frame/frame_types/fax
name = "Fax"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
frame_size = 3
/datum/frame/frame_types/recharger
name = "Recharger"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
circuit = /obj/item/weapon/circuitboard/recharger
frame_size = 3
/datum/frame/frame_types/grinder
name = "Grinder"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
circuit = /obj/item/weapon/circuitboard/grinder
frame_size = 3
/datum/frame/frame_types/display
name = "Display"
- frame_class = "display"
- frame_style = "wall"
+ frame_class = FRAME_CLASS_DISPLAY
+ frame_style = FRAME_STYLE_WALL
x_offset = 32
y_offset = 32
/datum/frame/frame_types/supply_request_console
name = "Supply Request Console"
- frame_class = "display"
- frame_style = "wall"
+ frame_class = FRAME_CLASS_DISPLAY
+ frame_style = FRAME_STYLE_WALL
x_offset = 32
y_offset = 32
/datum/frame/frame_types/atm
name = "ATM"
- frame_class = "display"
+ frame_class = FRAME_CLASS_DISPLAY
frame_size = 3
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 32
y_offset = 32
/datum/frame/frame_types/newscaster
name = "Newscaster"
- frame_class = "display"
+ frame_class = FRAME_CLASS_DISPLAY
frame_size = 3
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 28
y_offset = 30
/datum/frame/frame_types/wall_charger
name = "Wall Charger"
- frame_class = "machine"
+ frame_class = FRAME_CLASS_MACHINE
circuit = /obj/item/weapon/circuitboard/recharger/wrecharger
frame_size = 3
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 32
y_offset = 32
/datum/frame/frame_types/fire_alarm
name = "Fire Alarm"
- frame_class = "alarm"
+ frame_class = FRAME_CLASS_ALARM
frame_size = 2
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 24
y_offset = 24
/datum/frame/frame_types/air_alarm
name = "Air Alarm"
- frame_class = "alarm"
+ frame_class = FRAME_CLASS_ALARM
frame_size = 2
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 24
y_offset = 24
/datum/frame/frame_types/guest_pass_console
name = "Guest Pass Console"
- frame_class = "display"
+ frame_class = FRAME_CLASS_DISPLAY
frame_size = 2
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 30
y_offset = 30
/datum/frame/frame_types/intercom
name = "Intercom"
- frame_class = "alarm"
+ frame_class = FRAME_CLASS_ALARM
frame_size = 2
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 28
y_offset = 28
/datum/frame/frame_types/keycard_authenticator
name = "Keycard Authenticator"
- frame_class = "alarm"
+ frame_class = FRAME_CLASS_ALARM
frame_size = 1
- frame_style = "wall"
+ frame_style = FRAME_STYLE_WALL
x_offset = 24
y_offset = 24
/datum/frame/frame_types/cancel //used to get out of input dialogue
name = "Cancel"
+//////////////////////////////
+// Frame Object (Structure)
+//////////////////////////////
+
/obj/structure/frame
anchored = 0
name = "frame"
icon = 'icons/obj/stock_parts.dmi'
icon_state = "machine_0"
- var/state = 0
+ var/state = FRAME_PLACED
var/obj/item/weapon/circuitboard/circuit = null
var/need_circuit = 1
var/datum/frame/frame_types/frame_type = new /datum/frame/frame_types/machine
@@ -198,6 +206,11 @@
anchored = 1
density = 1
+/obj/structure/frame/examine(mob/user)
+ ..()
+ if(circuit)
+ to_chat(user, "It has \a [circuit] installed.")
+
/obj/structure/frame/proc/update_desc()
var/D
if(req_components)
@@ -228,7 +241,7 @@
..()
if(building)
frame_type = type
- state = 0
+ state = FRAME_PLACED
if(dir)
set_dir(dir)
@@ -237,10 +250,10 @@
src.loc = loc
if(frame_type.x_offset)
- pixel_x = (dir & 3)? 0 : (dir == 4 ? -frame_type.x_offset : frame_type.x_offset)
+ pixel_x = (dir & 3)? 0 : (dir == EAST ? -frame_type.x_offset : frame_type.x_offset)
if(frame_type.y_offset)
- pixel_y = (dir & 3)? (dir == 1 ? -frame_type.y_offset : frame_type.y_offset) : 0
+ pixel_y = (dir & 3)? (dir == NORTH ? -frame_type.y_offset : frame_type.y_offset) : 0
if(frame_type.circuit)
need_circuit = 0
@@ -249,34 +262,34 @@
if(frame_type.name == "Computer")
density = 1
- if(frame_type.frame_class == "machine")
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
density = 1
update_icon()
/obj/structure/frame/attackby(obj/item/P as obj, mob/user as mob)
if(istype(P, /obj/item/weapon/wrench))
- if(state == 0 && !anchored)
+ if(state == FRAME_PLACED && !anchored)
user << "You start to wrench the frame into place."
playsound(src.loc, P.usesound, 50, 1)
if(do_after(user, 20 * P.toolspeed))
anchored = 1
if(!need_circuit && circuit)
- state = 2
+ state = FRAME_FASTENED
check_components()
update_desc()
user << "You wrench the frame into place and set the outer cover."
else
user << "You wrench the frame into place."
- else if(state == 0 && anchored)
+ else if(state == FRAME_PLACED && anchored)
playsound(src, P.usesound, 50, 1)
if(do_after(user, 20 * P.toolspeed))
user << "You unfasten the frame."
anchored = 0
else if(istype(P, /obj/item/weapon/weldingtool))
- if(state == 0)
+ if(state == FRAME_PLACED)
var/obj/item/weapon/weldingtool/WT = P
if(WT.remove_fuel(0, user))
playsound(src.loc, P.usesound, 50, 1)
@@ -291,7 +304,7 @@
return
else if(istype(P, /obj/item/weapon/circuitboard) && need_circuit && !circuit)
- if(state == 0 && anchored)
+ if(state == FRAME_PLACED && anchored)
var/obj/item/weapon/circuitboard/B = P
var/datum/frame/frame_types/board_type = B.board_type
if(board_type.name == frame_type.name)
@@ -300,8 +313,8 @@
circuit = P
user.drop_item()
P.loc = src
- state = 1
- if(frame_type.frame_class == "machine")
+ state = FRAME_UNFASTENED
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
check_components()
update_desc()
else
@@ -309,25 +322,25 @@
return
else if(istype(P, /obj/item/weapon/screwdriver))
- if(state == 1)
+ if(state == FRAME_UNFASTENED)
if(need_circuit && circuit)
playsound(src, P.usesound, 50, 1)
user << "You screw the circuit board into place."
- state = 2
+ state = FRAME_FASTENED
- else if(state == 2)
+ else if(state == FRAME_FASTENED)
if(need_circuit && circuit)
playsound(src, P.usesound, 50, 1)
user << "You unfasten the circuit board."
- state = 1
+ state = FRAME_UNFASTENED
else if(!need_circuit && circuit)
playsound(src, P.usesound, 50, 1)
user << "You unfasten the outer cover."
- state = 0
+ state = FRAME_PLACED
- else if(state == 3)
- if(frame_type.frame_class == "machine")
+ else if(state == FRAME_WIRED)
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
var/component_check = 1
for(var/R in req_components)
if(req_components[R] > 0)
@@ -363,7 +376,7 @@
qdel(src)
return
- else if(frame_type.frame_class == "alarm")
+ else if(frame_type.frame_class == FRAME_CLASS_ALARM)
playsound(src, P.usesound, 50, 1)
user << "You fasten the cover."
var/obj/machinery/B = new circuit.build_path(src.loc)
@@ -376,8 +389,8 @@
qdel(src)
return
- else if(state == 4)
- if(frame_type.frame_class == "computer")
+ else if(state == FRAME_PANELED)
+ if(frame_type.frame_class == FRAME_CLASS_COMPUTER)
playsound(src, P.usesound, 50, 1)
user << "You connect the monitor."
var/obj/machinery/B = new circuit.build_path(src.loc)
@@ -390,7 +403,7 @@
qdel(src)
return
- else if(frame_type.frame_class == "display")
+ else if(frame_type.frame_class == FRAME_CLASS_DISPLAY)
playsound(src, P.usesound, 50, 1)
user << "You connect the monitor."
var/obj/machinery/B = new circuit.build_path(src.loc)
@@ -404,18 +417,18 @@
return
else if(istype(P, /obj/item/weapon/crowbar))
- if(state == 1)
+ if(state == FRAME_UNFASTENED)
if(need_circuit && circuit)
playsound(src, P.usesound, 50, 1)
user << "You remove the circuit board."
- state = 0
+ state = FRAME_PLACED
circuit.forceMove(src.loc)
circuit = null
- if(frame_type.frame_class == "machine")
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
req_components = null
- else if(state == 3)
- if(frame_type.frame_class == "machine")
+ else if(state == FRAME_WIRED)
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
playsound(src, P.usesound, 50, 1)
if(components.len == 0)
user << "There are no components to remove."
@@ -427,35 +440,35 @@
update_desc()
user << desc
- else if(state == 4)
- if(frame_type.frame_class == "computer")
+ else if(state == FRAME_PANELED)
+ if(frame_type.frame_class == FRAME_CLASS_COMPUTER)
playsound(src, P.usesound, 50, 1)
user << "You remove the glass panel."
- state = 3
+ state = FRAME_WIRED
new /obj/item/stack/material/glass(src.loc, 2)
- else if(frame_type.frame_class == "display")
+ else if(frame_type.frame_class == FRAME_CLASS_DISPLAY)
playsound(src, P.usesound, 50, 1)
user << "You remove the glass panel."
- state = 3
+ state = FRAME_WIRED
new /obj/item/stack/material/glass(src.loc, 2)
else if(istype(P, /obj/item/stack/cable_coil))
- if(state == 2)
+ if(state == FRAME_FASTENED)
var/obj/item/stack/cable_coil/C = P
if(C.get_amount() < 5)
user << "You need five coils of wire to add them to the frame."
return
user << "You start to add cables to the frame."
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
- if(do_after(user, 20) && state == 2)
+ if(do_after(user, 20) && state == FRAME_FASTENED)
if(C.use(5))
user << "You add cables to the frame."
- state = 3
- if(frame_type.frame_class == "machine")
+ state = FRAME_WIRED
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
user << desc
- else if(state == 3)
- if(frame_type.frame_class == "machine")
+ else if(state == FRAME_WIRED)
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
for(var/I in req_components)
if(istype(P, I) && (req_components[I] > 0))
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
@@ -481,60 +494,60 @@
user << desc
else if(istype(P, /obj/item/weapon/wirecutters))
- if(state == 3)
- if(frame_type.frame_class == "computer")
+ if(state == FRAME_WIRED)
+ if(frame_type.frame_class == FRAME_CLASS_COMPUTER)
playsound(src, P.usesound, 50, 1)
user << "You remove the cables."
- state = 2
+ state = FRAME_FASTENED
new /obj/item/stack/cable_coil(src.loc, 5)
- else if(frame_type.frame_class == "display")
+ else if(frame_type.frame_class == FRAME_CLASS_DISPLAY)
playsound(src, P.usesound, 50, 1)
user << "You remove the cables."
- state = 2
+ state = FRAME_FASTENED
new /obj/item/stack/cable_coil(src.loc, 5)
- else if(frame_type.frame_class == "alarm")
+ else if(frame_type.frame_class == FRAME_CLASS_ALARM)
playsound(src, P.usesound, 50, 1)
user << "You remove the cables."
- state = 2
+ state = FRAME_FASTENED
new /obj/item/stack/cable_coil(src.loc, 5)
- else if(frame_type.frame_class == "machine")
+ else if(frame_type.frame_class == FRAME_CLASS_MACHINE)
playsound(src, P.usesound, 50, 1)
user << "You remove the cables."
- state = 2
+ state = FRAME_FASTENED
new /obj/item/stack/cable_coil(src.loc, 5)
else if(istype(P, /obj/item/stack/material) && P.get_material_name() == "glass")
- if(state == 3)
- if(frame_type.frame_class == "computer")
+ if(state == FRAME_WIRED)
+ if(frame_type.frame_class == FRAME_CLASS_COMPUTER)
var/obj/item/stack/G = P
if(G.get_amount() < 2)
user << "You need two sheets of glass to put in the glass panel."
return
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
user << "You start to put in the glass panel."
- if(do_after(user, 20) && state == 3)
+ if(do_after(user, 20) && state == FRAME_WIRED)
if(G.use(2))
user << "You put in the glass panel."
- state = 4
+ state = FRAME_PANELED
- else if(frame_type.frame_class == "display")
+ else if(frame_type.frame_class == FRAME_CLASS_DISPLAY)
var/obj/item/stack/G = P
if(G.get_amount() < 2)
user << "You need two sheets of glass to put in the glass panel."
return
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
user << "You start to put in the glass panel."
- if(do_after(user, 20) && state == 3)
+ if(do_after(user, 20) && state == FRAME_WIRED)
if(G.use(2))
user << "You put in the glass panel."
- state = 4
+ state = FRAME_PANELED
else if(istype(P, /obj/item))
- if(state == 3)
- if(frame_type.frame_class == "machine")
+ if(state == FRAME_WIRED)
+ if(frame_type.frame_class == FRAME_CLASS_MACHINE)
for(var/I in req_components)
if(istype(P, I) && (req_components[I] > 0))
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
@@ -578,17 +591,7 @@
set_dir(turn(dir, 90))
- var/dir_text
- if(dir == 1)
- dir_text = "north"
- else if(dir == 2)
- dir_text = "south"
- else if(dir == 4)
- dir_text = "east"
- else if(dir == 8)
- dir_text = "west"
-
- usr << "You rotate the [src] to face [dir_text]!"
+ usr << "You rotate the [src] to face [dir2text(dir)]!"
return
@@ -607,16 +610,6 @@
set_dir(turn(dir, 270))
- var/dir_text
- if(dir == 1)
- dir_text = "north"
- else if(dir == 2)
- dir_text = "south"
- else if(dir == 4)
- dir_text = "east"
- else if(dir == 8)
- dir_text = "west"
-
- usr << "You rotate the [src] to face [dir_text]!"
+ usr << "You rotate the [src] to face [dir2text(dir)]!"
return
\ No newline at end of file
diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm
index 7e465db189..f438f7d003 100644
--- a/code/game/machinery/holosign.dm
+++ b/code/game/machinery/holosign.dm
@@ -36,6 +36,11 @@
name = "surgery holosign"
desc = "Small wall-mounted holographic projector. This one reads SURGERY."
on_icon = "surgery"
+
+/obj/machinery/holosign/exit
+ name = "exit holosign"
+ desc = "Small wall-mounted holographic projector. This one reads EXIT."
+ on_icon = "exit"
////////////////////SWITCH///////////////////////////////////////
/obj/machinery/button/holosign
diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm
index 94fb9913b2..8f77ac90d9 100644
--- a/code/game/machinery/lightswitch.dm
+++ b/code/game/machinery/lightswitch.dm
@@ -31,7 +31,8 @@
/obj/machinery/light_switch/proc/updateicon()
if(!overlay)
- overlay = image(icon, "light1-overlay", LIGHTING_LAYER+0.1)
+ overlay = image(icon, "light1-overlay")
+ overlay.plane = PLANE_LIGHTING_ABOVE
overlays.Cut()
if(stat & NOPOWER)
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index f476c295e2..2e881f6583 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -118,16 +118,12 @@ Class Procs:
..(l)
if(d)
set_dir(d)
- if(!machinery_sort_required && ticker)
- dd_insertObjectList(machines, src)
- else
- machines += src
- machinery_sort_required = 1
+ START_MACHINE_PROCESSING(src)
if(circuit)
circuit = new circuit(src)
/obj/machinery/Destroy()
- machines -= src
+ STOP_MACHINE_PROCESSING(src)
if(component_parts)
for(var/atom/A in component_parts)
if(A.loc == src) // If the components are inside the machine, delete them.
@@ -321,6 +317,27 @@ Class Procs:
user << " [C.name]"
return 1
+// Default behavior for wrenching down machines. Supports both delay and instant modes.
+/obj/machinery/proc/default_unfasten_wrench(var/mob/user, var/obj/item/weapon/wrench/W, var/time = 0)
+ if(!istype(W))
+ return FALSE
+ if(panel_open)
+ return FALSE // Close panel first!
+ playsound(loc, W.usesound, 50, 1)
+ var/actual_time = W.toolspeed * time
+ if(actual_time != 0)
+ user.visible_message( \
+ "\The [user] begins [anchored ? "un" : ""]securing \the [src].", \
+ "You start [anchored ? "un" : ""]securing \the [src].")
+ if(actual_time == 0 || do_after(user, actual_time, target = src))
+ user.visible_message( \
+ "\The [user] has [anchored ? "un" : ""]secured \the [src].", \
+ "You [anchored ? "un" : ""]secure \the [src].")
+ anchored = !anchored
+ power_change() //Turn on or off the machine depending on the status of power in the new area.
+ update_icon()
+ return TRUE
+
/obj/machinery/proc/default_deconstruction_crowbar(var/mob/user, var/obj/item/weapon/crowbar/C)
if(!istype(C))
return 0
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index e9f693e431..ec0f8a027c 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -491,16 +491,16 @@ Buildable meters
P.initialize_directions = pipe_dir
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_SUPPLY_STRAIGHT, PIPE_SUPPLY_BENT)
@@ -510,16 +510,16 @@ Buildable meters
P.initialize_directions = pipe_dir
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_SCRUBBERS_STRAIGHT, PIPE_SCRUBBERS_BENT)
@@ -529,16 +529,16 @@ Buildable meters
P.initialize_directions = pipe_dir
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_UNIVERSAL)
@@ -548,16 +548,16 @@ Buildable meters
P.initialize_directions = pipe_dir
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_HE_STRAIGHT, PIPE_HE_BENT)
@@ -565,16 +565,16 @@ Buildable meters
P.set_dir(src.dir)
P.initialize_directions = pipe_dir //this var it's used to know if the pipe is bent or not
P.initialize_directions_he = pipe_dir
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_CONNECTOR) // connector
@@ -585,10 +585,10 @@ Buildable meters
C.name = pipename
var/turf/T = C.loc
C.level = !T.is_plating() ? 2 : 1
- C.initialize()
+ C.atmos_init()
C.build_network()
if (C.node)
- C.node.initialize()
+ C.node.atmos_init()
C.node.build_network()
@@ -600,19 +600,19 @@ Buildable meters
//M.New()
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
- M.initialize()
+ M.atmos_init()
if (QDELETED(M))
usr << pipefailtext
return 1
M.build_network()
if (M.node1)
- M.node1.initialize()
+ M.node1.atmos_init()
M.node1.build_network()
if (M.node2)
- M.node2.initialize()
+ M.node2.atmos_init()
M.node2.build_network()
if (M.node3)
- M.node3.initialize()
+ M.node3.atmos_init()
M.node3.build_network()
if(PIPE_SUPPLY_MANIFOLD) //manifold
@@ -623,19 +623,19 @@ Buildable meters
//M.New()
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
- M.initialize()
+ M.atmos_init()
if (!M)
usr << "There's nothing to connect this manifold to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
M.build_network()
if (M.node1)
- M.node1.initialize()
+ M.node1.atmos_init()
M.node1.build_network()
if (M.node2)
- M.node2.initialize()
+ M.node2.atmos_init()
M.node2.build_network()
if (M.node3)
- M.node3.initialize()
+ M.node3.atmos_init()
M.node3.build_network()
if(PIPE_SCRUBBERS_MANIFOLD) //manifold
@@ -646,19 +646,20 @@ Buildable meters
//M.New()
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
- M.initialize()
+ M.atmos_init()
if (!M)
usr << "There's nothing to connect this manifold to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
M.build_network()
if (M.node1)
- M.node1.initialize()
+ M.node1.atmos_init()
M.node1.build_network()
if (M.node2)
- M.node2.initialize()
+ M.node2.atmos_init()
M.node2.build_network()
if (M.node3)
- M.node3.initialize()
+ M.node3.atmos_init()
+ M.node3.build_network()
M.node3.build_network()
if(PIPE_MANIFOLD4W) //4-way manifold
@@ -669,22 +670,22 @@ Buildable meters
//M.New()
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
- M.initialize()
+ M.atmos_init()
if (QDELETED(M))
usr << pipefailtext
return 1
M.build_network()
if (M.node1)
- M.node1.initialize()
+ M.node1.atmos_init()
M.node1.build_network()
if (M.node2)
- M.node2.initialize()
+ M.node2.atmos_init()
M.node2.build_network()
if (M.node3)
- M.node3.initialize()
+ M.node3.atmos_init()
M.node3.build_network()
if (M.node4)
- M.node4.initialize()
+ M.node4.atmos_init()
M.node4.build_network()
if(PIPE_SUPPLY_MANIFOLD4W) //4-way manifold
@@ -696,22 +697,22 @@ Buildable meters
//M.New()
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
- M.initialize()
+ M.atmos_init()
if (!M)
usr << "There's nothing to connect this manifold to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
M.build_network()
if (M.node1)
- M.node1.initialize()
+ M.node1.atmos_init()
M.node1.build_network()
if (M.node2)
- M.node2.initialize()
+ M.node2.atmos_init()
M.node2.build_network()
if (M.node3)
- M.node3.initialize()
+ M.node3.atmos_init()
M.node3.build_network()
if (M.node4)
- M.node4.initialize()
+ M.node4.atmos_init()
M.node4.build_network()
if(PIPE_SCRUBBERS_MANIFOLD4W) //4-way manifold
@@ -723,22 +724,22 @@ Buildable meters
//M.New()
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
- M.initialize()
+ M.atmos_init()
if (!M)
usr << "There's nothing to connect this manifold to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
M.build_network()
if (M.node1)
- M.node1.initialize()
+ M.node1.atmos_init()
M.node1.build_network()
if (M.node2)
- M.node2.initialize()
+ M.node2.atmos_init()
M.node2.build_network()
if (M.node3)
- M.node3.initialize()
+ M.node3.atmos_init()
M.node3.build_network()
if (M.node4)
- M.node4.initialize()
+ M.node4.atmos_init()
M.node4.build_network()
if(PIPE_JUNCTION)
@@ -746,16 +747,16 @@ Buildable meters
P.set_dir(src.dir)
P.initialize_directions = src.get_pdir()
P.initialize_directions_he = src.get_hdir()
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext //"There's nothing to connect this pipe to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_UVENT) //unary vent
@@ -766,10 +767,10 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node)
- V.node.initialize()
+ V.node.atmos_init()
V.node.build_network()
if(PIPE_MVALVE) //manual valve
@@ -780,15 +781,15 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node1)
// world << "[V.node1.name] is connected to valve, forcing it to update its nodes."
- V.node1.initialize()
+ V.node1.atmos_init()
V.node1.build_network()
if (V.node2)
// world << "[V.node2.name] is connected to valve, forcing it to update its nodes."
- V.node2.initialize()
+ V.node2.atmos_init()
V.node2.build_network()
if(PIPE_PUMP) //gas pump
@@ -799,13 +800,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_GAS_FILTER) //gas filter
@@ -816,16 +817,16 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if (P.node3)
- P.node3.initialize()
+ P.node3.atmos_init()
P.node3.build_network()
if(PIPE_GAS_MIXER) //gas mixer
@@ -836,16 +837,16 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if (P.node3)
- P.node3.initialize()
+ P.node3.atmos_init()
P.node3.build_network()
if(PIPE_GAS_FILTER_M) //gas filter mirrored
@@ -856,16 +857,16 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if (P.node3)
- P.node3.initialize()
+ P.node3.atmos_init()
P.node3.build_network()
if(PIPE_GAS_MIXER_T) //gas mixer-t
@@ -876,16 +877,16 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if (P.node3)
- P.node3.initialize()
+ P.node3.atmos_init()
P.node3.build_network()
if(PIPE_GAS_MIXER_M) //gas mixer mirrored
@@ -896,16 +897,16 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if (P.node3)
- P.node3.initialize()
+ P.node3.atmos_init()
P.node3.build_network()
if(PIPE_SCRUBBER) //scrubber
@@ -916,10 +917,10 @@ Buildable meters
S.name = pipename
var/turf/T = S.loc
S.level = !T.is_plating() ? 2 : 1
- S.initialize()
+ S.atmos_init()
S.build_network()
if (S.node)
- S.node.initialize()
+ S.node.atmos_init()
S.node.build_network()
if(PIPE_INSULATED_STRAIGHT, PIPE_INSULATED_BENT)
@@ -928,16 +929,16 @@ Buildable meters
P.initialize_directions = pipe_dir
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_MTVALVE) //manual t-valve
@@ -948,16 +949,16 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node1)
- V.node1.initialize()
+ V.node1.atmos_init()
V.node1.build_network()
if (V.node2)
- V.node2.initialize()
+ V.node2.atmos_init()
V.node2.build_network()
if (V.node3)
- V.node3.initialize()
+ V.node3.atmos_init()
V.node3.build_network()
if(PIPE_MTVALVEM) //manual t-valve
@@ -968,46 +969,46 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node1)
- V.node1.initialize()
+ V.node1.atmos_init()
V.node1.build_network()
if (V.node2)
- V.node2.initialize()
+ V.node2.atmos_init()
V.node2.build_network()
if (V.node3)
- V.node3.initialize()
+ V.node3.atmos_init()
V.node3.build_network()
if(PIPE_CAP)
var/obj/machinery/atmospherics/pipe/cap/C = new(src.loc)
C.set_dir(dir)
C.initialize_directions = pipe_dir
- C.initialize()
+ C.atmos_init()
C.build_network()
if(C.node)
- C.node.initialize()
+ C.node.atmos_init()
C.node.build_network()
if(PIPE_SUPPLY_CAP)
var/obj/machinery/atmospherics/pipe/cap/hidden/supply/C = new(src.loc)
C.set_dir(dir)
C.initialize_directions = pipe_dir
- C.initialize()
+ C.atmos_init()
C.build_network()
if(C.node)
- C.node.initialize()
+ C.node.atmos_init()
C.node.build_network()
if(PIPE_SCRUBBERS_CAP)
var/obj/machinery/atmospherics/pipe/cap/hidden/scrubbers/C = new(src.loc)
C.set_dir(dir)
C.initialize_directions = pipe_dir
- C.initialize()
+ C.atmos_init()
C.build_network()
if(C.node)
- C.node.initialize()
+ C.node.atmos_init()
C.node.build_network()
if(PIPE_PASSIVE_GATE) //passive gate
@@ -1018,13 +1019,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_VOLUME_PUMP) //volume pump
@@ -1035,13 +1036,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_HEAT_EXCHANGE) // heat exchanger
@@ -1052,10 +1053,10 @@ Buildable meters
C.name = pipename
var/turf/T = C.loc
C.level = !T.is_plating() ? 2 : 1
- C.initialize()
+ C.atmos_init()
C.build_network()
if (C.node)
- C.node.initialize()
+ C.node.atmos_init()
C.node.build_network()
if(PIPE_DVALVE) //digital valve
@@ -1070,13 +1071,13 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node1)
- V.node1.initialize()
+ V.node1.atmos_init()
V.node1.build_network()
if (V.node2)
- V.node2.initialize()
+ V.node2.atmos_init()
V.node2.build_network()
if(PIPE_DTVALVE) //digital t-valve
@@ -1091,16 +1092,16 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node1)
- V.node1.initialize()
+ V.node1.atmos_init()
V.node1.build_network()
if (V.node2)
- V.node2.initialize()
+ V.node2.atmos_init()
V.node2.build_network()
if (V.node3)
- V.node3.initialize()
+ V.node3.atmos_init()
V.node3.build_network()
if(PIPE_DTVALVEM) //mirrored digital t-valve
@@ -1115,16 +1116,16 @@ Buildable meters
V.name = pipename
var/turf/T = V.loc
V.level = !T.is_plating() ? 2 : 1
- V.initialize()
+ V.atmos_init()
V.build_network()
if (V.node1)
- V.node1.initialize()
+ V.node1.atmos_init()
V.node1.build_network()
if (V.node2)
- V.node2.initialize()
+ V.node2.atmos_init()
V.node2.build_network()
if (V.node3)
- V.node3.initialize()
+ V.node3.atmos_init()
V.node3.build_network()
///// Z-Level stuff
@@ -1136,13 +1137,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_DOWN)
var/obj/machinery/atmospherics/pipe/zpipe/down/P = new(src.loc)
@@ -1152,13 +1153,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_SUPPLY_UP)
var/obj/machinery/atmospherics/pipe/zpipe/up/supply/P = new(src.loc)
@@ -1168,13 +1169,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_SUPPLY_DOWN)
var/obj/machinery/atmospherics/pipe/zpipe/down/supply/P = new(src.loc)
@@ -1184,13 +1185,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_SCRUBBERS_UP)
var/obj/machinery/atmospherics/pipe/zpipe/up/scrubbers/P = new(src.loc)
@@ -1200,13 +1201,13 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
if(PIPE_SCRUBBERS_DOWN)
var/obj/machinery/atmospherics/pipe/zpipe/down/scrubbers/P = new(src.loc)
@@ -1216,26 +1217,26 @@ Buildable meters
P.name = pipename
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
if (P.node2)
- P.node2.initialize()
+ P.node2.atmos_init()
P.node2.build_network()
///// Z-Level stuff
if(PIPE_OMNI_MIXER)
var/obj/machinery/atmospherics/omni/mixer/P = new(loc)
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if(PIPE_OMNI_FILTER)
var/obj/machinery/atmospherics/omni/atmos_filter/P = new(loc)
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if(PIPE_PASSIVE_VENT)
var/obj/machinery/atmospherics/pipe/vent/P = new(loc)
@@ -1243,10 +1244,10 @@ Buildable meters
P.initialize_directions = pipe_dir
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
- P.initialize()
+ P.atmos_init()
P.build_network()
if (P.node1)
- P.node1.initialize()
+ P.node1.atmos_init()
P.node1.build_network()
playsound(src, W.usesound, 50, 1)
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 1b181aebb5..840cd5d24c 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -81,11 +81,28 @@
/obj/machinery/porta_turret/ai_defense
name = "defense turret"
- desc = "This varient appears to be much more durable."
+ desc = "This variant appears to be much more durable."
installation = /obj/item/weapon/gun/energy/xray // For the armor pen.
health = 250 // Since lasers do 40 each.
maxhealth = 250
+/obj/machinery/porta_turret/alien // The kind used on the UFO submap.
+ name = "interior anti-boarding turret"
+ desc = "A very tough looking turret made by alien hands."
+ installation = /obj/item/weapon/gun/energy/alien
+ enabled = TRUE
+ lethal = TRUE
+ ailock = TRUE
+ check_all = TRUE
+ health = 250 // Similar to the AI turrets.
+ maxhealth = 250
+
+/obj/machinery/porta_turret/alien/destroyed // Turrets that are already dead, to act as a warning of what the rest of the submap contains.
+ name = "broken interior anti-boarding turret"
+ desc = "A very tough looking turret made by alien hands. This one looks destroyed, thankfully."
+ icon_state = "destroyed_target_prism"
+ stat = BROKEN
+
/obj/machinery/porta_turret/New()
..()
req_access.Cut()
@@ -103,6 +120,11 @@
req_one_access.Cut()
req_access = list(access_cent_specops)
+/obj/machinery/porta_turret/alien/New()
+ ..()
+ req_one_access.Cut()
+ req_access = list(access_alien)
+
/obj/machinery/porta_turret/Destroy()
qdel(spark_system)
spark_system = null
@@ -359,7 +381,7 @@ var/list/turret_icons
else
//if the turret was attacked with the intention of harming it:
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(I))
take_damage(I.force * 0.5)
if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off
if(!attacked && !emagged)
@@ -436,6 +458,14 @@ var/list/turret_icons
return
..()
+/obj/machinery/porta_turret/alien/emp_act(severity) // This is overrided to give an EMP resistance as well as avoid scambling the turret settings.
+ if(prob(75)) // Superior alien technology, I guess.
+ return
+ enabled = FALSE
+ spawn(rand(1 MINUTE, 2 MINUTES))
+ if(!enabled)
+ enabled = TRUE
+
/obj/machinery/porta_turret/ex_act(severity)
switch (severity)
if(1)
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 87077b68db..34e89ac6fd 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -103,13 +103,6 @@
R.adjustFireLoss(-wire_rate)
else if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
- if(!isnull(H.internal_organs_by_name["cell"]) && H.nutrition < 450)
- H.nutrition = min(H.nutrition+10, 450)
- cell.use(7000/450*10)
-
- else if(istype(occupant, /mob/living/carbon/human))
-
- var/mob/living/carbon/human/H = occupant
// In case they somehow end up with positive values for otherwise unobtainable damage...
if(H.getToxLoss()>0) H.adjustToxLoss(-(rand(1,3)))
@@ -153,9 +146,21 @@
return
if(default_part_replacement(user, O))
return
+ if (istype(O, /obj/item/weapon/grab) && get_dist(src,user)<2)
+ var/obj/item/weapon/grab/G = O
+ if(istype(G.affecting,/mob/living))
+ var/mob/living/M = G.affecting
+ qdel(O)
+ go_in(M)
..()
+/obj/machinery/recharge_station/MouseDrop_T(var/mob/target, var/mob/user)
+ if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user))
+ return
+
+ go_in(target)
+
/obj/machinery/recharge_station/RefreshParts()
..()
var/man_rating = 0
@@ -214,15 +219,16 @@
if(icon_update_tick == 0)
build_overlays()
-/obj/machinery/recharge_station/Bumped(var/mob/living/silicon/robot/R)
- go_in(R)
+/obj/machinery/recharge_station/Bumped(var/mob/living/L)
+ go_in(L)
-/obj/machinery/recharge_station/proc/go_in(var/mob/living/silicon/robot/R)
+/obj/machinery/recharge_station/proc/go_in(var/mob/living/L)
if(occupant)
return
- if(istype(R, /mob/living/silicon/robot))
+ if(istype(L, /mob/living/silicon/robot))
+ var/mob/living/silicon/robot/R = L
if(R.incapacitated())
return
@@ -237,8 +243,8 @@
update_icon()
return 1
- else if(istype(R, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = R
+ else if(istype(L, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = L
if(!isnull(H.internal_organs_by_name["cell"]))
add_fingerprint(H)
H.reset_view(src)
diff --git a/code/game/machinery/records_scanner.dm b/code/game/machinery/records_scanner.dm
index f9ed05c7e9..e8fdc17f0e 100644
--- a/code/game/machinery/records_scanner.dm
+++ b/code/game/machinery/records_scanner.dm
@@ -1,6 +1,6 @@
//not a computer
obj/machinery/scanner
- name = "Identity Analyser"
+ name = "identity analyzer"
var/outputdir = 0
icon = 'icons/obj/stationobjs.dmi'
icon_state = "scanner_idle"
@@ -70,7 +70,7 @@ obj/machinery/scanner/attack_hand(mob/living/carbon/human/user)
Black Marks:
"}
for(var/A in marks)
text += "[A]
"
- user << "You feel a sting as the scanner extracts some of your blood."
+ to_chat(user, "You feel a sting as the scanner extracts some of your blood.")
var/turf/T = get_step(src,outputdir)
var/obj/item/weapon/paper/print = new(T)
print.name = "[mname] Report"
@@ -136,4 +136,4 @@ obj/machinery/scanner/attack_hand(mob/living/carbon/human/user)
data_core.general += G
data_core.medical += M
data_core.security += S
- data_core.locked += L
\ No newline at end of file
+ data_core.locked += L
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index dd07ac5b29..45e1a7315d 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -23,7 +23,7 @@ var/req_console_information = list()
var/list/obj/machinery/requests_console/allConsoles = list()
/obj/machinery/requests_console
- name = "Requests Console"
+ name = "requests console"
desc = "A console intended to send requests to different departments on the station."
anchored = 1
icon = 'icons/obj/terminals.dmi'
@@ -72,7 +72,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
announcement.title = "[department] announcement"
announcement.newscast = 1
- name = "[department] Requests Console"
+ name = "[department] requests console"
allConsoles += src
if(departmentType & RC_ASSIST)
req_console_assistance |= department
@@ -208,15 +208,15 @@ var/list/obj/machinery/requests_console/allConsoles = list()
updateUsrDialog()
return
- //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messanging on that console (EXTREME priority...), but the code for that was removed.
+ //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messaging on that console (EXTREME priority...), but the code for that was removed.
/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob)
if(computer_deconstruction_screwdriver(user, O))
return
if(istype(O, /obj/item/device/multitool))
if(panel_open)
- var/input = sanitize(input(usr, "What Department id would you like to give this Request Console?", "Multitool-Request Console interface", department))
+ var/input = sanitize(input(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department))
if(!input)
- usr << "No input found please hang up and try your call again."
+ to_chat(usr, "No input found. Please hang up and try your call again.")
return
department = input
announcement.title = "[department] announcement"
diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm
index 59f364d948..f77982843b 100644
--- a/code/game/machinery/robot_fabricator.dm
+++ b/code/game/machinery/robot_fabricator.dm
@@ -1,5 +1,5 @@
/obj/machinery/robotic_fabricator
- name = "Robotic Fabricator"
+ name = "robotic fabricator"
icon = 'icons/obj/robotics.dmi'
icon_state = "fab-idle"
density = 1
@@ -30,7 +30,7 @@
overlays -= "fab-load-metal"
updateDialog()
else
- user << "The robot part maker is full. Please remove metal from the robot part maker in order to insert more."
+ to_chat(user, "The robot part maker is full. Please remove metal from the robot part maker in order to insert more.")
/obj/machinery/robotic_fabricator/attack_hand(user as mob)
var/dat
@@ -135,4 +135,4 @@ Please wait until completion...
for (var/mob/M in viewers(1, src))
if(M.client && M.machine == src)
- attack_hand(M)
\ No newline at end of file
+ attack_hand(M)
diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm
index 3441b09cbb..0f58bd9a48 100644
--- a/code/game/machinery/supplybeacon.dm
+++ b/code/game/machinery/supplybeacon.dm
@@ -47,7 +47,7 @@
/obj/machinery/power/supply_beacon/attackby(var/obj/item/weapon/W, var/mob/user)
if(!use_power && istype(W, /obj/item/weapon/wrench))
if(!anchored && !connect_to_network())
- user << "This device must be placed over an exposed cable."
+ to_chat(user, "This device must be placed over an exposed cable.")
return
anchored = !anchored
user.visible_message("\The [user] [anchored ? "secures" : "unsecures"] \the [src].")
@@ -59,13 +59,13 @@
if(expended)
use_power = 0
- user << "\The [src] has used up its charge."
+ to_chat (user, "\The [src] has used up its charge.")
return
if(anchored)
return use_power ? deactivate(user) : activate(user)
else
- user << "You need to secure the beacon with a wrench first!"
+ to_chat(user, "You need to secure the beacon with a wrench first!")
return
/obj/machinery/power/supply_beacon/attack_ai(var/mob/user)
@@ -76,12 +76,12 @@
if(expended)
return
if(surplus() < 500)
- if(user) user << "The connected wire doesn't have enough current."
+ if(user) to_chat(user, "The connected wire doesn't have enough current.")
return
set_light(3, 3, "#00CCAA")
icon_state = "beacon_active"
use_power = 1
- if(user) user << "You activate the beacon. The supply drop will be dispatched soon."
+ if(user) to_chat(user, "You activate the beacon. The supply drop will be dispatched soon.")
/obj/machinery/power/supply_beacon/proc/deactivate(var/mob/user, var/permanent)
if(permanent)
@@ -92,7 +92,7 @@
set_light(0)
use_power = 0
target_drop_time = null
- if(user) user << "You deactivate the beacon."
+ if(user) to_chat(user, "You deactivate the beacon.")
/obj/machinery/power/supply_beacon/Destroy()
if(use_power)
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index 442703a42e..cf2449f1d3 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -94,7 +94,7 @@
singulo.target = src
icon_state = "[icontype]1"
active = 1
- machines |= src
+ START_MACHINE_PROCESSING(src)
if(user)
user << "You activate the beacon."
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index 27a03eb410..b55a354945 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -384,6 +384,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
blackbox.msg_cargo += blackbox_msg
if(SRV_FREQ)
blackbox.msg_service += blackbox_msg
+ if(EXP_FREQ)
+ blackbox.msg_explorer += blackbox_msg
else
blackbox.messages += blackbox_msg
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index b3c2c23f64..9353bb888c 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -1,5 +1,5 @@
/obj/machinery/computer/teleporter
- name = "Teleporter Control Console"
+ name = "teleporter control console"
desc = "Used to control a linked teleportation Hub and Station."
icon_keyboard = "teleport_key"
icon_screen = "teleport"
@@ -56,8 +56,8 @@
L = locate("landmark*[C.data]") // use old stype
if(istype(L, /obj/effect/landmark/) && istype(L.loc, /turf))
- usr << "You insert the coordinates into the machine."
- usr << "A message flashes across the screen reminding the traveller that the nuclear authentication disk is to remain on the station at all times."
+ to_chat(usr, "You insert the coordinates into the machine.")
+ to_chat(usr, "A message flashes across the screen, reminding the user that the nuclear authentication disk is not transportable via insecure means.")
user.drop_item()
qdel(I)
diff --git a/code/game/machinery/transportpod.dm b/code/game/machinery/transportpod.dm
new file mode 100644
index 0000000000..96401c0908
--- /dev/null
+++ b/code/game/machinery/transportpod.dm
@@ -0,0 +1,110 @@
+/obj/machinery/transportpod
+ name = "Ballistic Transportation Pod"
+ desc = "A fast transit ballistic pod used to get from one place to the next. Batteries not included!"
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "borg_pod_opened"
+
+ density = 1 //thicc
+ anchored = 1
+ use_power = 0
+
+ var/in_transit = 0
+ var/mob/occupant = null
+
+ var/xc = list(137, 209, 163, 110, 95, 60, 129, 201) // List of x values on the map to go to.
+ var/yc = list(134, 99, 169, 120, 96, 122, 189, 219) // List of y values on the map to go to.
+
+ var/limit_x = 3
+ var/limit_y = 3
+
+/obj/machinery/transportpod/process()
+ if(occupant)
+ if(in_transit)
+ var/locNum = rand(0, 7) //pick a random location
+ var/turf/L = locate(xc[locNum], yc[locNum], 1) // Pairs the X and Y to get an actual location.
+ limit_x = xc[locNum]+1
+ limit_y = yc[locNum]+1
+ build()
+ sleep(20) //Give explosion time so the pod itself doesn't go boom
+ src.forceMove(L)
+ playsound(src, pick('sound/effects/Explosion1.ogg', 'sound/effects/Explosion2.ogg', 'sound/effects/Explosion3.ogg', 'sound/effects/Explosion4.ogg'))
+ in_transit = 0
+ sleep(2)
+ go_out()
+ sleep(2)
+ del(src)
+
+/obj/machinery/transportpod/relaymove(mob/user as mob)
+ if(user.stat)
+ return
+ go_out()
+ return
+
+/obj/machinery/transportpod/update_icon()
+ ..()
+ if(occupant)
+ icon_state = "borg_pod_closed"
+ else
+ icon_state = "borg_pod_opened"
+
+/obj/machinery/transportpod/Bumped(var/mob/living/O)
+ go_in(O)
+
+/obj/machinery/transportpod/proc/go_in(var/mob/living/carbon/human/O)
+ if(occupant)
+ return
+
+ if(O.incapacitated()) //aint no sleepy people getting in here
+ return
+
+ add_fingerprint(O)
+ O.reset_view(src)
+ O.forceMove(src)
+ occupant = O
+ update_icon()
+ if(alert(O, "Are you sure you're ready to launch?", , "Yes", "No") == "Yes")
+ in_transit = 1
+ playsound(src, HYPERSPACE_WARMUP)
+ else
+ go_out()
+ return 1
+
+/obj/machinery/transportpod/proc/go_out()
+ if(!occupant)
+ return
+
+ occupant.forceMove(src.loc)
+ occupant.reset_view()
+ occupant = null
+ update_icon()
+
+/obj/machinery/transportpod/verb/move_eject()
+ set category = "Object"
+ set name = "Eject Pod"
+ set src in oview(1)
+
+ if(usr.incapacitated())
+ return
+
+ go_out()
+ add_fingerprint(usr)
+ return
+
+/obj/machinery/transportpod/verb/move_inside()
+ set category = "Object"
+ set name = "Enter Pod"
+ set src in oview(1)
+
+ if(usr.incapacitated()) //just to DOUBLE CHECK the damn sleepy people don't touch the pod
+ return
+
+ go_in(usr)
+
+/obj/machinery/transportpod/proc/build()
+ for(var/x = limit_x-2, x <= limit_x, x++)
+ for(var/y = limit_y-2, y <= limit_y, y++)
+ var/current_cell = locate(x, y, 1)
+ var/turf/T = get_turf(current_cell)
+ if(!current_cell)
+ continue
+ T.ChangeTurf(/turf/unsimulated/floor/shuttle_ceiling)
\ No newline at end of file
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 2253213957..41e3bdc38a 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -147,7 +147,7 @@
/obj/machinery/vending/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
emagged = 1
- user << "You short out the product lock on \the [src]"
+ to_chat(user, "You short out \the [src]'s product lock.")
return 1
/obj/machinery/vending/attackby(obj/item/weapon/W as obj, mob/user as mob)
@@ -182,7 +182,7 @@
return
else if(istype(W, /obj/item/weapon/screwdriver))
panel_open = !panel_open
- user << "You [panel_open ? "open" : "close"] the maintenance panel."
+ to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
playsound(src, W.usesound, 50, 1)
overlays.Cut()
if(panel_open)
@@ -199,7 +199,7 @@
W.forceMove(src)
coin = W
categories |= CAT_COIN
- user << "You insert \the [W] into \the [src]."
+ to_chat(user, "You insert \the [W] into \the [src].")
nanomanager.update_uis(src)
return
else if(istype(W, /obj/item/weapon/wrench))
@@ -211,7 +211,7 @@
if(do_after(user, 20 * W.toolspeed))
if(!src) return
- user << "You [anchored? "un" : ""]secured \the [src]!"
+ to_chat(user, "You [anchored? "un" : ""]secured \the [src]!")
anchored = !anchored
return
else
@@ -232,7 +232,7 @@
// This is not a status display message, since it's something the character
// themselves is meant to see BEFORE putting the money in
- usr << "\icon[cashmoney] That is not enough money."
+ to_chat(usr, "\icon[cashmoney] That is not enough money.")
return 0
if(istype(cashmoney, /obj/item/weapon/spacecash))
@@ -418,21 +418,22 @@
if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon))
if(!coin)
- usr << "There is no coin in this machine."
+ to_chat(usr, "There is no coin in this machine.")
return
coin.forceMove(src.loc)
if(!usr.get_active_hand())
usr.put_in_hands(coin)
- usr << "You remove \the [coin] from \the [src]"
+ to_chat(usr, "You remove \the [coin] from \the [src]")
coin = null
categories &= ~CAT_COIN
if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))))
if((href_list["vend"]) && (vend_ready) && (!currently_vending))
if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
- usr << "Access denied." //Unless emagged of course
+ to_chat(usr, "Access denied.") //Unless emagged of course
flick(icon_deny,src)
+ playsound(src.loc, 'sound/machines/deniedbeep.ogg', 50, 0)
return
var/key = text2num(href_list["vend"])
@@ -445,12 +446,12 @@
if(R.price <= 0)
vend(R, usr)
else if(istype(usr,/mob/living/silicon)) //If the item is not free, provide feedback if a synth is trying to buy something.
- usr << "Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled."
+ to_chat(usr, "Lawed unit recognized. Lawed units cannot complete this transaction. Purchase canceled.")
return
else
currently_vending = R
if(!vendor_account || vendor_account.suspended)
- status_message = "This machine is currently unable to process payments due to problems with the associated account."
+ status_message = "This machine is currently unable to process payments due to issues with the associated account."
status_error = 1
else
status_message = "Please swipe a card or insert cash to pay for the item."
@@ -467,8 +468,9 @@
/obj/machinery/vending/proc/vend(datum/stored_item/vending_product/R, mob/user)
if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
- usr << "Access denied." //Unless emagged of course
+ to_chat(usr, "Access denied.") //Unless emagged of course
flick(icon_deny,src)
+ playsound(src.loc, 'sound/machines/deniedbeep.ogg', 50, 0)
return
vend_ready = 0 //One thing at a time!!
status_message = "Vending..."
@@ -477,13 +479,13 @@
if(R.category & CAT_COIN)
if(!coin)
- user << "You need to insert a coin to get this item."
+ to_chat(user, "You need to insert a coin to get this item.")
return
if(coin.string_attached)
if(prob(50))
- user << "You successfully pull the coin out before \the [src] could swallow it."
+ to_chat(user, "You successfully pull the coin out before \the [src] could swallow it.")
else
- user << "You weren't able to pull the coin out fast enough, the machine ate it, string and all."
+ to_chat(user, "You weren't able to pull the coin out fast enough, the machine ate it, string and all.")
qdel(coin)
coin = null
categories &= ~CAT_COIN
@@ -685,6 +687,7 @@
/obj/item/weapon/reagent_containers/food/drinks/glass2/pint = 10,
/obj/item/weapon/reagent_containers/food/drinks/glass2/mug = 10,
/obj/item/weapon/reagent_containers/food/drinks/glass2/wine = 10,
+ /obj/item/weapon/reagent_containers/food/drinks/metaglass = 10,
/obj/item/weapon/reagent_containers/food/drinks/bottle/gin = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/bluecuracao = 5,
@@ -706,6 +709,7 @@
/obj/item/weapon/reagent_containers/food/drinks/bottle/tomatojuice = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/limejuice = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/lemonjuice = 5,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/applejuice = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/milk = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/cream = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/cola = 5,
@@ -765,7 +769,7 @@
/obj/machinery/vending/cola
name = "Robust Softdrinks"
desc = "A softdrink vendor provided by Robust Industries, LLC."
- icon_state = "Cola_Machine"
+ icon_state = "Cola_Machine" //VOREStation Edit
product_slogans = "Robust Softdrinks: More robust than a toolbox to the head!"
product_ads = "Refreshing!;Hope you're thirsty!;Over 1 million drinks sold!;Thirsty? Why not cola?;Please, have a drink!;Drink up!;The best drinks in space."
products = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola = 10,/obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind = 10,
@@ -805,7 +809,6 @@
contraband = list(/obj/item/weapon/reagent_containers/syringe/steroid = 4)
-//This one's from bay12
/obj/machinery/vending/cart
name = "PTech"
desc = "Cartridges for PDAs."
@@ -820,7 +823,7 @@
has_logs = 1
/obj/machinery/vending/cigarette
- name = "Cigarette machine" //OCD had to be uppercase to look nice with the new formating
+ name = "cigarette machine"
desc = "If you want to get cancer, might as well do it in style!"
product_slogans = "Space cigs taste good like a cigarette should.;I'd rather toolbox than switch.;Smoke!;Don't believe the reports - smoke today!"
product_ads = "Probably not bad for you!;Don't believe the scientists!;It's good for you!;Don't quit, buy more!;Smoke!;Nicotine heaven.;Best cigarettes since 2150.;Award-winning cigs.;Feeling temperamental? Try a Temperamento!;Carcinoma Angels - go fuck yerself!;Don't be so hard on yourself, kid. Smoke a Lucky Star!"
@@ -864,7 +867,6 @@
req_log_access = access_cmo
has_logs = 1
-//This one's from bay12
/obj/machinery/vending/phoronresearch
name = "Toximate 3000"
desc = "All the fine parts you need in one vending machine!"
@@ -984,7 +986,7 @@
products = list(
/obj/item/weapon/tray = 8,
/obj/item/weapon/material/kitchen/utensil/fork = 6,
- /obj/item/weapon/material/kitchen/utensil/knife = 6,
+ /obj/item/weapon/material/knife = 6,
/obj/item/weapon/material/kitchen/utensil/spoon = 6,
/obj/item/weapon/material/knife = 3,
/obj/item/weapon/material/kitchen/rollingpin = 2,
@@ -993,6 +995,7 @@
/obj/item/weapon/glass_extra/stick = 15,
/obj/item/weapon/glass_extra/straw = 15,
/obj/item/clothing/suit/chef/classic = 2,
+ /obj/item/weapon/storage/bag/food = 2,
/obj/item/weapon/storage/toolbox/lunchbox = 3,
/obj/item/weapon/storage/toolbox/lunchbox/heart = 3,
/obj/item/weapon/storage/toolbox/lunchbox/cat = 3,
@@ -1050,7 +1053,6 @@
req_log_access = access_ce
has_logs = 1
-//This one's from bay12
/obj/machinery/vending/engineering
name = "Robco Tool Maker"
desc = "Everything you need for do-it-yourself station repair."
@@ -1069,7 +1071,6 @@
req_log_access = access_ce
has_logs = 1
-//This one's from bay12
/obj/machinery/vending/robotics
name = "Robotech Deluxe"
desc = "All the tools you need to create your own robot army."
diff --git a/code/game/machinery/vending_vr.dm b/code/game/machinery/vending_vr.dm
index dd51fc712d..c7b43c3ce5 100644
--- a/code/game/machinery/vending_vr.dm
+++ b/code/game/machinery/vending_vr.dm
@@ -32,7 +32,7 @@
products = list(
/obj/item/weapon/tray = 8,
/obj/item/weapon/material/kitchen/utensil/fork = 6,
- /obj/item/weapon/material/kitchen/utensil/knife = 6,
+ /obj/item/weapon/material/knife/plastic = 6,
/obj/item/weapon/material/kitchen/utensil/spoon = 6,
/obj/item/weapon/material/knife = 3,
/obj/item/weapon/material/kitchen/rollingpin = 2,
@@ -60,7 +60,7 @@
icon_deny = "boozeomat-deny"
products = list(/obj/item/weapon/tray = 8,
/obj/item/weapon/material/kitchen/utensil/fork = 6,
- /obj/item/weapon/material/kitchen/utensil/knife = 6,
+ /obj/item/weapon/material/knife/plastic = 6,
/obj/item/weapon/material/kitchen/utensil/spoon = 6,
/obj/item/weapon/reagent_containers/food/snacks/tomatosoup = 8,
/obj/item/weapon/reagent_containers/food/snacks/mushroomsoup = 8,
@@ -94,7 +94,7 @@
icon_deny = "boozeomat-deny"
products = list(/obj/item/weapon/tray = 6,
/obj/item/weapon/material/kitchen/utensil/fork = 6,
- /obj/item/weapon/material/kitchen/utensil/knife = 6,
+ /obj/item/weapon/material/knife/plastic = 6,
/obj/item/weapon/material/kitchen/utensil/spoon = 6,
/obj/item/weapon/reagent_containers/food/snacks/hotandsoursoup = 3,
/obj/item/weapon/reagent_containers/food/snacks/kitsuneudon = 3,
diff --git a/code/game/machinery/vr_console.dm b/code/game/machinery/vr_console.dm
index ff156fa287..7b57e623af 100644
--- a/code/game/machinery/vr_console.dm
+++ b/code/game/machinery/vr_console.dm
@@ -1,5 +1,5 @@
/obj/machinery/vr_sleeper
- name = "VR sleeper"
+ name = "virtual reality sleeper"
desc = "A fancy bed with built-in sensory I/O ports and connectors to interface users' minds with their bodies in virtual reality."
icon = 'icons/obj/Cryogenic2.dmi'
icon_state = "syndipod_0"
@@ -53,6 +53,10 @@
if(default_deconstruction_screwdriver(user, I))
return
else if(default_deconstruction_crowbar(user, I))
+ if(occupant && avatar)
+ avatar.exit_vr()
+ avatar = null
+ go_out()
return
@@ -213,10 +217,9 @@
occupant.enter_vr(avatar)
// Prompt for username after they've enterred the body.
- var/newname = sanitize(input(avatar, "You are enterring virtual reality. Your username is currently [src.name]. Would you like to change it to something else?", "Name change") as null|text, MAX_NAME_LEN)
+ var/newname = sanitize(input(avatar, "You are entering virtual reality. Your username is currently [src.name]. Would you like to change it to something else?", "Name change") as null|text, MAX_NAME_LEN)
if (newname)
avatar.real_name = newname
else
occupant.enter_vr(avatar)
-
diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm
index 4e895b2b62..f06cf40da2 100644
--- a/code/game/mecha/equipment/tools/medical_tools.dm
+++ b/code/game/mecha/equipment/tools/medical_tools.dm
@@ -9,7 +9,7 @@
equip_cooldown = 50
var/mob/living/carbon/human/occupant = null
var/datum/global_iterator/pr_mech_sleeper
- var/inject_amount = 10
+ var/inject_amount = 5
required_type = /obj/mecha/medical
salvageable = 0
@@ -185,10 +185,14 @@
if(!R || !occupant || !SG || !(SG in chassis.equipment))
return 0
var/to_inject = min(R.volume, inject_amount)
- if(to_inject && occupant.reagents.get_reagent_amount(R.id) + to_inject <= inject_amount*2)
+ if(to_inject && occupant.reagents.get_reagent_amount(R.id) + to_inject > inject_amount*4)
+ occupant_message("Sleeper safeties prohibit you from injecting more than [inject_amount*4] units of [R.name].")
+ else
occupant_message("Injecting [occupant] with [to_inject] units of [R.name].")
log_message("Injecting [occupant] with [to_inject] units of [R.name].")
- SG.reagents.trans_id_to(occupant,R.id,to_inject)
+ //SG.reagents.trans_id_to(occupant,R.id,to_inject)
+ SG.reagents.remove_reagent(R.id,to_inject)
+ occupant.reagents.add_reagent(R.id,to_inject)
update_equip_info()
return
@@ -200,6 +204,19 @@
return 1
return
+/obj/item/mecha_parts/mecha_equipment/tool/sleeper/verb/eject()
+ set name = "Sleeper Eject"
+ set category = "Exosuit Interface"
+ set src = usr.loc
+ set popup_menu = 0
+ if(usr!=src.occupant || usr.stat == 2)
+ return
+ to_chat(usr,"Release sequence activated. This will take one minute.")
+ sleep(600)
+ if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already
+ return
+ go_out()//and release him from the eternal prison.
+
/datum/global_iterator/mech_sleeper
process(var/obj/item/mecha_parts/mecha_equipment/tool/sleeper/S)
diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm
index f3df144ea7..164140583f 100644
--- a/code/game/mecha/equipment/tools/tools.dm
+++ b/code/game/mecha/equipment/tools/tools.dm
@@ -19,7 +19,7 @@
//loading
if(istype(target,/obj))
var/obj/O = target
- if(O.buckled_mob)
+ if(O.has_buckled_mobs())
return
if(locate(/mob/living) in O)
occupant_message("You can't load living things into the cargo compartment.")
@@ -57,6 +57,7 @@
M.adjustOxyLoss(round(dam_force/2))
M.updatehealth()
occupant_message("You squeeze [target] with [src.name]. Something cracks.")
+ playsound(src.loc, "fracture", 5, 1, -2) //CRACK
chassis.visible_message("[chassis] squeezes [target].")
else
step_away(M,chassis)
@@ -408,7 +409,7 @@
/obj/item/mecha_parts/mecha_equipment/gravcatapult
name = "gravitational catapult"
- desc = "An exosuit mounted Gravitational Catapult."
+ desc = "An exosuit mounted gravitational catapult."
icon_state = "mecha_teleport"
origin_tech = list(TECH_BLUESPACE = 2, TECH_MAGNET = 3)
equip_cooldown = 10
@@ -1041,7 +1042,7 @@
/obj/item/mecha_parts/mecha_equipment/tool/passenger
name = "passenger compartment"
- desc = "A mountable passenger compartment for exo-suits. Rather cramped."
+ desc = "A mountable passenger compartment for exosuits. Rather cramped."
icon_state = "mecha_abooster_ccw"
origin_tech = list(TECH_ENGINEERING = 1, TECH_BIO = 1)
energy_drain = 10
@@ -1071,9 +1072,9 @@
log_message("[user] boarded.")
occupant_message("[user] boarded.")
else if(src.occupant != user)
- user << "[src.occupant] was faster. Try better next time, loser."
+ to_chat(user, "[src.occupant] was faster. Try harder next time, loser.")
else
- user << "You stop entering the exosuit."
+ to_chat(user, "You stop entering the exosuit.")
/obj/item/mecha_parts/mecha_equipment/tool/passenger/verb/eject()
set name = "Eject"
@@ -1083,7 +1084,7 @@
if(usr != occupant)
return
- occupant << "You climb out from \the [src]."
+ to_chat(occupant, "You climb out from \the [src].")
go_out()
occupant_message("[occupant] disembarked.")
log_message("[occupant] disembarked.")
@@ -1186,3 +1187,98 @@
#undef LOCKED
#undef OCCUPIED
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack
+ name = "ion jetpack"
+ desc = "Using directed ion bursts and cunning solar wind reflection technique, this device enables controlled space flight."
+ icon_state = "mecha_jetpack"
+ equip_cooldown = 5
+ energy_drain = 50
+ var/wait = 0
+ var/datum/effect/effect/system/ion_trail_follow/ion_trail
+
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/can_attach(obj/mecha/M as obj)
+ if(!(locate(src.type) in M.equipment) && !M.proc_res["dyndomove"])
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/detach()
+ ..()
+ chassis.proc_res["dyndomove"] = null
+ return
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/attach(obj/mecha/M as obj)
+ ..()
+ if(!ion_trail)
+ ion_trail = new
+ ion_trail.set_up(chassis)
+ return
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/proc/toggle()
+ if(!chassis)
+ return
+ !equip_ready? turn_off() : turn_on()
+ return equip_ready
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/proc/turn_on()
+ set_ready_state(0)
+ chassis.proc_res["dyndomove"] = src
+ ion_trail.start()
+ occupant_message("Activated")
+ log_message("Activated")
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/proc/turn_off()
+ set_ready_state(1)
+ chassis.proc_res["dyndomove"] = null
+ ion_trail.stop()
+ occupant_message("Deactivated")
+ log_message("Deactivated")
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/proc/dyndomove(direction)
+ if(!action_checks())
+ return chassis.dyndomove(direction)
+ var/move_result = 0
+ if(chassis.hasInternalDamage(MECHA_INT_CONTROL_LOST))
+ move_result = step_rand(chassis)
+ else if(chassis.dir!=direction)
+ chassis.set_dir(direction)
+ move_result = 1
+ else
+ move_result = step(chassis,direction)
+ if(chassis.occupant)
+ for(var/obj/effect/speech_bubble/B in range(1, chassis))
+ if(B.parent == chassis.occupant)
+ B.loc = chassis.loc
+ if(move_result)
+ wait = 1
+ chassis.use_power(energy_drain)
+ if(!chassis.pr_inertial_movement.active())
+ chassis.pr_inertial_movement.start(list(chassis,direction))
+ else
+ chassis.pr_inertial_movement.set_process_args(list(chassis,direction))
+ do_after_cooldown()
+ return 1
+ return 0
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/action_checks()
+ if(equip_ready || wait)
+ return 0
+ if(energy_drain && !chassis.has_charge(energy_drain))
+ return 0
+ if(chassis.check_for_support())
+ return 0
+ return 1
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/get_equip_info()
+ if(!chassis) return
+ return "* [src.name] \[Toggle\]"
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/Topic(href,href_list)
+ ..()
+ if(href_list["toggle"])
+ toggle()
+
+/obj/item/mecha_parts/mecha_equipment/tool/jetpack/do_after_cooldown()
+ sleep(equip_cooldown)
+ wait = 0
+ return 1
\ No newline at end of file
diff --git a/code/game/mecha/equipment/tools/unused_tools.dm b/code/game/mecha/equipment/tools/unused_tools.dm
index 1b6d8c9e23..c7ef794c4e 100644
--- a/code/game/mecha/equipment/tools/unused_tools.dm
+++ b/code/game/mecha/equipment/tools/unused_tools.dm
@@ -6,134 +6,6 @@
-
-//NEEDS SPRITE! (When this gets ticked in search for 'TODO MECHA JETPACK SPRITE MISSING' through code to uncomment the place where it's missing.)
-/obj/item/mecha_parts/mecha_equipment/jetpack
- name = "jetpack"
- desc = "Using directed ion bursts and cunning solar wind reflection technique, this device enables controlled space flight."
- icon_state = "mecha_equip"
- equip_cooldown = 5
- energy_drain = 50
- var/wait = 0
- var/datum/effect/effect/system/ion_trail_follow/ion_trail
-
-
- can_attach(obj/mecha/M as obj)
- if(!(locate(src.type) in M.equipment) && !M.proc_res["dyndomove"])
- return ..()
-
- detach()
- ..()
- chassis.proc_res["dyndomove"] = null
- return
-
- attach(obj/mecha/M as obj)
- ..()
- if(!ion_trail)
- ion_trail = new
- ion_trail.set_up(chassis)
- return
-
- proc/toggle()
- if(!chassis)
- return
- !equip_ready? turn_off() : turn_on()
- return equip_ready
-
- proc/turn_on()
- set_ready_state(0)
- chassis.proc_res["dyndomove"] = src
- ion_trail.start()
- occupant_message("Activated")
- log_message("Activated")
-
- proc/turn_off()
- set_ready_state(1)
- chassis.proc_res["dyndomove"] = null
- ion_trail.stop()
- occupant_message("Deactivated")
- log_message("Deactivated")
-
- proc/dyndomove(direction)
- if(!action_checks())
- return chassis.dyndomove(direction)
- var/move_result = 0
- if(chassis.hasInternalDamage(MECHA_INT_CONTROL_LOST))
- move_result = step_rand(chassis)
- else if(chassis.dir!=direction)
- chassis.set_dir(direction)
- move_result = 1
- else
- move_result = step(chassis,direction)
- if(chassis.occupant)
- for(var/obj/effect/speech_bubble/B in range(1, chassis))
- if(B.parent == chassis.occupant)
- B.loc = chassis.loc
- if(move_result)
- wait = 1
- chassis.use_power(energy_drain)
- if(!chassis.pr_inertial_movement.active())
- chassis.pr_inertial_movement.start(list(chassis,direction))
- else
- chassis.pr_inertial_movement.set_process_args(list(chassis,direction))
- do_after_cooldown()
- return 1
- return 0
-
- action_checks()
- if(equip_ready || wait)
- return 0
- if(energy_drain && !chassis.has_charge(energy_drain))
- return 0
- if(chassis.check_for_support())
- return 0
- return 1
-
- get_equip_info()
- if(!chassis) return
- return "* [src.name] \[Toggle\]"
-
-
- Topic(href,href_list)
- ..()
- if(href_list["toggle"])
- toggle()
-
- do_after_cooldown()
- sleep(equip_cooldown)
- wait = 0
- return 1
-
-
-/obj/item/mecha_parts/mecha_equipment/defence_shocker
- name = "exosuit defence shocker"
- desc = ""
- icon_state = "mecha_teleport"
- equip_cooldown = 10
- energy_drain = 100
- range = RANGED
- var/shock_damage = 15
- var/active
-
- can_attach(obj/mecha/M as obj)
- if(..())
- if(!istype(M, /obj/mecha/combat/honker))
- if(!M.proc_res["dynattackby"] && !M.proc_res["dynattackhand"] && !M.proc_res["dynattackalien"])
- return 1
- return 0
-
- attach(obj/mecha/M as obj)
- ..()
- chassis.proc_res["dynattackby"] = src
- return
-
- proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
- if(!action_checks(user) || !active)
- return
- user.electrocute_act(shock_damage, src)
- return chassis.dynattackby(W,user)
-
-
/*
/obj/item/mecha_parts/mecha_equipment/book_stocker
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 3b8cf65816..50012fe95c 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -117,20 +117,18 @@
projectile = /obj/item/projectile/beam/stun
fire_sound = 'sound/weapons/Taser.ogg'
-/* Commenting this out rather than removing it because it may be useful for reference.
+/*
/obj/item/mecha_parts/mecha_equipment/weapon/honker
- name = "\improper HoNkER BlAsT 5000"
+ name = "sound emission device"
icon_state = "mecha_honker"
- energy_drain = 200
+ energy_drain = 300
equip_cooldown = 150
range = MELEE|RANGED
- construction_time = 500
- construction_cost = list("metal"=20000,"bananium"=10000)
+ origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 4, TECH_ILLEGAL = 1)
- can_attach(obj/mecha/combat/honker/M as obj)
- if(!istype(M))
- return 0
- return ..()
+ var/ear_safety = 0
+ if(iscarbon(M))
+ ear_safety = M.get_ear_protection()
action(target)
if(!chassis)
@@ -140,25 +138,22 @@
if(!equip_ready)
return 0
- playsound(chassis, 'sound/items/AirHorn.ogg', 100, 1)
- chassis.occupant_message("HONK")
+ playsound(chassis, 'sound/effects/bang.ogg', 30, 1, 30)
+ chassis.occupant_message("You emit a high-pitched noise from the mech.")
for(var/mob/living/carbon/M in ohearers(6, chassis))
if(istype(M, /mob/living/carbon/human))
var/mob/living/carbon/human/H = M
- if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs))
- continue
- M << "HONK"
+ if(ear_safety > 0)
+ return
+ to_chat(M, "\Your ears feel like they're bleeding!")
+ playsound(M, 'sound/effects/bang.ogg', 70, 1, 30)
M.sleeping = 0
- M.stuttering += 20
M.ear_deaf += 30
+ M.ear_damage += rand(5, 20)
M.Weaken(3)
- if(prob(30))
- M.Stun(10)
- M.Paralyse(4)
- else
- M.make_jittery(500)
+ M.Stun(5)
chassis.use_power(energy_drain)
- log_message("Honked from [src.name]. HONK!")
+ log_message("Used a sound emission device.")
do_after_cooldown()
return
*/
@@ -335,3 +330,35 @@
projectile = /obj/item/projectile/bullet/incendiary/flamethrower
origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3, TECH_PHORON = 3, TECH_ILLEGAL = 2)
+
+//////////////
+//Defensive//
+//////////////
+
+/obj/item/mecha_parts/mecha_equipment/shocker
+ name = "exosuit electrifier"
+ desc = "A device to electrify the external portions of a mecha in order to increase its defensive capabilities."
+ icon_state = "mecha_coil"
+ equip_cooldown = 10
+ energy_drain = 100
+ range = RANGED
+ origin_tech = list(TECH_COMBAT = 3, TECH_POWER = 6)
+ var/shock_damage = 15
+ var/active
+
+/obj/item/mecha_parts/mecha_equipment/shocker/can_attach(obj/mecha/M as obj)
+ if(..())
+ if(!M.proc_res["dynattackby"] && !M.proc_res["dynattackhand"] && !M.proc_res["dynattackalien"])
+ return 1
+ return 0
+
+/obj/item/mecha_parts/mecha_equipment/shocker/attach(obj/mecha/M as obj)
+ ..()
+ chassis.proc_res["dynattackby"] = src
+ return
+
+/obj/item/mecha_parts/mecha_equipment/shocker/proc/dynattackby(obj/item/weapon/W, mob/living/user)
+ if(!action_checks(user) || !active)
+ return
+ user.electrocute_act(shock_damage, src)
+ return chassis.dynattackby(W,user)
\ No newline at end of file
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 23363f2a39..103fb9b876 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -13,7 +13,7 @@
var/speed = 1
var/mat_efficiency = 1
- var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "gold" = 0, "silver" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0)
+ var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0)
var/res_max_amount = 200000
var/datum/research/files
diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm
index 87da5760ce..64123337fc 100644
--- a/code/game/mecha/mech_prosthetics.dm
+++ b/code/game/mecha/mech_prosthetics.dm
@@ -13,7 +13,7 @@
var/speed = 1
var/mat_efficiency = 1
- var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "gold" = 0, "silver" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0, "plasteel" = 0)
+ var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0, "plasteel" = 0)
var/res_max_amount = 200000
var/datum/research/files
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index db8fc51992..360c9e1bbf 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -7,7 +7,6 @@
#define MELEE 1
#define RANGED 2
-
/obj/mecha
name = "Mecha"
desc = "Exosuit"
@@ -505,7 +504,7 @@
return
/obj/mecha/attack_hand(mob/user as mob)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed())
src.log_message("Attack by hand/paw. Attacker - [user].",1)
if(istype(user,/mob/living/carbon/human))
@@ -513,7 +512,6 @@
if(H.species.can_shred(user))
if(!prob(src.deflect_chance))
src.take_damage(15)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
user << "You slash at the armored suit!"
@@ -666,7 +664,7 @@
return
/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
src.log_message("Attacked by [W]. Attacker - [user]")
if(prob(src.deflect_chance))
user << "\The [W] bounces off [src.name]."
@@ -1052,6 +1050,17 @@
return
+/obj/mecha/MouseDrop_T(mob/O, mob/user as mob)
+ //Humans can pilot mechs.
+ if(!ishuman(O))
+ return
+
+ //Can't put other people into mechs (can comment this out if you want that to be possible)
+ if(O != user)
+ return
+
+ move_inside()
+
/obj/mecha/verb/move_inside()
set category = "Object"
set name = "Enter Exosuit"
@@ -1061,17 +1070,17 @@
return
if (usr.buckled)
- usr << "You can't climb into the exosuit while buckled!"
+ to_chat(usr,"You can't climb into the exosuit while buckled!")
return
src.log_message("[usr] tries to move in.")
if(iscarbon(usr))
var/mob/living/carbon/C = usr
if(C.handcuffed)
- usr << "Kinda hard to climb in while handcuffed don't you think?"
+ to_chat(usr,"Kinda hard to climb in while handcuffed don't you think?")
return
if (src.occupant)
- usr << "The [src.name] is already occupied!"
+ to_chat(usr,"The [src.name] is already occupied!")
src.log_append_to_last("Permission denied.")
return
/*
@@ -1086,12 +1095,12 @@
else if(src.operation_allowed(usr))
passed = 1
if(!passed)
- usr << "Access denied"
+ 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)
- usr << "You're too busy getting your life sucked out of you."
+ to_chat(usr,"You're too busy getting your life sucked out of you.")
return
// usr << "You start climbing into [src.name]"
@@ -1101,9 +1110,9 @@
if(!src.occupant)
moved_inside(usr)
else if(src.occupant!=usr)
- usr << "[src.occupant] was faster. Try better next time, loser."
+ to_chat(usr,"[src.occupant] was faster. Try better next time, loser.")
else
- usr << "You stop entering the exosuit."
+ to_chat(usr,"You stop entering the exosuit.")
return
/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob)
@@ -1763,7 +1772,7 @@
/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed())
if(!damage)
return 0
diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm
index 6aec830c89..90577eaf64 100644
--- a/code/game/mecha/medical/odysseus.dm
+++ b/code/game/mecha/medical/odysseus.dm
@@ -23,6 +23,7 @@
occupant_message("[H.glasses] prevent you from using [src] [hud]")
else
H.glasses = hud
+ H.recalculate_vis()
return 1
else
return 0
@@ -32,6 +33,7 @@
var/mob/living/carbon/human/H = occupant
if(H.glasses == hud)
H.glasses = null
+ H.recalculate_vis()
..()
return
/*
@@ -63,7 +65,7 @@
name = "Integrated Medical Hud"
- process_hud(var/mob/M)
+// process_hud(var/mob/M) //TODO VIS
/*
world<< "view(M)"
for(var/mob/mob in view(M))
@@ -74,7 +76,7 @@
world<< "view(M.loc)"
for(var/mob/mob in view(M.loc))
world << "[mob]"
-*/
+
if(!M || M.stat || !(M in view(M))) return
if(!M.client) return
@@ -99,7 +101,9 @@
C.images += holder
holder = patient.hud_list[STATUS_HUD]
- if(patient.stat == DEAD)
+ if(patient.isSynthetic())
+ holder.icon_state = "hudrobo"
+ else if(patient.stat == DEAD)
holder.icon_state = "huddead"
else if(foundVirus)
holder.icon_state = "hudill"
@@ -113,7 +117,7 @@
holder.icon_state = "hudhealthy"
C.images += holder
-
+*/
/obj/mecha/medical/odysseus/loaded/New()
..()
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper
diff --git a/code/game/objects/banners.dm b/code/game/objects/banners.dm
new file mode 100644
index 0000000000..445df1dff5
--- /dev/null
+++ b/code/game/objects/banners.dm
@@ -0,0 +1,35 @@
+/obj/item/weapon/banner
+ name = "banner"
+ icon = 'icons/obj/items.dmi'
+ icon_state = "banner"
+ desc = "A banner that's invisible because it shouldn't exist."
+
+/obj/item/weapon/banner/red
+ name = "red banner"
+ icon_state = "banner-red"
+ desc = "A red colored banner."
+
+/obj/item/weapon/banner/blue
+ name = "blue banner"
+ icon_state = "banner-blue"
+ desc = "A blue colored banner."
+
+/obj/item/weapon/banner/green
+ name = "green banner"
+ icon_state = "banner-green"
+ desc = "A green colored banner."
+
+/obj/item/weapon/banner/nt
+ name = "\improper NanoTrasen banner"
+ icon_state = "banner-nt"
+ desc = "A banner with NanoTrasen's logo on it."
+
+/obj/item/weapon/banner/solgov
+ name = "\improper SolGov banner"
+ icon_state = "banner-solgov"
+ desc = "A banner with the symbol of the Solar Confederate Government."
+
+/obj/item/weapon/banner/virgov
+ name = "\improper VirGov banner"
+ icon_state = "banner-virgov"
+ desc = "A banner with the symbol of the local government, the Vir Governmental Authority, also known as SifGov."
\ No newline at end of file
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index a91e022f9b..9b27cdecbd 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -6,26 +6,44 @@
var/buckle_dir = 0
var/buckle_lying = -1 //bed-like behavior, forces mob.lying = buckle_lying if != -1
var/buckle_require_restraints = 0 //require people to be handcuffed before being able to buckle. eg: pipes
- var/mob/living/buckled_mob = null
+// var/mob/living/buckled_mob = null
+ var/list/mob/living/buckled_mobs = null //list()
+ var/max_buckled_mobs = 1
/atom/movable/attack_hand(mob/living/user)
. = ..()
- if(can_buckle && buckled_mob)
- user_unbuckle_mob(user)
+// if(can_buckle && buckled_mob)
+// user_unbuckle_mob(user)
+
+ if(can_buckle && has_buckled_mobs())
+ if(buckled_mobs.len > 1)
+ var/unbuckled = input(user, "Who do you wish to unbuckle?","Unbuckle Who?") as null|mob in buckled_mobs
+ if(user_unbuckle_mob(unbuckled, user))
+ return TRUE
+ else
+ if(user_unbuckle_mob(buckled_mobs[1], user))
+ return TRUE
/obj/proc/attack_alien(mob/user as mob) //For calling in the event of Xenomorph or other alien checks.
return
/obj/attack_robot(mob/living/user)
- if(Adjacent(user) && buckled_mob) //Checks if what we're touching is adjacent to us and has someone buckled to it. This should prevent interacting with anti-robot manual valves among other things.
+ if(Adjacent(user) && has_buckled_mobs()) //Checks if what we're touching is adjacent to us and has someone buckled to it. This should prevent interacting with anti-robot manual valves among other things.
return attack_hand(user) //Process as if we're a normal person touching the object.
return ..() //Otherwise, treat this as an AI click like usual.
/atom/movable/MouseDrop_T(mob/living/M, mob/living/user)
. = ..()
if(can_buckle && istype(M))
- user_buckle_mob(M, user)
+ if(user_buckle_mob(M, user))
+ return TRUE
+
+/atom/movable/proc/has_buckled_mobs()
+ if(!buckled_mobs)
+ return FALSE
+ if(buckled_mobs.len)
+ return TRUE
/atom/movable/Destroy()
unbuckle_mob()
@@ -33,52 +51,81 @@
/atom/movable/proc/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE)
- if((!can_buckle && !forced) || !istype(M) || M.buckled || M.pinned.len || (buckle_require_restraints && !M.restrained()))
- return 0
+ if(!buckled_mobs)
+ buckled_mobs = list()
+
+ if(!istype(M))
+ return FALSE
+
if(check_loc && M.loc != loc)
- return 0
- if(buckled_mob) //Handles trying to buckle yourself to the chair when someone is on it
- M << "\The [src] already has someone buckled to it."
- return 0
+ return FALSE
+
+ if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained()))
+ return FALSE
+
+ if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it
+ to_chat(M, "\The [src] can't buckle anymore people.")
+ return FALSE
M.buckled = src
M.facing_dir = null
M.set_dir(buckle_dir ? buckle_dir : dir)
M.update_canmove()
M.update_floating( M.Check_Dense_Object() )
- buckled_mob = M
+// buckled_mob = M
+ buckled_mobs |= M
post_buckle_mob(M)
- return 1
+ return TRUE
+
+/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force = FALSE)
+ if(!buckled_mob) // If we didn't get told which mob needs to get unbuckled, just assume its the first one on the list.
+ if(has_buckled_mobs())
+ buckled_mob = buckled_mobs[1]
+ else
+ return
-/atom/movable/proc/unbuckle_mob()
if(buckled_mob && buckled_mob.buckled == src)
. = buckled_mob
buckled_mob.buckled = null
buckled_mob.anchored = initial(buckled_mob.anchored)
buckled_mob.update_canmove()
buckled_mob.update_floating( buckled_mob.Check_Dense_Object() )
- buckled_mob = null
+ // buckled_mob = null
+ buckled_mobs -= buckled_mob
post_buckle_mob(.)
+/atom/movable/proc/unbuckle_all_mobs(force = FALSE)
+ if(!has_buckled_mobs())
+ return
+ for(var/m in buckled_mobs)
+ unbuckle_mob(m, force)
+
+//Handle any extras after buckling/unbuckling
+//Called on buckle_mob() and unbuckle_mob()
/atom/movable/proc/post_buckle_mob(mob/living/M)
return
+//Wrapper procs that handle sanity and user feedback
/atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, var/forced = FALSE, var/silent = FALSE)
if(!ticker)
user << "You can't buckle anyone in before the game starts."
- if(!user.Adjacent(M) || user.restrained() || user.lying || user.stat || istype(user, /mob/living/silicon/pai))
- return
- if(M == buckled_mob)
- return
+ return FALSE // Is this really needed?
+ if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai))
+ return FALSE
+ if(M in buckled_mobs)
+ to_chat(user, "\The [M] is already buckled to \the [src].")
+ return FALSE
add_fingerprint(user)
- unbuckle_mob()
+// unbuckle_mob()
//can't buckle unless you share locs so try to move M to the obj.
if(M.loc != src.loc)
- step_towards(M, src)
+ if(M.Adjacent(src) && user.Adjacent(src))
+ M.forceMove(get_turf(src))
+ // step_towards(M, src)
. = buckle_mob(M, forced)
if(.)
@@ -94,8 +141,8 @@
"You are buckled to [src] by [user.name]!",\
"You hear metal clanking.")
-/atom/movable/proc/user_unbuckle_mob(mob/user)
- var/mob/living/M = unbuckle_mob()
+/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
+ var/mob/living/M = unbuckle_mob(buckled_mob)
if(M)
if(M != user)
M.visible_message(\
@@ -111,18 +158,20 @@
return M
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
- if(buckled_mob)
-// if(!buckled_mob.Move(newloc, direct))
- if(!buckled_mob.forceMove(newloc, direct))
- loc = buckled_mob.loc
- last_move = buckled_mob.last_move
- buckled_mob.inertia_dir = last_move
- return FALSE
- else
- buckled_mob.set_dir(dir)
+ if(has_buckled_mobs())
+ for(var/A in buckled_mobs)
+ var/mob/living/L = A
+// if(!L.Move(newloc, direct))
+ if(!L.forceMove(newloc, direct))
+ loc = L.loc
+ last_move = L.last_move
+ L.inertia_dir = last_move
+ return FALSE
+ else
+ L.set_dir(dir)
return TRUE
/atom/movable/Move(atom/newloc, direct = 0)
. = ..()
- if(. && buckled_mob && !handle_buckled_mob_movement(newloc, direct)) //movement failed due to buckled mob(s)
+ if(. && has_buckled_mobs() && !handle_buckled_mob_movement(newloc, direct)) //movement failed due to buckled mob(s)
. = 0
diff --git a/code/game/objects/effects/alien/aliens.dm b/code/game/objects/effects/alien/aliens.dm
index 3d9713cd29..2c864f0a56 100644
--- a/code/game/objects/effects/alien/aliens.dm
+++ b/code/game/objects/effects/alien/aliens.dm
@@ -120,7 +120,7 @@
/obj/effect/alien/resin/attackby(obj/item/weapon/W as obj, mob/user as mob)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
var/aforce = W.force
health = max(0, health - aforce)
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
@@ -227,7 +227,7 @@ Alien plants should do something if theres a lot of poison
return
/obj/effect/alien/weeds/attackby(var/obj/item/weapon/W, var/mob/user)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
if(W.attack_verb.len)
visible_message("\The [src] have been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]")
else
diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm
index 9b15464544..ee1c75980c 100644
--- a/code/game/objects/effects/chem/water.dm
+++ b/code/game/objects/effects/chem/water.dm
@@ -3,7 +3,7 @@
icon = 'icons/effects/effects.dmi'
icon_state = "extinguish"
mouse_opacity = 0
- pass_flags = PASSTABLE | PASSGRILLE
+ pass_flags = PASSTABLE | PASSGRILLE | PASSBLOB
/obj/effect/effect/water/New(loc)
..()
@@ -27,7 +27,7 @@
var/mob/M
for(var/atom/A in T)
if(!ismob(A) && A.simulated) // Mobs are handled differently
- reagents.touch(A)
+ reagents.touch(A, reagents.total_volume)
else if(ismob(A) && !M)
M = A
if(M)
diff --git a/code/game/objects/effects/decals/Cleanable/fuel.dm b/code/game/objects/effects/decals/Cleanable/fuel.dm
index d9e9baabf6..c04540535b 100644
--- a/code/game/objects/effects/decals/Cleanable/fuel.dm
+++ b/code/game/objects/effects/decals/Cleanable/fuel.dm
@@ -6,7 +6,7 @@
anchored = 1
var/amount = 1
-/obj/effect/decal/cleanable/liquid_fuel/New(turf/newLoc,amt=1,nologs=0)
+/obj/effect/decal/cleanable/liquid_fuel/New(turf/newLoc,amt=1,nologs=1)
if(!nologs)
message_admins("Liquid fuel has spilled in [newLoc.loc.name] ([newLoc.x],[newLoc.y],[newLoc.z]) (JMP)")
log_game("Liquid fuel has spilled in [newLoc.loc.name] ([newLoc.x],[newLoc.y],[newLoc.z])")
diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm
index f104b77fd7..0ad5ac13f8 100644
--- a/code/game/objects/effects/decals/Cleanable/tracks.dm
+++ b/code/game/objects/effects/decals/Cleanable/tracks.dm
@@ -154,20 +154,49 @@ var/global/list/image/fluidtrack_cache=list()
/obj/effect/decal/cleanable/blood/tracks/footprints
name = "wet footprints"
dryname = "dried footprints"
- desc = "Whoops..."
- drydesc = "Whoops..."
+ desc = "They look like still wet tracks left by footwear."
+ drydesc = "They look like dried tracks left by footwear."
coming_state = "human1"
going_state = "human2"
amount = 0
+/obj/effect/decal/cleanable/blood/tracks/snake
+ name = "wet tracks"
+ dryname = "dried tracks"
+ desc = "They look like still wet tracks left by a giant snake."
+ drydesc = "They look like dried tracks left by a giant snake."
+ coming_state = "snake1"
+ going_state = "snake2"
+ random_icon_states = null
+ amount = 0
+
+/obj/effect/decal/cleanable/blood/tracks/paw
+ name = "wet tracks"
+ dryname = "dried tracks"
+ desc = "They look like still wet tracks left by a mammal."
+ drydesc = "They look like dried tracks left by a mammal."
+ coming_state = "paw1"
+ going_state = "paw2"
+ random_icon_states = null
+ amount = 0
+
+/obj/effect/decal/cleanable/blood/tracks/claw
+ name = "wet tracks"
+ dryname = "dried tracks"
+ desc = "They look like still wet tracks left by a reptile."
+ drydesc = "They look like dried tracks left by a reptile."
+ coming_state = "claw1"
+ going_state = "claw2"
+ random_icon_states = null
+ amount = 0
+
/obj/effect/decal/cleanable/blood/tracks/wheels
name = "wet tracks"
dryname = "dried tracks"
- desc = "Whoops..."
- drydesc = "Whoops..."
+ desc = "They look like still wet tracks left by wheels."
+ drydesc = "They look like dried tracks left by wheels."
coming_state = "wheels"
going_state = ""
- desc = "They look like tracks left by wheels."
gender = PLURAL
random_icon_states = null
amount = 0
\ No newline at end of file
diff --git a/code/game/objects/effects/explosion_particles.dm b/code/game/objects/effects/explosion_particles.dm
index e0750ba1c3..63b2a09c24 100644
--- a/code/game/objects/effects/explosion_particles.dm
+++ b/code/game/objects/effects/explosion_particles.dm
@@ -67,4 +67,10 @@
spawn(5)
var/datum/effect/effect/system/smoke_spread/S = new/datum/effect/effect/system/smoke_spread()
S.set_up(5,0,location,null)
- S.start()
\ No newline at end of file
+ S.start()
+
+/datum/effect/system/explosion/smokeless/start()
+ new/obj/effect/explosion(location)
+ var/datum/effect/system/expl_particles/P = new/datum/effect/system/expl_particles()
+ P.set_up(10,location)
+ P.start()
\ No newline at end of file
diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm
index c2f590c561..48026296d7 100644
--- a/code/game/objects/effects/misc.dm
+++ b/code/game/objects/effects/misc.dm
@@ -5,4 +5,43 @@
icon = 'icons/obj/items.dmi'
icon_state = "strangepresent"
density = 1
- anchored = 0
\ No newline at end of file
+ anchored = 0
+
+/obj/effect/temporary_effect
+ name = "self deleting effect"
+ desc = "How are you examining what which cannot be seen?"
+ icon = 'icons/effects/effects.dmi'
+ invisibility = 0
+ var/time_to_die = 10 SECONDS // Afer which, it will delete itself.
+
+/obj/effect/temporary_effect/New()
+ ..()
+ if(time_to_die)
+ spawn(time_to_die)
+ qdel(src)
+
+// Shown really briefly when attacking with axes.
+/obj/effect/temporary_effect/cleave_attack
+ name = "cleaving attack"
+ desc = "Something swinging really wide."
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "cleave"
+ layer = 6
+ time_to_die = 6
+ alpha = 140
+ mouse_opacity = 0
+ 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.
+ animate(src, alpha = 0, time = time_to_die - 1)
+
+/obj/effect/temporary_effect/shuttle_landing
+ name = "shuttle landing"
+ desc = "You better move if you don't want to go splat!"
+ icon_state = "shuttle_warning_still"
+ time_to_die = 4.9 SECONDS
+
+/obj/effect/temporary_effect/shuttle_landing/initialize()
+ flick("shuttle_warning", src) // flick() forces the animation to always begin at the start.
+ ..()
\ No newline at end of file
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index e9d25346f2..b0e66f6306 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -21,7 +21,7 @@
return
/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(W))
if(W.attack_verb.len)
visible_message("\The [src] have been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]")
@@ -218,25 +218,8 @@
//=================
if(isturf(loc))
- if(prob(25))
- var/list/nearby = trange(5, src) - loc
- if(nearby.len)
- var/target_atom = pick(nearby)
- walk_to(src, target_atom, 5)
- if(prob(25))
- src.visible_message("\The [src] skitters[pick(" away"," around","")].")
- else if(prob(5))
- //vent crawl!
- for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))
- if(!v.welded)
- entry_vent = v
- walk_to(src, entry_vent, 5)
- break
+ skitter()
- if(amount_grown >= 100)
- var/spawn_type = pick(grow_as)
- new spawn_type(src.loc, src)
- qdel(src)
else if(isorgan(loc))
if(!amount_grown) amount_grown = 1
var/obj/item/organ/external/O = loc
@@ -257,6 +240,27 @@
if(amount_grown)
amount_grown += rand(0,2)
+/obj/effect/spider/spiderling/proc/skitter()
+ if(isturf(loc))
+ if(prob(25))
+ var/list/nearby = trange(5, src) - loc
+ if(nearby.len)
+ var/target_atom = pick(nearby)
+ walk_to(src, target_atom, 5)
+ if(prob(25))
+ src.visible_message("\The [src] skitters[pick(" away"," around","")].")
+ else if(prob(5))
+ //vent crawl!
+ for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))
+ if(!v.welded)
+ entry_vent = v
+ walk_to(src, entry_vent, 5)
+ break
+ if(amount_grown >= 100)
+ var/spawn_type = pick(grow_as)
+ new spawn_type(src.loc, src)
+ qdel(src)
+
/obj/effect/decal/cleanable/spiderling_remains
name = "spiderling remains"
desc = "Green squishy mess."
@@ -269,7 +273,7 @@
icon_state = "cocoon1"
health = 60
- New()
+/obj/effect/spider/cocoon/New()
icon_state = pick("cocoon1","cocoon2","cocoon3")
/obj/effect/spider/cocoon/Destroy()
diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm
index 4fdfa8a171..18f66e70f5 100644
--- a/code/game/objects/effects/step_triggers.dm
+++ b/code/game/objects/effects/step_triggers.dm
@@ -158,7 +158,7 @@ var/global/list/tele_landmarks = list() // Terrible, but the alternative is loop
var/safety = 100 // Infinite loop protection.
while(!T && safety)
var/turf/simulated/candidate = pick(planet.planet_floors)
- if(!istype(candidate) || istype(candidate, /turf/simulated/sky))
+ if(!istype(candidate) || istype(candidate, /turf/simulated/sky) || !T.outdoors)
safety--
continue
else
@@ -175,12 +175,9 @@ var/global/list/tele_landmarks = list() // Terrible, but the alternative is loop
if(isliving(A)) // Someday, implement parachutes. For now, just turbomurder whoever falls.
var/mob/living/L = A
- for(var/i = 1 to 6)
- L.adjustBruteLoss(100)
+ L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this.
message_admins("\The [A] fell out of the sky.")
- explosion(T, 0, 1, 2)
A.forceMove(T)
- T.visible_message("\A [A] falls out of the sky and crashes into \the [T]!")
else
message_admins("ERROR: planetary_fall step trigger lacks a planet to fall onto.")
return
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index 0bf146f037..f6eec0de71 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -104,7 +104,7 @@ proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impa
sleep(8)
if(!powernet_rebuild_was_deferred_already && defer_powernet_rebuild)
- makepowernets()
+ SSmachines.makepowernets()
defer_powernet_rebuild = 0
return 1
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 8b00612641..bf091e1180 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -80,6 +80,8 @@
var/list/sprite_sheets_obj = list()
var/toolspeed = 1.0 // This is a multipler on how 'fast' a tool works. e.g. setting this to 0.5 will make the tool work twice as fast.
+ var/attackspeed = DEFAULT_ATTACK_COOLDOWN // How long click delay will be when using this, in 1/10ths of a second. Checked in the user's get_attack_speed().
+ var/reach = 1 // Length of tiles it can reach, 1 is adjacent.
var/addblends // Icon overlay for ADD highlights when applicable.
/obj/item/New()
@@ -232,7 +234,9 @@
// apparently called whenever an item is removed from a slot, container, or anything else.
/obj/item/proc/dropped(mob/user as mob)
..()
- if(zoom) zoom() //binoculars, scope, etc
+ if(zoom)
+ zoom() //binoculars, scope, etc
+ appearance_flags &= ~NO_CLIENT_COLOR
// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
@@ -256,7 +260,7 @@
// for items that can be placed in multiple slots
// note this isn't called during the initial dressing of a player
/obj/item/proc/equipped(var/mob/user, var/slot)
- layer = 20
+ hud_layerise()
if(user.client) user.client.screen |= src
if(user.pulling == src) user.stop_pulling()
return
@@ -457,7 +461,7 @@ var/list/global/slot_flags_enumeration = list(
M.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])"
msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (JMP)") //BS12 EDIT ALG
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed())
user.do_attack_animation(M)
src.add_fingerprint(user)
@@ -640,3 +644,15 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
/obj/item/proc/pwr_drain()
return 0 // Process Kill
+// Used for non-adjacent melee attacks with specific weapons capable of reaching more than one tile.
+// This uses changeling range string A* but for this purpose its also applicable.
+/obj/item/proc/attack_can_reach(var/atom/us, var/atom/them, var/range)
+ if(us.Adjacent(them))
+ return TRUE // Already adjacent.
+ if(AStar(get_turf(us), get_turf(them), /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=range))
+ return TRUE
+ return FALSE
+
+// Check if an object should ignite others, like a lit lighter or candle.
+/obj/item/proc/is_hot()
+ return FALSE
\ No newline at end of file
diff --git a/code/game/objects/items/antag_spawners.dm b/code/game/objects/items/antag_spawners.dm
index ce358bc1ee..100c113994 100644
--- a/code/game/objects/items/antag_spawners.dm
+++ b/code/game/objects/items/antag_spawners.dm
@@ -12,7 +12,7 @@
sparks.attach(loc)
/obj/item/weapon/antag_spawner/Destroy()
- qdel(sparks)
+ qdel_null(sparks)
return ..()
/obj/item/weapon/antag_spawner/proc/spawn_antag(client/C, turf/T)
diff --git a/code/game/objects/items/contraband_vr.dm b/code/game/objects/items/contraband_vr.dm
index 944585ae92..399be72e30 100644
--- a/code/game/objects/items/contraband_vr.dm
+++ b/code/game/objects/items/contraband_vr.dm
@@ -73,7 +73,7 @@
/obj/item/weapon/reagent_containers/food/snacks/clownstears,
/obj/item/weapon/reagent_containers/food/snacks/xenomeat,
/obj/item/weapon/reagent_containers/glass/beaker/neurotoxin,
- /obj/item/weapon/rig/combat/equipped,
+ /obj/item/weapon/rig/combat,
/obj/item/weapon/shield/energy,
/obj/item/weapon/stamp/centcomm,
/obj/item/weapon/stamp/solgov,
diff --git a/code/game/objects/items/devices/advnifrepair.dm b/code/game/objects/items/devices/advnifrepair.dm
new file mode 100644
index 0000000000..987bee8b37
--- /dev/null
+++ b/code/game/objects/items/devices/advnifrepair.dm
@@ -0,0 +1,64 @@
+//Programs nanopaste into NIF repair nanites
+/obj/item/device/nifrepairer
+ name = "advanced NIF repair tool"
+ desc = "A tool that accepts nanopaste and converts the nanites into NIF repair nanites for injection/ingestion. Insert paste, deposit into container."
+ icon = 'icons/obj/device_alt.dmi'
+ icon_state = "hydro"
+ item_state = "gun"
+ flags = CONDUCT
+ slot_flags = SLOT_BELT
+ throwforce = 3
+ w_class = ITEMSIZE_SMALL
+ throw_speed = 5
+ throw_range = 10
+ matter = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 6000)
+ origin_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5)
+ var/datum/reagents/supply
+ var/efficiency = 15 //How many units reagent per 1 unit nanopaste
+
+
+/obj/item/device/nifrepairer/New()
+ ..()
+
+ supply = new(max = 60, A = src)
+
+/obj/item/device/nifrepairer/attackby(obj/W, mob/user)
+ if(istype(W,/obj/item/stack/nanopaste))
+ var/obj/item/stack/nanopaste/np = W
+ if(np.use(1) && supply.get_free_space() >= efficiency)
+ to_chat(user,"You convert some nanopaste into programmed nanites inside \the [src].")
+ supply.add_reagent(id = "nifrepairnanites", amount = efficiency)
+ update_icon()
+ else if(supply.get_free_space() < efficiency)
+ to_chat(user,"\The [src] is too full. Empty it into a container first.")
+ return
+
+/obj/item/device/nifrepairer/update_icon()
+ if(supply.total_volume)
+ icon_state = "[initial(icon_state)]2"
+ else
+ icon_state = initial(icon_state)
+
+/obj/item/device/nifrepairer/afterattack(var/atom/target, var/mob/user, var/proximity)
+ if(!target.is_open_container() || !target.reagents)
+ return 0
+
+ if(!supply || !supply.total_volume)
+ to_chat(user,"[src] is empty. Feed it nanopaste.")
+ return 1
+
+ if(!target.reagents.get_free_space())
+ user << "[target] is already full."
+ return 1
+
+ var/trans = supply.trans_to(target, 15)
+ to_chat(user,"You transfer [trans] units of the programmed nanites to [target].")
+ update_icon()
+ return 1
+
+/obj/item/device/nifrepairer/examine(mob/user)
+ if(..(user, 1))
+ if(supply.total_volume)
+ to_chat(user,"\The [src] contains [supply.total_volume] units of programmed nanites, ready for dispensing.")
+ else
+ to_chat(user,"\The [src] is empty and ready to accept nanopaste.")
diff --git a/code/game/objects/items/devices/body_snatcher_vr.dm b/code/game/objects/items/devices/body_snatcher_vr.dm
index 053d5dbff3..9e673dfe9c 100644
--- a/code/game/objects/items/devices/body_snatcher_vr.dm
+++ b/code/game/objects/items/devices/body_snatcher_vr.dm
@@ -33,7 +33,7 @@
var/choice = alert(usr,"This will swap your mind with the target's mind. This will result in them controlling your body, and you controlling their body. Continue?","Confirmation","Continue","Cancel")
if(choice == "Continue" && usr.get_active_hand() == src && usr.Adjacent(M))
- usr.visible_message("[usr] pushes the device up his forehead and [M]'s head, the device beginning to let out a series of light beeps!","You begin swap minds with [M]!")
+ usr.visible_message("[usr] pushes the device up their forehead and [M]'s head, the device beginning to let out a series of light beeps!","You begin swap minds with [M]!")
if(do_after(usr,35 SECONDS,M))
if(usr.mind && M.mind && M.stat != DEAD && usr.stat != DEAD)
log_and_message_admins("[usr.ckey] used a Bodysnatcher to swap bodies with [M.ckey]")
diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm
new file mode 100644
index 0000000000..b54c879d68
--- /dev/null
+++ b/code/game/objects/items/devices/communicator/UI.dm
@@ -0,0 +1,254 @@
+// Proc: ui_interact()
+// Parameters: 4 (standard NanoUI arguments)
+// Description: Uses a bunch of for loops to turn lists into lists of lists, so they can be displayed in nanoUI, then displays various buttons to the user.
+/obj/item/device/communicator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/key_state = null)
+ // this is the data which will be sent to the ui
+ var/data[0] //General nanoUI information
+ var/communicators[0] //List of communicators
+ var/invites[0] //Communicators and ghosts we've invited to our communicator.
+ var/requests[0] //Communicators and ghosts wanting to go in our communicator.
+ var/voices[0] //Current /mob/living/voice s inside the device.
+ var/connected_communicators[0] //Current communicators connected to the device.
+
+ var/im_contacts_ui[0] //List of communicators that have been messaged.
+ var/im_list_ui[0] //List of messages.
+
+ var/weather[0]
+ var/injection = null
+ var/modules_ui[0] //Home screen info.
+
+ //First we add other 'local' communicators.
+ for(var/obj/item/device/communicator/comm in known_devices)
+ if(comm.network_visibility && comm.exonet)
+ communicators[++communicators.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address)
+
+ //Now for ghosts who we pretend have communicators.
+ for(var/mob/observer/dead/O in known_devices)
+ if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet)
+ communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
+
+ //Lists all the other communicators that we invited.
+ for(var/obj/item/device/communicator/comm in voice_invites)
+ if(comm.exonet)
+ invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
+
+ //Ghosts we invited.
+ for(var/mob/observer/dead/O in voice_invites)
+ if(O.exonet && O.client)
+ invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
+
+ //Communicators that want to talk to us.
+ for(var/obj/item/device/communicator/comm in voice_requests)
+ if(comm.exonet)
+ requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
+
+ //Ghosts that want to talk to us.
+ for(var/mob/observer/dead/O in voice_requests)
+ if(O.exonet && O.client)
+ requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
+
+ //Now for all the voice mobs inside the communicator.
+ for(var/mob/living/voice/voice in contents)
+ voices[++voices.len] = list("name" = sanitize("[voice.name]'s communicator"), "true_name" = sanitize(voice.name))
+
+ //Finally, all the communicators linked to this one.
+ for(var/obj/item/device/communicator/comm in communicating)
+ connected_communicators[++connected_communicators.len] = list("name" = sanitize(comm.name), "true_name" = sanitize(comm.name), "ref" = "\ref[comm]")
+
+ //Devices that have been messaged or recieved messages from.
+ for(var/obj/item/device/communicator/comm in im_contacts)
+ if(comm.exonet)
+ im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
+
+ for(var/mob/observer/dead/ghost in im_contacts)
+ if(ghost.exonet)
+ im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(ghost.name), "address" = ghost.exonet.address, "ref" = "\ref[ghost]")
+
+ //Actual messages.
+ for(var/I in im_list)
+ im_list_ui[++im_list_ui.len] = list("address" = I["address"], "to_address" = I["to_address"], "im" = I["im"])
+
+ //Weather reports.
+ for(var/datum/planet/planet in planet_controller.planets)
+ if(planet.weather_holder && planet.weather_holder.current_weather)
+ var/list/W = list(
+ "Planet" = planet.name,
+ "Time" = planet.current_time.show_time("hh:mm"),
+ "Weather" = planet.weather_holder.current_weather.name,
+ "Temperature" = planet.weather_holder.temperature - T0C,
+ "High" = planet.weather_holder.current_weather.temp_high - T0C,
+ "Low" = planet.weather_holder.current_weather.temp_low - T0C)
+ weather[++weather.len] = W
+
+ injection = "Test
"
+
+ //Modules for homescreen.
+ for(var/list/R in modules)
+ modules_ui[++modules_ui.len] = R
+
+ data["owner"] = owner ? owner : "Unset"
+ data["occupation"] = occupation ? occupation : "Swipe ID to set."
+ data["connectionStatus"] = get_connection_to_tcomms()
+ data["visible"] = network_visibility
+ data["address"] = exonet.address ? exonet.address : "Unallocated"
+ data["targetAddress"] = target_address
+ data["targetAddressName"] = target_address_name
+ data["currentTab"] = selected_tab
+ data["knownDevices"] = communicators
+ data["invitesSent"] = invites
+ data["requestsReceived"] = requests
+ data["voice_mobs"] = voices
+ data["communicating"] = connected_communicators
+ data["video_comm"] = video_source ? "\ref[video_source.loc]" : null
+ data["imContacts"] = im_contacts_ui
+ data["imList"] = im_list_ui
+ data["time"] = stationtime2text()
+ data["ring"] = ringer
+ data["homeScreen"] = modules_ui
+ data["note"] = note // current notes
+ data["weather"] = weather
+ data["aircontents"] = src.analyze_air()
+ data["flashlight"] = fon
+ data["injection"] = injection
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if(!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "communicator.tmpl", "Communicator", 475, 700, state = key_state)
+ // when the ui is first opened this is the data it will use
+ ui.set_initial_data(data)
+ // open the new ui window
+ ui.open()
+ // auto update every five Master Controller tick
+ ui.set_auto_update(5)
+
+// Proc: Topic()
+// Parameters: 2 (standard Topic arguments)
+// Description: Responds to NanoUI button presses.
+/obj/item/device/communicator/Topic(href, href_list)
+ if(..())
+ return 1
+ if(href_list["rename"])
+ var/new_name = sanitizeSafe(input(usr,"Please enter your name.","Communicator",usr.name) )
+ if(new_name)
+ owner = new_name
+ name = "[owner]'s [initial(name)]"
+ if(camera)
+ camera.name = name
+ camera.c_tag = name
+
+ if(href_list["toggle_visibility"])
+ switch(network_visibility)
+ if(1) //Visible, becoming invisbile
+ network_visibility = 0
+ if(camera)
+ camera.remove_network(NETWORK_COMMUNICATORS)
+ if(0) //Invisible, becoming visible
+ network_visibility = 1
+ if(camera)
+ camera.add_network(NETWORK_COMMUNICATORS)
+
+ if(href_list["toggle_ringer"])
+ ringer = !ringer
+
+ if(href_list["add_hex"])
+ var/hex = href_list["add_hex"]
+ add_to_EPv2(hex)
+
+ if(href_list["write_target_address"])
+ var/new_address = sanitizeSafe(input(usr,"Please enter the desired target EPv2 address. Note that you must write the colons \
+ yourself.","Communicator",src.target_address) )
+ if(new_address)
+ target_address = new_address
+
+ if(href_list["clear_target_address"])
+ target_address = ""
+
+ if(href_list["dial"])
+ if(!get_connection_to_tcomms())
+ usr << "Error: Cannot connect to Exonet node."
+ return
+ var/their_address = href_list["dial"]
+ exonet.send_message(their_address, "voice")
+
+ if(href_list["decline"])
+ var/ref_to_remove = href_list["decline"]
+ var/atom/decline = locate(ref_to_remove)
+ if(decline)
+ del_request(decline)
+
+ if(href_list["message"])
+ if(!get_connection_to_tcomms())
+ usr << "Error: Cannot connect to Exonet node."
+ return
+ var/their_address = href_list["message"]
+ var/text = sanitizeSafe(input(usr,"Enter your message.","Text Message"))
+ if(text)
+ exonet.send_message(their_address, "text", text)
+ im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text))
+ log_pda("[usr] (COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]")
+ for(var/mob/M in player_list)
+ if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
+ if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat)
+ continue
+ if(exonet.get_atom_from_address(their_address) == M)
+ continue
+ M.show_message("Comm IM - [src] -> [exonet.get_atom_from_address(their_address)]: [text]")
+
+ if(href_list["disconnect"])
+ var/name_to_disconnect = href_list["disconnect"]
+ for(var/mob/living/voice/V in contents)
+ if(name_to_disconnect == V.name)
+ close_connection(usr, V, "[usr] hung up")
+ for(var/obj/item/device/communicator/comm in communicating)
+ if(name_to_disconnect == comm.name)
+ close_connection(usr, comm, "[usr] hung up")
+
+ if(href_list["startvideo"])
+ var/ref_to_video = href_list["startvideo"]
+ var/obj/item/device/communicator/comm = locate(ref_to_video)
+ if(comm)
+ connect_video(usr, comm)
+
+ if(href_list["endvideo"])
+ if(video_source)
+ end_video()
+
+ if(href_list["watchvideo"])
+ if(video_source)
+ watch_video(usr,video_source.loc)
+
+ if(href_list["copy"])
+ target_address = href_list["copy"]
+
+ if(href_list["copy_name"])
+ target_address_name = href_list["copy_name"]
+
+ if(href_list["hang_up"])
+ for(var/mob/living/voice/V in contents)
+ close_connection(usr, V, "[usr] hung up")
+ for(var/obj/item/device/communicator/comm in communicating)
+ close_connection(usr, comm, "[usr] hung up")
+
+ if(href_list["switch_tab"])
+ selected_tab = href_list["switch_tab"]
+
+ if(href_list["edit"])
+ var/n = input(usr, "Please enter message", name, notehtml)
+ n = sanitizeSafe(n, extra = 0)
+ if(n)
+ note = html_decode(n)
+ notehtml = note
+ note = replacetext(note, "\n", "
")
+ else
+ note = ""
+ notehtml = note
+
+ if(href_list["Light"])
+ fon = !fon
+ set_light(fon * flum)
+
+ nanomanager.update_uis(src)
+ add_fingerprint(usr)
\ No newline at end of file
diff --git a/code/game/objects/items/devices/communicator/cartridge.dm b/code/game/objects/items/devices/communicator/cartridge.dm
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm
index 6c300bd833..ad3ad9522f 100644
--- a/code/game/objects/items/devices/communicator/communicator.dm
+++ b/code/game/objects/items/devices/communicator/communicator.dm
@@ -32,12 +32,16 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
var/notehtml = ""
var/obj/item/weapon/cartridge/cartridge = null //current cartridge
+ var/fon = 0 // Internal light
+ var/flum = 2 // Brightness
+
var/list/modules = list(
list("module" = "Phone", "icon" = "phone64", "number" = 2),
list("module" = "Contacts", "icon" = "person64", "number" = 3),
list("module" = "Messaging", "icon" = "comment64", "number" = 4),
list("module" = "Note", "icon" = "note64", "number" = 5),
- list("module" = "Settings", "icon" = "gear64", "number" = 6)
+ list("module" = "Weather", "icon" = "sun64", "number" = 6),
+ list("module" = "Settings", "icon" = "gear64", "number" = 7)
) //list("module" = "Name of Module", "icon" = "icon name64", "number" = "what tab is the module")
var/selected_tab = 1
@@ -191,16 +195,19 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(istype(C, /obj/item/weapon/card/id))
var/obj/item/weapon/card/id/idcard = C
if(!idcard.registered_name || !idcard.assignment)
- user << "\The [src] rejects the ID."
- return
- if(!owner)
- user << "\The [src] rejects the ID."
- return
- if(owner == idcard.registered_name)
+ to_chat(user, "\The [src] rejects the ID.")
+ else if(!owner)
+ to_chat(user, "\The [src] rejects the ID.")
+ else if(owner == idcard.registered_name)
occupation = idcard.assignment
- user << "Occupation updated."
- return
- else return
+ to_chat(user, "Occupation updated.")
+// else if(istype(C, /obj/item/weapon/cartridge))
+// if(cartridge)
+// to_chat(user, "\The [src] already has an external device attached!")
+// else
+// modules.Add(list("module" = "External Device", "icon = external64", "number" = 8))
+// cartridge = C
+ return
// Proc: attack_self()
// Parameters: 1 (user - the mob that clicked the device in their hand)
@@ -258,281 +265,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
exonet = null
return ..()
-// Proc: ui_interact()
-// Parameters: 4 (standard NanoUI arguments)
-// Description: Uses a bunch of for loops to turn lists into lists of lists, so they can be displayed in nanoUI, then displays various buttons to the user.
-/obj/item/device/communicator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/key_state = null)
- // this is the data which will be sent to the ui
- var/data[0] //General nanoUI information
- var/communicators[0] //List of communicators
- var/invites[0] //Communicators and ghosts we've invited to our communicator.
- var/requests[0] //Communicators and ghosts wanting to go in our communicator.
- var/voices[0] //Current /mob/living/voice s inside the device.
- var/connected_communicators[0] //Current communicators connected to the device.
-
- var/im_contacts_ui[0] //List of communicators that have been messaged.
- var/im_list_ui[0] //List of messages.
-
- var/modules_ui[0] //Home screen info.
-
- //First we add other 'local' communicators.
- for(var/obj/item/device/communicator/comm in known_devices)
- if(comm.network_visibility && comm.exonet)
- communicators[++communicators.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address)
-
- //Now for ghosts who we pretend have communicators.
- for(var/mob/observer/dead/O in known_devices)
- if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet)
- communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
-
- //Lists all the other communicators that we invited.
- for(var/obj/item/device/communicator/comm in voice_invites)
- if(comm.exonet)
- invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
-
- //Ghosts we invited.
- for(var/mob/observer/dead/O in voice_invites)
- if(O.exonet && O.client)
- invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
-
- //Communicators that want to talk to us.
- for(var/obj/item/device/communicator/comm in voice_requests)
- if(comm.exonet)
- requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
-
- //Ghosts that want to talk to us.
- for(var/mob/observer/dead/O in voice_requests)
- if(O.exonet && O.client)
- requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
-
- //Now for all the voice mobs inside the communicator.
- for(var/mob/living/voice/voice in contents)
- voices[++voices.len] = list("name" = sanitize("[voice.name]'s communicator"), "true_name" = sanitize(voice.name))
-
- //Finally, all the communicators linked to this one.
- for(var/obj/item/device/communicator/comm in communicating)
- connected_communicators[++connected_communicators.len] = list("name" = sanitize(comm.name), "true_name" = sanitize(comm.name), "ref" = "\ref[comm]")
-
- //Devices that have been messaged or recieved messages from.
- for(var/obj/item/device/communicator/comm in im_contacts)
- if(comm.exonet)
- im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
-
- for(var/mob/observer/dead/ghost in im_contacts)
- if(ghost.exonet)
- im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(ghost.name), "address" = ghost.exonet.address, "ref" = "\ref[ghost]")
-
- //Actual messages.
- for(var/I in im_list)
- im_list_ui[++im_list_ui.len] = list("address" = I["address"], "to_address" = I["to_address"], "im" = I["im"])
-
- //Modules for homescreen.
- for(var/list/R in modules)
- modules_ui[++modules_ui.len] = R
-
- data["owner"] = owner ? owner : "Unset"
- data["occupation"] = occupation ? occupation : "Swipe ID to set."
- data["connectionStatus"] = get_connection_to_tcomms()
- data["visible"] = network_visibility
- data["address"] = exonet.address ? exonet.address : "Unallocated"
- data["targetAddress"] = target_address
- data["targetAddressName"] = target_address_name
- data["currentTab"] = selected_tab
- data["knownDevices"] = communicators
- data["invitesSent"] = invites
- data["requestsReceived"] = requests
- data["voice_mobs"] = voices
- data["communicating"] = connected_communicators
- data["video_comm"] = video_source ? "\ref[video_source.loc]" : null
- data["imContacts"] = im_contacts_ui
- data["imList"] = im_list_ui
- data["time"] = stationtime2text()
- data["ring"] = ringer
- data["homeScreen"] = modules_ui
- data["note"] = note // current notes
-
- // update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- // the ui does not exist, so we'll create a new() one
- // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
- ui = new(user, src, ui_key, "communicator.tmpl", "Communicator", 475, 700, state = key_state)
- // when the ui is first opened this is the data it will use
- ui.set_initial_data(data)
- // open the new ui window
- ui.open()
- // auto update every five Master Controller tick
- ui.set_auto_update(5)
-
-// Proc: Topic()
-// Parameters: 2 (standard Topic arguments)
-// Description: Responds to NanoUI button presses.
-/obj/item/device/communicator/Topic(href, href_list)
- if(..())
- return 1
- if(href_list["rename"])
- var/new_name = sanitizeSafe(input(usr,"Please enter your name.","Communicator",usr.name) )
- if(new_name)
- owner = new_name
- name = "[owner]'s [initial(name)]"
- if(camera)
- camera.name = name
- camera.c_tag = name
-
- if(href_list["toggle_visibility"])
- switch(network_visibility)
- if(1) //Visible, becoming invisbile
- network_visibility = 0
- if(camera)
- camera.remove_network(NETWORK_COMMUNICATORS)
- if(0) //Invisible, becoming visible
- network_visibility = 1
- if(camera)
- camera.add_network(NETWORK_COMMUNICATORS)
-
- if(href_list["toggle_ringer"])
- ringer = !ringer
-
- if(href_list["add_hex"])
- var/hex = href_list["add_hex"]
- add_to_EPv2(hex)
-
- if(href_list["write_target_address"])
- var/new_address = sanitizeSafe(input(usr,"Please enter the desired target EPv2 address. Note that you must write the colons \
- yourself.","Communicator",src.target_address) )
- if(new_address)
- target_address = new_address
-
- if(href_list["clear_target_address"])
- target_address = ""
-
- if(href_list["dial"])
- if(!get_connection_to_tcomms())
- usr << "Error: Cannot connect to Exonet node."
- return
- var/their_address = href_list["dial"]
- exonet.send_message(their_address, "voice")
-
- if(href_list["decline"])
- var/ref_to_remove = href_list["decline"]
- var/atom/decline = locate(ref_to_remove)
- if(decline)
- del_request(decline)
-
- if(href_list["message"])
- if(!get_connection_to_tcomms())
- usr << "Error: Cannot connect to Exonet node."
- return
- var/their_address = href_list["message"]
- var/text = sanitizeSafe(input(usr,"Enter your message.","Text Message"))
- if(text)
- exonet.send_message(their_address, "text", text)
- im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text))
- log_pda("[usr] (COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]")
-
- if(href_list["disconnect"])
- var/name_to_disconnect = href_list["disconnect"]
- for(var/mob/living/voice/V in contents)
- if(name_to_disconnect == V.name)
- close_connection(usr, V, "[usr] hung up")
- for(var/obj/item/device/communicator/comm in communicating)
- if(name_to_disconnect == comm.name)
- close_connection(usr, comm, "[usr] hung up")
-
- if(href_list["startvideo"])
- var/ref_to_video = href_list["startvideo"]
- var/obj/item/device/communicator/comm = locate(ref_to_video)
- if(comm)
- connect_video(usr, comm)
-
- if(href_list["endvideo"])
- if(video_source)
- end_video()
-
- if(href_list["watchvideo"])
- if(video_source)
- watch_video(usr,video_source.loc)
-
- if(href_list["copy"])
- target_address = href_list["copy"]
-
- if(href_list["copy_name"])
- target_address_name = href_list["copy_name"]
-
- if(href_list["hang_up"])
- for(var/mob/living/voice/V in contents)
- close_connection(usr, V, "[usr] hung up")
- for(var/obj/item/device/communicator/comm in communicating)
- close_connection(usr, comm, "[usr] hung up")
-
- if(href_list["switch_tab"])
- selected_tab = href_list["switch_tab"]
-
- if(href_list["edit"])
- var/n = input(usr, "Please enter message", name, notehtml)
- n = sanitizeSafe(n, extra = 0)
- if(n)
- note = html_decode(n)
- notehtml = note
- note = replacetext(note, "\n", "
")
- else
- note = ""
- notehtml = note
-
- nanomanager.update_uis(src)
- add_fingerprint(usr)
-
-// Proc: receive_exonet_message()
-// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received,
-// text - message text to send if message is of type "text")
-// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function.
-/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text)
- if(message == "voice")
- if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator))
- if(origin_atom in voice_invites)
- var/user = null
- if(ismob(origin_atom.loc))
- user = origin_atom.loc
- open_connection(user, origin_atom)
- return
- else if(origin_atom in voice_requests)
- return //Spam prevention
- else
- request(origin_atom)
- if(message == "ping")
- if(network_visibility)
- var/random = rand(200,350)
- random = random / 10
- exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms")
- if(message == "text")
- request_im(origin_atom, origin_address, text)
- return
-
-// Proc: receive_exonet_message()
-// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received)
-// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response.
-/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text)
- if(message == "voice")
- if(istype(origin_atom, /obj/item/device/communicator))
- var/obj/item/device/communicator/comm = origin_atom
- if(src in comm.voice_invites)
- comm.open_connection(src)
- return
- src << "\icon[origin_atom] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \
- verb, and select that name to answer the call."
- src << 'sound/machines/defib_SafetyOn.ogg'
- comm.voice_invites |= src
- if(message == "ping")
- if(client && client.prefs.communicator_visibility)
- var/random = rand(450,700)
- random = random / 10
- exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms")
- if(message == "text")
- src << "\icon[origin_atom] Received text message from [origin_atom]: \"[text]\""
- src << 'sound/machines/defib_safetyOff.ogg'
- exonet_messages.Add("From [origin_atom]:
[text]")
- return
-
// Proc: register_device()
// Parameters: 1 (user - the person to use their name for)
// Description: Updates the owner's name and the device's name.
@@ -546,252 +278,13 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
camera.name = name
camera.c_tag = name
-// Proc: add_communicating()
-// Parameters: 1 (comm - the communicator to add to communicating)
-// Description: Used when this communicator gets a new communicator to relay say/me messages to
-/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm)
- if(!comm || !istype(comm)) return
-
- communicating |= comm
- listening_objects |= src
- update_icon()
-
-// Proc: del_communicating()
-// Parameters: 1 (comm - the communicator to remove from communicating)
-// Description: Used when this communicator is being asked to stop relaying say/me messages to another
-/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm)
- if(!comm || !istype(comm)) return
-
- communicating.Remove(comm)
- update_icon()
-
-// Proc: open_connection()
-// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device)
-// Description: Typechecks the candidate, then calls the correct proc for further connecting.
-/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate)
- if(isobserver(candidate))
- voice_invites.Remove(candidate)
- open_connection_to_ghost(user, candidate)
- else
- if(istype(candidate, /obj/item/device/communicator))
- open_connection_to_communicator(user, candidate)
-
-// Proc: open_connection_to_communicator()
-// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator)
-// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed.
-/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate)
- if(!istype(candidate, /obj/item/device/communicator))
- return
- var/obj/item/device/communicator/comm = candidate
- voice_invites.Remove(candidate)
- comm.voice_requests.Remove(src)
-
- if(user)
- comm.visible_message("\icon[src] Connecting to [src].")
- user << "\icon[src] Attempting to call [comm]."
- sleep(10)
- user << "\icon[src] Dialing internally from [station_name()], [system_name()]." // Vorestation edit
- sleep(20) //If they don't have an exonet something is very wrong and we want a runtime.
- user << "\icon[src] Connection re-routed to [comm] at [comm.exonet.address]."
- sleep(40)
- user << "\icon[src] Connection to [comm] at [comm.exonet.address] established."
- comm.visible_message("\icon[src] Connection to [src] at [exonet.address] established.")
- sleep(20)
-
- src.add_communicating(comm)
- comm.add_communicating(src)
-
-// Proc: open_connection_to_ghost()
-// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob)
-// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client.
-/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate)
- if(!isobserver(candidate))
- return
- //Handle moving the ghost into the new shell.
- announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.")
- voice_requests.Remove(candidate)
- voice_invites.Remove(candidate)
- var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be.
- new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences.
- //Do some simple logging since this is a tad risky as a concept.
- var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \
- [user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]."
- message_admins(msg)
- log_game(msg)
- new_voice.mind = candidate.mind //Transfer the mind, if any.
- new_voice.ckey = candidate.ckey //Finally, bring the client over.
- voice_mobs.Add(new_voice)
- listening_objects |= src
-
- var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator.
- blackness.screen_loc = ui_entire_screen
- blackness.icon = 'icons/effects/effects.dmi'
- blackness.icon_state = "1"
- blackness.mouse_opacity = 2 //Can't see anything!
- new_voice.client.screen.Add(blackness)
-
- update_icon()
-
- //Now for some connection fluff.
- if(user)
- user << "\icon[src] Connecting to [candidate]."
- new_voice << "\icon[src] Attempting to call [src]."
- sleep(10)
- new_voice << "\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()]."
- sleep(20)
- new_voice << "\icon[src] Connecting to [station_name()] telecommunications array."
- sleep(40)
- new_voice << "\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src]."
- sleep(20)
-
- //We're connected, no need to hide everything.
- new_voice.client.screen.Remove(blackness)
- qdel(blackness)
-
- new_voice << "\icon[src] Connection to [src] established."
- new_voice << "To talk to the person on the other end of the call, just talk normally."
- new_voice << "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time."
- new_voice << "Remember, your character does not know anything you've learned from observing!"
- if(new_voice.mind)
- new_voice.mind.assigned_role = "Disembodied Voice"
- if(user)
- user << "\icon[src] Your communicator is now connected to [candidate]'s communicator."
-
-// Proc: close_connection()
-// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected)
-// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators
-// and voice mobs are removed.
-/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason)
- if(voice_mobs.len == 0 && communicating.len == 0)
- return
-
- for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers
- if(target && voice != target) //If no target is inputted, it deletes all of them.
- continue
- voice << "\icon[src] [reason]."
- visible_message("\icon[src] [reason].")
- voice_mobs.Remove(voice)
- qdel(voice)
- update_icon()
-
- for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators.
- if(target && comm != target)
- continue
- src.del_communicating(comm)
- comm.del_communicating(src)
- comm.visible_message("\icon[src] [reason].")
- visible_message("\icon[src] [reason].")
- if(comm.camera && video_source == comm.camera) //We hung up on the person on video
- end_video()
- if(camera && comm.video_source == camera) //We hung up on them while they were watching us
- comm.end_video()
-
- if(voice_mobs.len == 0 && communicating.len == 0)
- listening_objects.Remove(src)
-
-// Proc: request()
-// Parameters: 1 (candidate - the ghost or communicator wanting to call the device)
-// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters
-/obj/item/device/communicator/proc/request(var/atom/candidate)
- if(candidate in voice_requests)
- return
- var/who = null
- if(isobserver(candidate))
- who = candidate.name
- else if(istype(candidate, /obj/item/device/communicator))
- var/obj/item/device/communicator/comm = candidate
- who = comm.owner
- comm.voice_invites |= src
-
- if(!who)
- return
-
- voice_requests |= candidate
-
- if(ringer)
- playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
- for (var/mob/O in hearers(2, loc))
- O.show_message(text("\icon[src] *beep*"))
-
- alert_called = 1
- update_icon()
-
- //Search for holder of the device.
- var/mob/living/L = null
- if(loc && isliving(loc))
- L = loc
-
- if(L)
- L << "\icon[src] Communications request from [who]."
-
-// Proc: del_request()
-// Parameters: 1 (candidate - the ghost or communicator to be declined)
-// Description: Declines a request and cleans up both ends
-/obj/item/device/communicator/proc/del_request(var/atom/candidate)
- if(!(candidate in voice_requests))
- return
-
- if(isobserver(candidate))
- candidate << "Your communicator call request was declined."
- else if(istype(candidate, /obj/item/device/communicator))
- var/obj/item/device/communicator/comm = candidate
- comm.voice_invites -= src
-
- voice_requests -= candidate
-
- //Search for holder of our device.
- var/mob/living/us = null
- if(loc && isliving(loc))
- us = loc
-
- if(us)
- us << "\icon[src] Declined request."
-
-// Proc: request_im()
-// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message)
-// Description: Response to a communicator trying to message the device.
-// Adds them to the list of people that have messaged this device and adds the message to the message list.
-/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text)
- var/who = null
- if(isobserver(candidate))
- var/mob/observer/dead/ghost = candidate
- who = ghost
- im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text))
- else if(istype(candidate, /obj/item/device/communicator))
- var/obj/item/device/communicator/comm = candidate
- who = comm.owner
- comm.im_contacts |= src
- im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text))
- else return
-
- im_contacts |= candidate
-
- if(!who)
- return
-
- if(ringer)
- playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
- for (var/mob/O in hearers(2, loc))
- O.show_message(text("\icon[src] *beep*"))
-
- alert_called = 1
- update_icon()
-
- //Search for holder of the device.
- var/mob/living/L = null
- if(loc && isliving(loc))
- L = loc
-
- if(L)
- L << "\icon[src] Message from [who]."
-
// Proc: Destroy()
// Parameters: None
// Description: Deletes all the voice mobs, disconnects all linked communicators, and cuts lists to allow successful qdel()
/obj/item/device/communicator/Destroy()
for(var/mob/living/voice/voice in contents)
voice_mobs.Remove(voice)
- voice << "\icon[src] Connection timed out with remote host."
+ to_chat(voice, "\icon[src] Connection timed out with remote host.")
qdel(voice)
close_connection(reason = "Connection timed out")
communicating.Cut()
@@ -825,306 +318,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
icon_state = initial(icon_state)
-// Proc: see_emote()
-// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents)
-// Description: Relays the emote to all linked communicators.
-/obj/item/device/communicator/see_emote(mob/living/M, text)
- var/rendered = "\icon[src] [text]"
- for(var/obj/item/device/communicator/comm in communicating)
- var/turf/T = get_turf(comm)
- if(!T) return
- var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display
- var/list/mobs_to_relay = in_range["mobs"]
-
- for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other.
- var/dst = get_dist(get_turf(mob),get_turf(comm))
- if(dst <= video_range)
- mob.show_message(rendered)
- else
- mob << "You can barely see some movement on \the [src]'s display."
-
- ..()
-
-// Proc: hear_talk()
-// Parameters: 4 (M - the mob the speech originated from, text - what is being said, verb - the word used to describe how text is being said, speaking - language
-// being used)
-// Description: Relays the speech to all linked communicators.
-/obj/item/device/communicator/hear_talk(mob/living/M, text, verb, datum/language/speaking)
- for(var/obj/item/device/communicator/comm in communicating)
-
- var/turf/T = get_turf(comm)
- if(!T) return
- var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0)
- var/list/mobs_to_relay = in_range["mobs"]
-
- for(var/mob/mob in mobs_to_relay)
- //Can whoever is hearing us understand?
- if(!mob.say_understands(M, speaking))
- if(speaking)
- text = speaking.scramble(text)
- else
- text = stars(text)
- var/name_used = M.GetVoice()
- var/rendered = null
- if(speaking) //Language being used
- rendered = "\icon[src] [name_used] [speaking.format_message(text, verb)]"
- else
- rendered = "\icon[src] [name_used] [verb], \"[text]\""
- mob.show_message(rendered, 2)
-
-// Proc: show_message()
-// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown)
-// Description: Relays the message to all linked communicators.
-/obj/item/device/communicator/show_message(msg, type, alt, alt_type)
- var/rendered = "\icon[src] [msg]"
- for(var/obj/item/device/communicator/comm in communicating)
- var/turf/T = get_turf(comm)
- if(!T) return
- var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0)
- var/list/mobs_to_relay = in_range["mobs"]
-
- for(var/mob/mob in mobs_to_relay)
- mob.show_message(rendered)
- ..()
-
-// Verb: join_as_voice()
-// Parameters: None
-// Description: Allows ghosts to call communicators, if they meet all the requirements.
-/mob/observer/dead/verb/join_as_voice()
- set category = "Ghost"
- set name = "Call Communicator"
- set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up."
-
- if(ticker.current_state < GAME_STATE_PLAYING)
- src << "The game hasn't started yet!"
- return
-
- if (!src.stat)
- return
-
- if (usr != src)
- return //something is terribly wrong
-
- var/confirm = alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? \
- This will reset your respawn timer, if someone answers.", "Join as Voice?", "Yes","No")
- if(confirm == "No")
- return
-
- for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling.
- if(src.client.prefs.real_name == L.real_name)
- src << "Your identity is already present in the game world. Please load in a different character first."
- return
-
- var/obj/machinery/exonet_node/E = get_exonet_node()
- if(!E || !E.on || !E.allow_external_communicators)
- src << "The Exonet node at telecommunications is down at the moment, or is actively blocking you, so your call can't go through."
- return
-
- var/list/choices = list()
- for(var/obj/item/device/communicator/comm in all_communicators)
- if(!comm.network_visibility || !comm.exonet || !comm.exonet.address)
- continue
- choices.Add(comm)
-
- if(!choices.len)
- src << "There are no available communicators, sorry."
- return
-
- var/choice = input(src,"Send a voice request to whom?") as null|anything in choices
- if(choice)
- var/obj/item/device/communicator/chosen_communicator = choice
- var/mob/observer/dead/O = src
- if(O.exonet)
- O.exonet.send_message(chosen_communicator.exonet.address, "voice")
-
- src << "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers."
-
-// Verb: text_communicator()
-// Parameters: None
-// Description: Allows a ghost to send a text message to a communicator.
-/mob/observer/dead/verb/text_communicator()
- set category = "Ghost"
- set name = "Text Communicator"
- set desc = "If there is a communicator available, send a text message to it."
-
- if(ticker.current_state < GAME_STATE_PLAYING)
- src << "The game hasn't started yet!"
- return
-
- if (!src.stat)
- return
-
- if (usr != src)
- return //something is terribly wrong
-
- for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling.
- if(src.client.prefs.real_name == L.real_name)
- src << "Your identity is already present in the game world. Please load in a different character first."
- return
-
- var/obj/machinery/exonet_node/E = get_exonet_node()
- if(!E || !E.on || !E.allow_external_communicators)
- src << "The Exonet node at telecommunications is down at the moment, or is actively blocking you, so your call can't go through."
- return
-
- var/list/choices = list()
- for(var/obj/item/device/communicator/comm in all_communicators)
- if(!comm.network_visibility || !comm.exonet || !comm.exonet.address)
- continue
- choices.Add(comm)
-
- if(!choices.len)
- src << "There are no available communicators, sorry."
- return
-
- var/choice = input(src,"Send a text message to whom?") as null|anything in choices
- if(choice)
- var/obj/item/device/communicator/chosen_communicator = choice
- var/mob/observer/dead/O = src
- var/text_message = sanitize(input(src, "What do you want the message to say?")) as message
- if(text_message && O.exonet)
- O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message)
-
- src << "You have sent '[text_message]' to [chosen_communicator]."
- exonet_messages.Add("To [chosen_communicator]:
[text_message]")
- log_pda("[usr] (COMM: [src]) sent \"[text_message]\" to [chosen_communicator]")
-
-
-// Verb: show_text_messages()
-// Parameters: None
-// Description: Lets ghosts review messages they've sent or received.
-/mob/observer/dead/verb/show_text_messages()
- set category = "Ghost"
- set name = "Show Text Messages"
- set desc = "Allows you to see exonet text messages you've sent and received."
-
- var/HTML = "Exonet Message Log"
- for(var/line in exonet_messages)
- HTML += line + "
"
- HTML +=""
- usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0")
-
-// Proc: connect_video()
-// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end
-// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon
-/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm)
- if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video
-
- if(video_source) //Already in a video
- user << "You are already connected to a video call!"
- return
-
- if(user.blinded) //User is blinded
- user << "You cannot see well enough to do that!"
- return
-
- if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something
- user << "\icon[src]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning."
- return
-
- var/turf/t1 = get_turf(src)
- var/turf/t2 = get_turf(comm)
- if(!is_on_same_plane_or_station(t1.z, t2.z) || !video_source.can_use())
- user << "Request to establish video timed out!"
- return
-
- user << "\icon[src] Attempting to start video over existing call."
- sleep(30)
- user << "\icon[src] Please wait..."
-
- video_source = comm.camera
- comm.visible_message("\icon[src] New video connection from [comm].")
- watch_video(user)
- update_icon()
-
-// Proc: watch_video()
-// Parameters: user - the mob doing the viewing of video
-// Description: Moves a mob's eye to the far end for the duration of viewing the far end
-/obj/item/device/communicator/proc/watch_video(mob/user)
- if(!Adjacent(user) || !video_source) return
- user.set_machine(video_source)
- user.reset_view(video_source)
- to_chat(user,"Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View")
- to_chat(user,"To return to an active video session, use the communicator in your hand.")
- spawn(0)
- while(user.machine == video_source && (Adjacent(user) || loc == user))
- var/turf/T = get_turf(video_source)
- if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use())
- user << "The screen bursts into static, then goes black."
- video_cleanup(user)
- return
- sleep(10)
-
- video_cleanup(user)
-
-// Proc: video_cleanup()
-// Parameters: user - the mob who doesn't want to see video anymore
-// Description: Cleans up mob's client when they stop watching a video
-/obj/item/device/communicator/proc/video_cleanup(mob/user)
- if(!user) return
-
- user.reset_view(null)
- user.unset_machine()
-
-// Proc: end_video()
-// Parameters: reason - the text reason to print for why it ended
-// Description: Ends the video call by clearing video_source
-/obj/item/device/communicator/proc/end_video(var/reason)
- video_source = null
-
- . = "\icon[src] [reason ? reason : "Video session ended"]."
-
- visible_message(.)
- update_icon()
-
-//For synths who have no hands.
-/obj/item/device/communicator/integrated
- name = "integrated communicator"
- desc = "A circuit used for long-range communications, able to be integrated into a system."
-
-//A stupid hack because synths don't use languages properly or something.
-//I don't want to go digging in saycode for a week, so BS it as translation software or something.
-
-// Proc: open_connection_to_ghost()
-// Parameters: 2 (refer to base definition for arguments)
-// Description: Synths don't use languages properly, so this is a bandaid fix until that can be resolved..
-/obj/item/device/communicator/integrated/open_connection_to_ghost(user, candidate)
- ..(user, candidate)
- spawn(1)
- for(var/mob/living/voice/V in contents)
- V.universal_speak = 1
- V.universal_understand = 1
-
-// Verb: activate()
-// Parameters: None
-// Description: Lets synths use their communicators without hands.
-/obj/item/device/communicator/integrated/verb/activate()
- set category = "AI IM"
- set name = "Use Communicator"
- set desc = "Utilizes your built-in communicator."
- set src in usr
-
- if(usr.stat == 2)
- usr << "You can't do that because you are dead!"
- return
-
- src.attack_self(usr)
-
-// Verb: activate()
-// Parameters: None
-// Description: Lets synths use their communicators without hands.
-/obj/item/device/communicator/integrated/verb/see_video()
- set category = "AI IM"
- set name = "View Comm. Video"
- set desc = "Utilizes your built-in communicator."
- set src in usr
-
- if(usr.stat == 2)
- usr << "You can't do that because you are dead!"
- return
-
- src.watch_video(usr)
-
// A camera preset for spawning in the communicator
/obj/machinery/camera/communicator
network = list(NETWORK_COMMUNICATORS)
@@ -1133,3 +326,28 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
..()
client_huds |= global_hud.whitense
client_huds |= global_hud.darkMask
+
+//It's the 26th century. We should have smart watches by now.
+/obj/item/device/communicator/watch
+ name = "communicator watch"
+ desc = "A personal device used to enable long range dialog between two people, utilizing existing telecommunications infrastructure to allow \
+ communications across different stations, planets, or even star systems. You can wear this one on your wrist!"
+ icon = 'icons/obj/device.dmi'
+ icon_state = "commwatch"
+ slot_flags = SLOT_GLOVES
+
+/obj/item/device/communicator/watch/update_icon()
+ if(video_source)
+ icon_state = "commwatch-video"
+ return
+
+ if(voice_mobs.len || communicating.len)
+ icon_state = "commwatch-active"
+ return
+
+ if(alert_called)
+ icon_state = "commwatch-called"
+ return
+
+ icon_state = initial(icon_state)
+
diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm
new file mode 100644
index 0000000000..9c121112ac
--- /dev/null
+++ b/code/game/objects/items/devices/communicator/helper.dm
@@ -0,0 +1,27 @@
+/obj/item/device/communicator/proc/analyze_air()
+ var/list/results = list()
+ var/turf/T = get_turf(src.loc)
+ if(!isnull(T))
+ var/datum/gas_mixture/environment = T.return_air()
+ var/pressure = environment.return_pressure()
+ var/total_moles = environment.total_moles
+ if (total_moles)
+ var/o2_level = environment.gas["oxygen"]/total_moles
+ var/n2_level = environment.gas["nitrogen"]/total_moles
+ var/co2_level = environment.gas["carbon_dioxide"]/total_moles
+ var/phoron_level = environment.gas["phoron"]/total_moles
+ var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level)
+ results = list(
+ "pressure" = "[round(pressure,0.1)]",
+ "nitrogen" = "[round(n2_level*100,0.1)]",
+ "oxygen" = "[round(o2_level*100,0.1)]",
+ "carbon_dioxide" = "[round(co2_level*100,0.1)]",
+ "phoron" = "[round(phoron_level*100,0.01)]",
+ "other" = "[round(unknown_level, 0.01)]",
+ "temp" = "[round(environment.temperature-T0C,0.1)]",
+ "reading" = 1
+ )
+
+ if(isnull(results))
+ results = list("reading" = 0)
+ return results
\ No newline at end of file
diff --git a/code/game/objects/items/devices/communicator/integrated.dm b/code/game/objects/items/devices/communicator/integrated.dm
new file mode 100644
index 0000000000..a35d642632
--- /dev/null
+++ b/code/game/objects/items/devices/communicator/integrated.dm
@@ -0,0 +1,32 @@
+//For synths who have no hands.
+/obj/item/device/communicator/integrated
+ name = "integrated communicator"
+ desc = "A circuit used for long-range communications, able to be integrated into a system."
+
+//A stupid hack because synths don't use languages properly or something.
+//I don't want to go digging in saycode for a week, so BS it as translation software or something.
+
+// Proc: open_connection_to_ghost()
+// Parameters: 2 (refer to base definition for arguments)
+// Description: Synths don't use languages properly, so this is a bandaid fix until that can be resolved..
+/obj/item/device/communicator/integrated/open_connection_to_ghost(user, candidate)
+ ..(user, candidate)
+ spawn(1)
+ for(var/mob/living/voice/V in contents)
+ V.universal_speak = 1
+ V.universal_understand = 1
+
+// Verb: activate()
+// Parameters: None
+// Description: Lets synths use their communicators without hands.
+/obj/item/device/communicator/integrated/verb/activate()
+ set category = "AI IM"
+ set name = "Use Communicator"
+ set desc = "Utilizes your built-in communicator."
+ set src in usr
+
+ if(usr.stat == 2)
+ to_chat(usr, "You can't do that because you are dead!")
+ return
+
+ src.attack_self(usr)
\ No newline at end of file
diff --git a/code/game/objects/items/devices/communicator/messaging.dm b/code/game/objects/items/devices/communicator/messaging.dm
new file mode 100644
index 0000000000..c775a70499
--- /dev/null
+++ b/code/game/objects/items/devices/communicator/messaging.dm
@@ -0,0 +1,162 @@
+// Proc: receive_exonet_message()
+// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received,
+// text - message text to send if message is of type "text")
+// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function.
+/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text)
+ if(message == "voice")
+ if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator))
+ if(origin_atom in voice_invites)
+ var/user = null
+ if(ismob(origin_atom.loc))
+ user = origin_atom.loc
+ open_connection(user, origin_atom)
+ return
+ else if(origin_atom in voice_requests)
+ return //Spam prevention
+ else
+ request(origin_atom)
+ if(message == "ping")
+ if(network_visibility)
+ var/random = rand(200,350)
+ random = random / 10
+ exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms")
+ if(message == "text")
+ request_im(origin_atom, origin_address, text)
+ return
+
+// Proc: receive_exonet_message()
+// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received)
+// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response.
+/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text)
+ if(message == "voice")
+ if(istype(origin_atom, /obj/item/device/communicator))
+ var/obj/item/device/communicator/comm = origin_atom
+ if(src in comm.voice_invites)
+ comm.open_connection(src)
+ return
+ to_chat(src, "\icon[origin_atom] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \
+ verb, and select that name to answer the call.")
+ src << 'sound/machines/defib_SafetyOn.ogg'
+ comm.voice_invites |= src
+ if(message == "ping")
+ if(client && client.prefs.communicator_visibility)
+ var/random = rand(450,700)
+ random = random / 10
+ exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms")
+ if(message == "text")
+ to_chat(src, "\icon[origin_atom] Received text message from [origin_atom]: \"[text]\"")
+ src << 'sound/machines/defib_safetyOff.ogg'
+ exonet_messages.Add("From [origin_atom]:
[text]")
+ return
+
+// Proc: request_im()
+// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message)
+// Description: Response to a communicator trying to message the device.
+// Adds them to the list of people that have messaged this device and adds the message to the message list.
+/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text)
+ var/who = null
+ if(isobserver(candidate))
+ var/mob/observer/dead/ghost = candidate
+ who = ghost
+ im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text))
+ else if(istype(candidate, /obj/item/device/communicator))
+ var/obj/item/device/communicator/comm = candidate
+ who = comm.owner
+ comm.im_contacts |= src
+ im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text))
+ else return
+
+ im_contacts |= candidate
+
+ if(!who)
+ return
+
+ if(ringer)
+ playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
+ for (var/mob/O in hearers(2, loc))
+ O.show_message(text("\icon[src] *beep*"))
+
+ alert_called = 1
+ update_icon()
+
+ //Search for holder of the device.
+ var/mob/living/L = null
+ if(loc && isliving(loc))
+ L = loc
+
+ if(L)
+ to_chat(L, "\icon[src] Message from [who].")
+
+// Verb: text_communicator()
+// Parameters: None
+// Description: Allows a ghost to send a text message to a communicator.
+/mob/observer/dead/verb/text_communicator()
+ set category = "Ghost"
+ set name = "Text Communicator"
+ set desc = "If there is a communicator available, send a text message to it."
+
+ if(ticker.current_state < GAME_STATE_PLAYING)
+ to_chat(src, "The game hasn't started yet!")
+ return
+
+ if (!src.stat)
+ return
+
+ if (usr != src)
+ return //something is terribly wrong
+
+ for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling.
+ if(src.client.prefs.real_name == L.real_name)
+ to_chat(src, "Your identity is already present in the game world. Please load in a different character first.")
+ return
+
+ var/obj/machinery/exonet_node/E = get_exonet_node()
+ if(!E || !E.on || !E.allow_external_communicators)
+ to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \
+ so your call can't go through.")
+ return
+
+ var/list/choices = list()
+ for(var/obj/item/device/communicator/comm in all_communicators)
+ if(!comm.network_visibility || !comm.exonet || !comm.exonet.address)
+ continue
+ choices.Add(comm)
+
+ if(!choices.len)
+ to_chat(src, "There are no available communicators, sorry.")
+ return
+
+ var/choice = input(src,"Send a text message to whom?") as null|anything in choices
+ if(choice)
+ var/obj/item/device/communicator/chosen_communicator = choice
+ var/mob/observer/dead/O = src
+ var/text_message = sanitize(input(src, "What do you want the message to say?")) as message
+ if(text_message && O.exonet)
+ O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message)
+
+ to_chat(src, "You have sent '[text_message]' to [chosen_communicator].")
+ exonet_messages.Add("To [chosen_communicator]:
[text_message]")
+ log_pda("[usr] (COMM: [src]) sent \"[text_message]\" to [chosen_communicator]")
+ for(var/mob/M in player_list)
+ if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
+ if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat)
+ continue
+ if(M == src)
+ continue
+ M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]")
+
+
+
+// Verb: show_text_messages()
+// Parameters: None
+// Description: Lets ghosts review messages they've sent or received.
+/mob/observer/dead/verb/show_text_messages()
+ set category = "Ghost"
+ set name = "Show Text Messages"
+ set desc = "Allows you to see exonet text messages you've sent and received."
+
+ var/HTML = "Exonet Message Log"
+ for(var/line in exonet_messages)
+ HTML += line + "
"
+ HTML +=""
+ usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0")
\ No newline at end of file
diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm
new file mode 100644
index 0000000000..f4c61ae7d7
--- /dev/null
+++ b/code/game/objects/items/devices/communicator/phone.dm
@@ -0,0 +1,394 @@
+// Proc: add_communicating()
+// Parameters: 1 (comm - the communicator to add to communicating)
+// Description: Used when this communicator gets a new communicator to relay say/me messages to
+/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm)
+ if(!comm || !istype(comm)) return
+
+ communicating |= comm
+ listening_objects |= src
+ update_icon()
+
+// Proc: del_communicating()
+// Parameters: 1 (comm - the communicator to remove from communicating)
+// Description: Used when this communicator is being asked to stop relaying say/me messages to another
+/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm)
+ if(!comm || !istype(comm)) return
+
+ communicating.Remove(comm)
+ update_icon()
+
+// Proc: open_connection()
+// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device)
+// Description: Typechecks the candidate, then calls the correct proc for further connecting.
+/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate)
+ if(isobserver(candidate))
+ voice_invites.Remove(candidate)
+ open_connection_to_ghost(user, candidate)
+ else
+ if(istype(candidate, /obj/item/device/communicator))
+ open_connection_to_communicator(user, candidate)
+
+// Proc: open_connection_to_communicator()
+// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator)
+// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed.
+/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate)
+ if(!istype(candidate, /obj/item/device/communicator))
+ return
+ var/obj/item/device/communicator/comm = candidate
+ voice_invites.Remove(candidate)
+ comm.voice_requests.Remove(src)
+
+ if(user)
+ comm.visible_message("\icon[src] Connecting to [src].")
+ to_chat(user, "\icon[src] Attempting to call [comm].")
+ sleep(10)
+ to_chat(user, "\icon[src] Dialing internally from [station_name()], [system_name()].") // Vorestation edit
+ sleep(20) //If they don't have an exonet something is very wrong and we want a runtime.
+ to_chat(user, "\icon[src] Connection re-routed to [comm] at [comm.exonet.address].")
+ sleep(40)
+ to_chat(user, "\icon[src] Connection to [comm] at [comm.exonet.address] established.")
+ comm.visible_message("\icon[src] Connection to [src] at [exonet.address] established.")
+ sleep(20)
+
+ src.add_communicating(comm)
+ comm.add_communicating(src)
+
+// Proc: open_connection_to_ghost()
+// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob)
+// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client.
+/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate)
+ if(!isobserver(candidate))
+ return
+ //Handle moving the ghost into the new shell.
+ announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.")
+ voice_requests.Remove(candidate)
+ voice_invites.Remove(candidate)
+ var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be.
+ new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences.
+ //Do some simple logging since this is a tad risky as a concept.
+ var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \
+ [user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]."
+ message_admins(msg)
+ log_game(msg)
+ new_voice.mind = candidate.mind //Transfer the mind, if any.
+ new_voice.ckey = candidate.ckey //Finally, bring the client over.
+ voice_mobs.Add(new_voice)
+ listening_objects |= src
+
+ var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator.
+ blackness.screen_loc = ui_entire_screen
+ blackness.icon = 'icons/effects/effects.dmi'
+ blackness.icon_state = "1"
+ blackness.mouse_opacity = 2 //Can't see anything!
+ new_voice.client.screen.Add(blackness)
+
+ update_icon()
+
+ //Now for some connection fluff.
+ if(user)
+ to_chat(user, "\icon[src] Connecting to [candidate].")
+ to_chat(new_voice, "\icon[src] Attempting to call [src].")
+ sleep(10)
+ to_chat(new_voice, "\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()].")
+ sleep(20)
+ to_chat(new_voice, "\icon[src] Connecting to [station_name()] telecommunications array.")
+ sleep(40)
+ to_chat(new_voice, "\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].")
+ sleep(20)
+
+ //We're connected, no need to hide everything.
+ new_voice.client.screen.Remove(blackness)
+ qdel(blackness)
+
+ to_chat(new_voice, "\icon[src] Connection to [src] established.")
+ to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.")
+ to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.")
+ to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!")
+ if(new_voice.mind)
+ new_voice.mind.assigned_role = "Disembodied Voice"
+ if(user)
+ to_chat(user, "\icon[src] Your communicator is now connected to [candidate]'s communicator.")
+
+// Proc: close_connection()
+// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected)
+// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators
+// and voice mobs are removed.
+/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason)
+ if(voice_mobs.len == 0 && communicating.len == 0)
+ return
+
+ for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers
+ if(target && voice != target) //If no target is inputted, it deletes all of them.
+ continue
+ to_chat(voice, "\icon[src] [reason].")
+ visible_message("\icon[src] [reason].")
+ voice_mobs.Remove(voice)
+ qdel(voice)
+ update_icon()
+
+ for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators.
+ if(target && comm != target)
+ continue
+ src.del_communicating(comm)
+ comm.del_communicating(src)
+ comm.visible_message("\icon[src] [reason].")
+ visible_message("\icon[src] [reason].")
+ if(comm.camera && video_source == comm.camera) //We hung up on the person on video
+ end_video()
+ if(camera && comm.video_source == camera) //We hung up on them while they were watching us
+ comm.end_video()
+
+ if(voice_mobs.len == 0 && communicating.len == 0)
+ listening_objects.Remove(src)
+
+// Proc: request()
+// Parameters: 1 (candidate - the ghost or communicator wanting to call the device)
+// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters
+/obj/item/device/communicator/proc/request(var/atom/candidate)
+ if(candidate in voice_requests)
+ return
+ var/who = null
+ if(isobserver(candidate))
+ who = candidate.name
+ else if(istype(candidate, /obj/item/device/communicator))
+ var/obj/item/device/communicator/comm = candidate
+ who = comm.owner
+ comm.voice_invites |= src
+
+ if(!who)
+ return
+
+ voice_requests |= candidate
+
+ if(ringer)
+ playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
+ for (var/mob/O in hearers(2, loc))
+ O.show_message(text("\icon[src] *beep*"))
+
+ alert_called = 1
+ update_icon()
+
+ //Search for holder of the device.
+ var/mob/living/L = null
+ if(loc && isliving(loc))
+ L = loc
+
+ if(L)
+ to_chat(L, "\icon[src] Communications request from [who].")
+
+// Proc: del_request()
+// Parameters: 1 (candidate - the ghost or communicator to be declined)
+// Description: Declines a request and cleans up both ends
+/obj/item/device/communicator/proc/del_request(var/atom/candidate)
+ if(!(candidate in voice_requests))
+ return
+
+ if(isobserver(candidate))
+ to_chat(candidate, "Your communicator call request was declined.")
+ else if(istype(candidate, /obj/item/device/communicator))
+ var/obj/item/device/communicator/comm = candidate
+ comm.voice_invites -= src
+
+ voice_requests -= candidate
+
+ //Search for holder of our device.
+ var/mob/living/us = null
+ if(loc && isliving(loc))
+ us = loc
+
+ if(us)
+ to_chat(us, "\icon[src] Declined request.")
+
+// Proc: see_emote()
+// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents)
+// Description: Relays the emote to all linked communicators.
+/obj/item/device/communicator/see_emote(mob/living/M, text)
+ var/rendered = "\icon[src] [text]"
+ for(var/obj/item/device/communicator/comm in communicating)
+ var/turf/T = get_turf(comm)
+ if(!T) return
+ //VOREStation Edit Start for commlinks
+ var/list/mobs_to_relay
+ if(istype(comm,/obj/item/device/communicator/commlink))
+ var/obj/item/device/communicator/commlink/CL = comm
+ mobs_to_relay = list(CL.nif.human)
+ else
+ var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display
+ mobs_to_relay = in_range["mobs"]
+ //VOREStation Edit End
+
+ for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other.
+ var/dst = get_dist(get_turf(mob),get_turf(comm))
+ if(dst <= video_range)
+ mob.show_message(rendered)
+ else
+ to_chat(mob, "You can barely see some movement on \the [src]'s display.")
+
+ ..()
+
+// Proc: hear_talk()
+// Parameters: 4 (M - the mob the speech originated from, text - what is being said, verb - the word used to describe how text is being said, speaking - language
+// being used)
+// Description: Relays the speech to all linked communicators.
+/obj/item/device/communicator/hear_talk(mob/living/M, text, verb, datum/language/speaking)
+ for(var/obj/item/device/communicator/comm in communicating)
+
+ var/turf/T = get_turf(comm)
+ if(!T) return
+ //VOREStation Edit Start for commlinks
+ var/list/mobs_to_relay
+ if(istype(comm,/obj/item/device/communicator/commlink))
+ var/obj/item/device/communicator/commlink/CL = comm
+ mobs_to_relay = list(CL.nif.human)
+ else
+ var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display
+ mobs_to_relay = in_range["mobs"]
+ //VOREStation Edit End
+
+ for(var/mob/mob in mobs_to_relay)
+ //Can whoever is hearing us understand?
+ if(!mob.say_understands(M, speaking))
+ if(speaking)
+ text = speaking.scramble(text)
+ else
+ text = stars(text)
+ var/name_used = M.GetVoice()
+ var/rendered = null
+ if(speaking) //Language being used
+ rendered = "\icon[src] [name_used] [speaking.format_message(text, verb)]"
+ else
+ rendered = "\icon[src] [name_used] [verb], \"[text]\""
+ mob.show_message(rendered, 2)
+
+// Proc: show_message()
+// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown)
+// Description: Relays the message to all linked communicators.
+/obj/item/device/communicator/show_message(msg, type, alt, alt_type)
+ var/rendered = "\icon[src] [msg]"
+ for(var/obj/item/device/communicator/comm in communicating)
+ var/turf/T = get_turf(comm)
+ if(!T) return
+ var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0)
+ var/list/mobs_to_relay = in_range["mobs"]
+
+ for(var/mob/mob in mobs_to_relay)
+ mob.show_message(rendered)
+ ..()
+
+// Verb: join_as_voice()
+// Parameters: None
+// Description: Allows ghosts to call communicators, if they meet all the requirements.
+/mob/observer/dead/verb/join_as_voice()
+ set category = "Ghost"
+ set name = "Call Communicator"
+ set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up."
+
+ if(ticker.current_state < GAME_STATE_PLAYING)
+ to_chat(src, "The game hasn't started yet!")
+ return
+
+ if (!src.stat)
+ return
+
+ if (usr != src)
+ return //something is terribly wrong
+
+ var/confirm = alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? \
+ This will reset your respawn timer, if someone answers.", "Join as Voice?", "Yes","No")
+ if(confirm == "No")
+ return
+
+ for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling.
+ if(src.client.prefs.real_name == L.real_name)
+ to_chat(src, "Your identity is already present in the game world. Please load in a different character first.")
+ return
+
+ var/obj/machinery/exonet_node/E = get_exonet_node()
+ if(!E || !E.on || !E.allow_external_communicators)
+ to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \
+ so your call can't go through.")
+ return
+
+ var/list/choices = list()
+ for(var/obj/item/device/communicator/comm in all_communicators)
+ if(!comm.network_visibility || !comm.exonet || !comm.exonet.address)
+ continue
+ choices.Add(comm)
+
+ if(!choices.len)
+ to_chat(src , "There are no available communicators, sorry.")
+ return
+
+ var/choice = input(src,"Send a voice request to whom?") as null|anything in choices
+ if(choice)
+ var/obj/item/device/communicator/chosen_communicator = choice
+ var/mob/observer/dead/O = src
+ if(O.exonet)
+ O.exonet.send_message(chosen_communicator.exonet.address, "voice")
+
+ to_chat(src, "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers.")
+
+// Proc: connect_video()
+// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end
+// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon
+/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm)
+ if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video
+
+ if(video_source) //Already in a video
+ to_chat(user, "You are already connected to a video call!")
+
+ if(user.blinded) //User is blinded
+ to_chat(user, "You cannot see well enough to do that!")
+
+ if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something
+ to_chat(user, "\icon[src]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.")
+
+ to_chat(user, "\icon[src] Attempting to start video over existing call.")
+ sleep(30)
+ to_chat(user, "\icon[src] Please wait...")
+
+ video_source = comm.camera
+ comm.visible_message("\icon[src] New video connection from [comm].")
+ watch_video(user)
+ update_icon()
+
+// Proc: watch_video()
+// Parameters: user - the mob doing the viewing of video
+// Description: Moves a mob's eye to the far end for the duration of viewing the far end
+/obj/item/device/communicator/proc/watch_video(mob/user)
+ if(!Adjacent(user) || !video_source) return
+ user.set_machine(video_source)
+ user.reset_view(video_source)
+ to_chat(user,"Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View")
+ to_chat(user,"To return to an active video session, use the communicator in your hand.")
+ spawn(0)
+ while(user.machine == video_source && Adjacent(user))
+ var/turf/T = get_turf(video_source)
+ if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use())
+ user << "The screen bursts into static, then goes black."
+ video_cleanup(user)
+ return
+ sleep(10)
+
+ video_cleanup(user)
+
+// Proc: video_cleanup()
+// Parameters: user - the mob who doesn't want to see video anymore
+// Description: Cleans up mob's client when they stop watching a video
+/obj/item/device/communicator/proc/video_cleanup(mob/user)
+ if(!user) return
+
+ user.reset_view(null)
+ user.unset_machine()
+
+// Proc: end_video()
+// Parameters: reason - the text reason to print for why it ended
+// Description: Ends the video call by clearing video_source
+/obj/item/device/communicator/proc/end_video(var/reason)
+ video_source = null
+
+ . = "\icon[src] [reason ? reason : "Video session ended"]."
+
+ visible_message(.)
+ update_icon()
+
diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm
index decb40d807..847ce58560 100644
--- a/code/game/objects/items/devices/defib.dm
+++ b/code/game/objects/items/devices/defib.dm
@@ -35,8 +35,8 @@
qdel_null(paddles)
qdel_null(bcell)
-/obj/item/device/defib_kit/loaded //starts with highcap cell
- bcell = /obj/item/weapon/cell/high
+/obj/item/device/defib_kit/loaded //starts with a cell
+ bcell = /obj/item/weapon/cell/apc
/obj/item/device/defib_kit/update_icon()
@@ -209,7 +209,7 @@
var/combat = 0 //If it can be used to revive people wearing thick clothing (e.g. spacesuits)
var/cooldowntime = (6 SECONDS) // How long in deciseconds until the defib is ready again after use.
var/chargetime = (2 SECONDS)
- var/chargecost = 1000 //units of charge
+ var/chargecost = 1250 //units of charge per zap //With the default APC level cell, this allows 4 shocks
var/burn_damage_amt = 5
var/use_on_synthetic = 0 //If 1, this is only useful on FBPs, if 0, this is only useful on fleshies
@@ -284,7 +284,12 @@
return "buzzes, \"Resuscitation failed - Excessive neural degeneration. Further attempts futile.\""
H.updatehealth()
- if(H.health + H.getOxyLoss() <= config.health_threshold_dead || (HUSK in H.mutations))
+
+ if(H.isSynthetic())
+ if(H.health + H.getOxyLoss() + H.getToxLoss() <= config.health_threshold_dead)
+ return "buzzes, \"Resuscitation failed - Severe damage detected. Begin manual repair before further attempts futile.\""
+
+ else if(H.health + H.getOxyLoss() <= config.health_threshold_dead || (HUSK in H.mutations) || !H.can_defib)
return "buzzes, \"Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Further attempts futile.\""
var/bad_vital_organ = check_vital_organs(H)
@@ -374,7 +379,10 @@
// This proc is used so that we can return out of the revive process while ensuring that busy and update_icon() are handled
/obj/item/weapon/shockpaddles/proc/do_revive(mob/living/carbon/human/H, mob/user)
if(!H.client && !H.teleop)
- to_chat(find_dead_player(H.ckey, 1), "Someone is attempting to resuscitate you. Re-enter your body if you want to be revived!")
+ for(var/mob/observer/dead/ghost in player_list)
+ if(ghost.mind == H.mind)
+ to_chat(ghost, "Someone is attempting to resuscitate you. Re-enter your body if you want to be revived! (Verbs -> Ghost -> Re-enter corpse)")
+ break
//beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process
user.visible_message("\The [user] begins to place [src] on [H]'s chest.", "You begin to place [src] on [H]'s chest...")
@@ -420,6 +428,9 @@
var/adjust_health = barely_in_crit - H.health //need to increase health by this much
H.adjustOxyLoss(-adjust_health)
+ if(H.isSynthetic())
+ H.adjustToxLoss(-H.getToxLoss())
+
make_announcement("pings, \"Resuscitation successful.\"", "notice")
playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0)
@@ -642,7 +653,8 @@
name = "jumper cable kit"
desc = "A device that delivers powerful shocks to detachable jumper cables that are capable of reviving full body prosthetics."
icon_state = "jumperunit"
- item_state = "jumperunit"
+ item_state = "defibunit"
+// item_state = "jumperunit"
paddles = /obj/item/weapon/shockpaddles/linked/jumper
/obj/item/device/defib_kit/jumper_kit/loaded
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index d79ffcf1df..ba812cefd3 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -18,7 +18,7 @@
/obj/item/device/flash/proc/clown_check(var/mob/user)
if(user && (CLUMSY in user.mutations) && prob(50))
- user << "\The [src] slips out of your hand."
+ to_chat(user, "\The [src] slips out of your hand.")
user.drop_item()
return 0
return 1
@@ -43,7 +43,7 @@
if(prob( round(times_used / 2) )) //if you use it 10 times in a minute it has a 5% chance to break.
broken = 1
if(user)
- user << "The bulb has burnt out!"
+ to_chat(user, "The bulb has burnt out!")
icon_state = "flashburnt"
return FALSE
else
@@ -51,7 +51,8 @@
return TRUE
else //can only use it 10 times a minute
if(user)
- user << "*click* *click*"
+ to_chat(user, "click")
+ playsound(src.loc, 'sound/weapons/empty.ogg', 80, 1)
return FALSE
//attack_as_weapon
@@ -62,12 +63,12 @@
user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to flash [M.name] ([M.ckey])")
msg_admin_attack("[user.name] ([user.ckey]) Used the [src.name] to flash [M.name] ([M.ckey]) (JMP)")
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(M)
if(!clown_check(user)) return
if(broken)
- user << "\The [src] is broken."
+ to_chat(user, "\The [src] is broken.")
return
flash_recharge()
@@ -75,9 +76,6 @@
if(!check_capacitor(user))
return
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- user.do_attack_animation(M)
-
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
var/flashfail = 0
@@ -155,7 +153,7 @@
/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0)
if(!user || !clown_check(user)) return
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(src))
if(broken)
user.show_message("The [src.name] is broken", 2)
@@ -215,12 +213,12 @@
..()
if(!broken)
broken = 1
- user << "The bulb has burnt out!"
+ to_chat(user, "The bulb has burnt out!")
icon_state = "flashburnt"
/obj/item/device/flash/synthetic/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0)
..()
if(!broken)
broken = 1
- user << "The bulb has burnt out!"
+ to_chat(user, "The bulb has burnt out!")
icon_state = "flashburnt"
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index dd10e80859..151b762d7c 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -141,7 +141,7 @@
user.visible_message("\The [user] directs [src] to [M]'s eyes.", \
"You direct [src] to [M]'s eyes.")
- if(H == user) //can't look into your own eyes buster
+ if(H != user) //can't look into your own eyes buster
if(M.stat == DEAD || M.blinded) //mob is dead or fully blind
user << "\The [M]'s pupils do not react to the light!"
return
@@ -163,7 +163,7 @@
else
user << "\The [M]'s pupils narrow."
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //can be used offensively
+ user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively
M.flash_eyes()
else
return ..()
@@ -290,6 +290,7 @@
name = "desk lamp"
desc = "A desk lamp with an adjustable mount."
icon_state = "lamp"
+ force = 10
brightness_on = 5
w_class = ITEMSIZE_LARGE
flags = CONDUCT
@@ -320,7 +321,7 @@
w_class = ITEMSIZE_SMALL
brightness_on = 8 // Pretty bright.
light_power = 3
- light_color = "#e58775"
+ light_color = LIGHT_COLOR_FLARE
icon_state = "flare"
item_state = "flare"
action_button_name = null //just pull it manually, neckbeard.
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
new file mode 100644
index 0000000000..4ae0ebc4b0
--- /dev/null
+++ b/code/game/objects/items/devices/gps.dm
@@ -0,0 +1,211 @@
+var/list/GPS_list = list()
+
+/obj/item/device/gps
+ name = "global positioning system"
+ desc = "Triangulates the approximate co-ordinates using a nearby satellite network. Alt+click to toggle power."
+ icon = 'icons/obj/gps.dmi'
+ icon_state = "gps-c"
+ w_class = ITEMSIZE_TINY
+ slot_flags = SLOT_BELT
+ origin_tech = list(TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_MAGNET = 1)
+ matter = list(DEFAULT_WALL_MATERIAL = 500)
+ var/gps_tag = "COM0"
+ var/emped = FALSE
+ var/tracking = FALSE // Will not show other signals or emit its own signal if false.
+ var/long_range = FALSE // If true, can see farther, depending on get_map_levels().
+ var/local_mode = FALSE // If true, only GPS signals of the same Z level are shown.
+ 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()
+ GPS_list += src
+ name = "global positioning system ([gps_tag])"
+ update_icon()
+
+/obj/item/device/gps/Destroy()
+ GPS_list -= src
+ return ..()
+
+/obj/item/device/gps/AltClick(mob/user)
+ toggletracking(user)
+
+/obj/item/device/gps/proc/toggletracking(mob/living/user)
+ if(!istype(user))
+ return
+ if(emped)
+ to_chat(user, "It's busted!")
+ return
+ if(tracking)
+ to_chat(user, "[src] is no longer tracking, or visible to other GPS devices.")
+ tracking = FALSE
+ update_icon()
+ else
+ to_chat(user, "[src] is now tracking, and visible to other GPS devices.")
+ tracking = TRUE
+ update_icon()
+
+/obj/item/device/gps/emp_act(severity)
+ if(emped) // Without a fancy callback system, this will have to do.
+ return
+ var/severity_modifier = severity ? severity : 4 // In case emp_act gets called without any arguments.
+ var/duration = 5 MINUTES / severity_modifier
+ emped = TRUE
+ update_icon()
+
+ spawn(duration)
+ emped = FALSE
+ update_icon()
+ visible_message("\The [src] appears to be functional again.")
+
+/obj/item/device/gps/update_icon()
+ overlays.Cut()
+ if(emped)
+ overlays += image(icon, src, "emp")
+ else if(tracking)
+ overlays += image(icon, src, "working")
+
+/obj/item/device/gps/attack_self(mob/user)
+ display(user)
+
+/obj/item/device/gps/proc/display(mob/user)
+ if(!tracking)
+ to_chat(user, "The device is off. Alt-click it to turn it on.")
+ return
+ if(emped)
+ to_chat(user, "It's busted!")
+ return
+
+ var/list/dat = list()
+
+ var/turf/curr = get_turf(src)
+ var/area/my_area = get_area(src)
+ dat += "Current location: [my_area.name] ([curr.x], [curr.y], [curr.z])"
+ dat += "[hide_signal ? "Tagged" : "Broadcasting"] as '[gps_tag]'. \[Change Tag\] \
+ \[Toggle Scan Range\] \
+ [can_hide_signal ? "\[Toggle Signal Visibility\]":""]"
+
+ var/list/signals = list()
+
+ for(var/gps in GPS_list)
+ var/obj/item/device/gps/G = gps
+ if(G.emped || !G.tracking || G.hide_signal || G == src) // Their GPS isn't on or functional.
+ continue
+ var/turf/T = get_turf(G)
+ var/z_level_detection = using_map.get_map_levels(curr.z, long_range)
+
+ if(local_mode && T.z != curr.z) // Only care about the current z-level.
+ continue
+ else if(!(T.z in z_level_detection)) // Too far away.
+ continue
+
+ var/area/their_area = get_area(G)
+ var/area_name = their_area.name
+ if(istype(their_area, /area/submap))
+ area_name = "Unknown Area" // Avoid spoilers.
+ var/coord = "[T.x], [T.y], [T.z]"
+ var/degrees = round(Get_Angle(curr, T))
+ var/direction = uppertext(dir2text(get_dir(curr, T)))
+ var/distance = get_dist(curr, T)
+ var/local = curr.z == T.z ? TRUE : FALSE
+ if(!direction)
+ direction = "CENTER"
+ degrees = "N/A"
+
+ signals += " [G.gps_tag]: [area_name] ([coord]) [local ? "Dist: [distance]m Dir: [degrees]° ([direction])":""]"
+
+ if(signals.len)
+ dat += "Detected signals;"
+ for(var/line in signals)
+ dat += line
+ else
+ dat += "No other signals detected."
+
+ var/result = dat.Join("
")
+ to_chat(user, result)
+
+/obj/item/device/gps/Topic(var/href, var/list/href_list)
+ if(..())
+ return 1
+
+ if(href_list["tag"])
+ var/a = input("Please enter desired tag.", name, gps_tag) as text
+ a = uppertext(copytext(sanitize(a), 1, 11))
+ if(in_range(src, usr))
+ gps_tag = a
+ name = "global positioning system ([gps_tag])"
+ to_chat(usr, "You set your GPS's tag to '[gps_tag]'.")
+
+ if(href_list["range"])
+ local_mode = !local_mode
+ to_chat(usr, "You set the signal receiver to [local_mode ? "'NARROW'" : "'BROAD'"].")
+
+ if(href_list["hide"])
+ if(!can_hide_signal)
+ return
+ hide_signal = !hide_signal
+ to_chat(usr, "You set the device to [hide_signal ? "not " : ""]broadcast a signal while scanning for other signals.")
+
+/obj/item/device/gps/on // Defaults to off to avoid polluting the signal list with a bunch of GPSes without owners. If you need to spawn active ones, use these.
+ tracking = TRUE
+
+/obj/item/device/gps/science
+ icon_state = "gps-s"
+ gps_tag = "SCI0"
+
+/obj/item/device/gps/science/on
+ tracking = TRUE
+
+/obj/item/device/gps/engineering
+ icon_state = "gps-e"
+ gps_tag = "ENG0"
+
+/obj/item/device/gps/engineering/on
+ tracking = TRUE
+
+/obj/item/device/gps/mining
+ icon_state = "gps-m"
+ gps_tag = "MINE0"
+ desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life. Alt+click to toggle power."
+
+/obj/item/device/gps/mining/on
+ tracking = TRUE
+
+/obj/item/device/gps/explorer
+ icon_state = "gps-ex"
+ gps_tag = "EX0"
+ desc = "A positioning system helpful for rescuing trapped or injured explorers, keeping one on you at all times while exploring might just save your life. Alt+click to toggle power."
+
+/obj/item/device/gps/explorer/on
+ tracking = TRUE
+
+/obj/item/device/gps/syndie
+ icon_state = "gps-syndie"
+ gps_tag = "NULL"
+ desc = "A positioning system that has extended range and can detect other GPS device signals without revealing its own. How that works is best left a mystery. Alt+click to toggle power."
+ origin_tech = list(TECH_MATERIAL = 2, TECH_BLUESPACE = 3, TECH_MAGNET = 2, TECH_ILLEGAL = 2)
+ long_range = TRUE
+ hide_signal = TRUE
+ can_hide_signal = TRUE
+
+/obj/item/device/gps/robot
+ icon_state = "gps-b"
+ gps_tag = "SYNTH0"
+ desc = "A synthetic internal positioning system. Used as a recovery beacon for damaged synthetic assets, or a collaboration tool for mining or exploration teams. \
+ Alt+click to toggle power."
+ tracking = TRUE // On by default.
+
+/obj/item/device/gps/internal // Base type for immobile/internal GPS units.
+ icon_state = "internal"
+ gps_tag = "Eerie Signal"
+ desc = "Report to a coder immediately."
+ invisibility = INVISIBILITY_MAXIMUM
+ tracking = TRUE // Meant to point to a location, so it needs to be on.
+ anchored = TRUE
+
+/obj/item/device/gps/internal/base
+ gps_tag = "NT_BASE"
+ desc = "A homing signal from NanoTrasen's outpost."
+
+/obj/item/device/gps/internal/alien_vessel
+ gps_tag = "Mysterious Signal"
+ desc = "A signal that seems forboding."
\ No newline at end of file
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 35f41730df..8fd96981fc 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -42,7 +42,7 @@
name = "light replacer"
desc = "A device to automatically replace lights. Refill with working lightbulbs or sheets of glass."
-
+ force = 8
icon = 'icons/obj/janitor.dmi'
icon_state = "lightreplacer0"
flags = CONDUCT
@@ -61,32 +61,32 @@
/obj/item/device/lightreplacer/examine(mob/user)
if(..(user, 2))
- user << "It has [uses] lights remaining."
+ to_chat(user, "It has [uses] lights remaining.")
/obj/item/device/lightreplacer/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/stack/material) && W.get_material_name() == "glass")
var/obj/item/stack/G = W
if(uses >= max_uses)
- user << "[src.name] is full."
+ to_chat(user, "[src.name] is full.")
return
else if(G.use(1))
- AddUses(16) //Autolathe converts 1 sheet into 16 lights.
- user << "You insert a piece of glass into \the [src.name]. You have [uses] light\s remaining."
+ add_uses(16) //Autolathe converts 1 sheet into 16 lights.
+ to_chat(user, "You insert a piece of glass into \the [src.name]. You have [uses] light\s remaining.")
return
else
- user << "You need one sheet of glass to replace lights."
+ to_chat(user, "You need one sheet of glass to replace lights.")
if(istype(W, /obj/item/weapon/light))
var/obj/item/weapon/light/L = W
if(L.status == 0) // LIGHT OKAY
if(uses < max_uses)
- AddUses(1)
- user << "You insert \the [L.name] into \the [src.name]. You have [uses] light\s remaining."
+ add_uses(1)
+ to_chat(user, "You insert \the [L.name] into \the [src.name]. You have [uses] light\s remaining.")
user.drop_item()
qdel(L)
return
else
- user << "You need a working light."
+ to_chat(user, "You need a working light.")
return
/obj/item/device/lightreplacer/attack_self(mob/user)
@@ -95,10 +95,10 @@
var/mob/living/silicon/robot/R = user
if(R.emagged)
src.Emag()
- usr << "You shortcircuit the [src]."
+ to_chat(usr, You short circuit the [src].")
return
*/
- usr << "It has [uses] lights remaining."
+ to_chat(usr, "It has [uses] lights remaining.")
/obj/item/device/lightreplacer/update_icon()
icon_state = "lightreplacer[emagged]"
@@ -107,17 +107,17 @@
/obj/item/device/lightreplacer/proc/Use(var/mob/user)
playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
- AddUses(-1)
+ add_uses(-1)
return 1
// Negative numbers will subtract
-/obj/item/device/lightreplacer/proc/AddUses(var/amount = 1)
+/obj/item/device/lightreplacer/proc/add_uses(var/amount = 1)
uses = min(max(uses + amount, 0), max_uses)
/obj/item/device/lightreplacer/proc/Charge(var/mob/user, var/amount = 1)
charge += amount
if(charge > 6)
- AddUses(1)
+ add_uses(1)
charge = 0
/obj/item/device/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U)
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index d79fc021db..21d855c674 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -44,4 +44,4 @@
icon = 'icons/obj/abductor.dmi'
icon_state = "multitool"
toolspeed = 0.1
- origin_tech = list(TECH_MAGNETS = 5, TECH_ENGINEERING = 5)
\ No newline at end of file
+ origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5)
\ No newline at end of file
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index e4c1bb3f20..f3bc620d08 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -26,7 +26,7 @@
/obj/item/device/powersink/Destroy()
processing_objects.Remove(src)
- processing_power_items.Remove(src)
+ STOP_PROCESSING_POWER_OBJECT(src)
..()
/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user)
@@ -50,7 +50,7 @@
else
if (mode == 2)
processing_objects.Remove(src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite
- processing_power_items.Remove(src)
+ STOP_PROCESSING_POWER_OBJECT(src)
anchored = 0
mode = 0
src.visible_message("[user] detaches [src] from the cable!")
@@ -74,14 +74,14 @@
mode = 2
icon_state = "powersink1"
processing_objects.Add(src)
- processing_power_items.Add(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)
- processing_power_items.Remove(src)
+ STOP_PROCESSING_POWER_OBJECT(src)
/obj/item/device/powersink/pwr_drain()
if(!attached)
diff --git a/code/game/objects/items/devices/radio/encryptionkey_vr.dm b/code/game/objects/items/devices/radio/encryptionkey_vr.dm
new file mode 100644
index 0000000000..b9ad7f72c1
--- /dev/null
+++ b/code/game/objects/items/devices/radio/encryptionkey_vr.dm
@@ -0,0 +1,15 @@
+/obj/item/device/encryptionkey/heads/hop
+ name = "head of personnel's encryption key"
+ icon_state = "hop_cypherkey"
+ channels = list("Supply" = 1, "Service" = 1, "Command" = 1, "Security" = 0, "Explorer" = 0)
+
+/obj/item/device/encryptionkey/heads/ai_integrated
+ name = "ai integrated encryption key"
+ desc = "Integrated encryption key"
+ icon_state = "cap_cypherkey"
+ channels = list("Command" = 1, "Security" = 1, "Engineering" = 1, "Science" = 1, "Medical" = 1, "Supply" = 1, "Service" = 1, "AI Private" = 1, "Explorer" = 1)
+
+/obj/item/device/encryptionkey/heads/captain
+ name = "colony director's encryption key"
+ icon_state = "cap_cypherkey"
+ channels = list("Command" = 1, "Security" = 1, "Engineering" = 0, "Science" = 0, "Medical" = 0, "Supply" = 0, "Service" = 0, "Explorer" = 0)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index ccbbb5fea7..4187cfc422 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -23,6 +23,13 @@ REAGENT SCANNER
matter = list(DEFAULT_WALL_MATERIAL = 200)
origin_tech = list(TECH_MAGNET = 1, TECH_BIO = 1)
var/mode = 1;
+ var/advscan = 0
+ var/showadvscan = 1
+
+/obj/item/device/healthanalyzer/New()
+ if(advscan >= 1)
+ verbs += /obj/item/device/healthanalyzer/proc/toggle_adv
+ ..()
/obj/item/device/healthanalyzer/do_surgery(mob/living/M, mob/living/user)
if(user.a_intent != I_HELP) //in case it is ever used as a surgery tool
@@ -39,7 +46,7 @@ REAGENT SCANNER
for(var/mob/O in viewers(M, null))
O.show_message("\The [user] has analyzed the floor's vitals!", 1)
user.show_message("Analyzing Results for The floor:", 1)
- user.show_message("Overall Status: Healthy", 1)
+ user.show_message("Overall Status: Healthy", 1)
user.show_message(" Damage Specifics: 0-0-0-0", 1)
user.show_message("Key: Suffocation/Toxin/Burns/Brute", 1)
user.show_message("Body Temperature: ???", 1)
@@ -100,39 +107,72 @@ REAGENT SCANNER
OX = fake_oxy > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal"
user.show_message("[OX] | [TX] | [BU] | [BR]")
if(M.radiation)
- user.show_message("Radiation detected.")
+ if(advscan >= 2 && showadvscan == 1)
+ if(M.radiation >= 75)
+ user.show_message("Critical levels of radiation detected. Immediate treatment advised.")
+ else if(M.radiation >= 50)
+ user.show_message("Severe levels of radiation detected.")
+ else if(M.radiation >= 25)
+ user.show_message("Moderate levels of radiation detected.")
+ else if(M.radiation >= 1)
+ user.show_message("Low levels of radiation detected.")
+ else
+ user.show_message("Radiation detected.")
if(istype(M, /mob/living/carbon))
var/mob/living/carbon/C = M
if(C.reagents.total_volume)
var/unknown = 0
var/reagentdata[0]
+ var/unknownreagents[0]
for(var/A in C.reagents.reagent_list)
var/datum/reagent/R = A
if(R.scannable)
reagentdata["[R.id]"] = " [round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name]"
else
unknown++
+ unknownreagents["[R.id]"] = " [round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name]"
if(reagentdata.len)
user.show_message("Beneficial reagents detected in subject's blood:")
for(var/d in reagentdata)
user.show_message(reagentdata[d])
if(unknown)
- user.show_message("Warning: Unknown substance[(unknown>1)?"s":""] detected in subject's blood.")
+ if(advscan >= 3 && showadvscan == 1)
+ user.show_message("Warning: Non-medical reagent[(unknown>1)?"s":""] detected in subject's blood:")
+ for(var/d in unknownreagents)
+ user.show_message(unknownreagents[d])
+ else
+ user.show_message("Warning: Unknown substance[(unknown>1)?"s":""] detected in subject's blood.")
if(C.ingested && C.ingested.total_volume)
var/unknown = 0
- for(var/datum/reagent/R in C.ingested.reagent_list)
- if(R.scannable)
- user << "[R.name] found in subject's stomach."
+ var/stomachreagentdata[0]
+ var/stomachunknownreagents[0]
+ for(var/B in C.ingested.reagent_list)
+ var/datum/reagent/T = B
+ if(T.scannable)
+ stomachreagentdata["[T.id]"] = " [round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]"
+ if (advscan == 0 || showadvscan == 0)
+ user.show_message("[T.name] found in subject's stomach.")
else
++unknown
+ stomachunknownreagents["[T.id]"] = " [round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]"
+ if(advscan >= 1 && showadvscan == 1)
+ user.show_message("Beneficial reagents detected in subject's stomach:")
+ for(var/d in stomachreagentdata)
+ user.show_message(stomachreagentdata[d])
if(unknown)
- user << "Non-medical reagent[(unknown > 1)?"s":""] found in subject's stomach."
+ if(advscan >= 3 && showadvscan == 1)
+ user.show_message("Warning: Non-medical reagent[(unknown > 1)?"s":""] found in subject's stomach:")
+ for(var/d in stomachunknownreagents)
+ user.show_message(stomachunknownreagents[d])
+ else
+ user.show_message("Unknown substance[(unknown > 1)?"s":""] found in subject's stomach.")
if(C.virus2.len)
for (var/ID in C.virus2)
if (ID in virusDB)
var/datum/data/record/V = virusDB[ID]
user.show_message("Warning: Pathogen [V.fields["name"]] detected in subject's blood. Known antigen : [V.fields["antigen"]]")
-// user.show_message(text("Warning: Unknown pathogen detected in subject's blood."))
+ else
+ user.show_message("Warning: Unknown pathogen detected in subject's blood.")
if (M.getCloneLoss())
user.show_message("Subject appears to have been imperfectly cloned.")
// if (M.reagents && M.reagents.get_reagent_amount("inaprovaline"))
@@ -145,6 +185,8 @@ REAGENT SCANNER
user.show_message("Severe brain damage detected. Subject likely to have a traumatic brain injury.")
else if (M.getBrainLoss() >= 10)
user.show_message("Significant brain damage detected. Subject may have had a concussion.")
+ else if (M.getBrainLoss() >= 1 && advscan >= 2 && showadvscan == 1)
+ user.show_message("Minor brain damage detected.")
if(ishuman(M))
var/mob/living/carbon/human/H = M
for(var/name_i in H.internal_organs_by_name)
@@ -166,21 +208,28 @@ REAGENT SCANNER
var/limb = e.name
if(e.status & ORGAN_BROKEN)
if(((e.name == "l_arm") || (e.name == "r_arm") || (e.name == "l_leg") || (e.name == "r_leg")) && (!e.splinted))
- user << "Unsecured fracture in subject [limb]. Splinting recommended for transport."
+ to_chat(user, "Unsecured fracture in subject [limb]. Splinting recommended for transport.")
if(e.has_infected_wound())
- user << "Infected wound detected in subject [limb]. Disinfection recommended."
+ to_chat(user, "Infected wound detected in subject [limb]. Disinfection recommended.")
for(var/name in H.organs_by_name)
var/obj/item/organ/external/e = H.organs_by_name[name]
if(e && e.status & ORGAN_BROKEN)
- user.show_message(text("Bone fractures detected. Advanced scanner required for location."), 1)
- break
+ if(advscan >= 1 && showadvscan == 1)
+ user.show_message(text("Bone fractures detected in subject [e.name]."), 1)
+ else
+ user.show_message(text("Bone fractures detected. Advanced scanner required for location."), 1)
+ break
for(var/obj/item/organ/external/e in H.organs)
if(!e)
continue
for(var/datum/wound/W in e.wounds) if(W.internal)
- user.show_message(text("Internal bleeding detected. Advanced scanner required for location."), 1)
- break
+ if(advscan >= 1 && showadvscan == 1)
+ user.show_message(text("Internal bleeding detected in subject [e.name]."), 1)
+ else
+ user.show_message(text("Internal bleeding detected. Advanced scanner required for location."), 1)
+ break
+ break
if(M:vessel)
var/blood_volume = H.vessel.get_reagent_amount("blood")
@@ -202,10 +251,37 @@ REAGENT SCANNER
mode = !mode
switch (mode)
if(1)
- usr << "The scanner now shows specific limb damage."
+ to_chat(usr, "The scanner now shows specific limb damage.")
if(0)
- usr << "The scanner no longer shows limb damage."
+ to_chat(usr, "The scanner no longer shows limb damage.")
+/obj/item/device/healthanalyzer/proc/toggle_adv()
+ set name = "Toggle Advanced Scan"
+ set category = "Object"
+
+ showadvscan = !showadvscan
+ switch (showadvscan)
+ if(1)
+ to_chat(usr, "The scanner will now perform an advanced analysis.")
+ if(0)
+ to_chat(usr, "The scanner will now perform a basic analysis.")
+
+/obj/item/device/healthanalyzer/improved //reports bone fractures, IB, quantity of beneficial reagents in stomach; also regular health analyzer stuff
+ name = "advanced health analyzer"
+ desc = "A miracle of medical technology, this handheld scanner can produce an accurate and specific report of a patient's biosigns."
+ advscan = 1
+ origin_tech = list(TECH_MAGNET = 5, TECH_BIO = 6)
+ icon_state = "advhealth"
+
+/obj/item/device/healthanalyzer/advanced //reports all of the above, as well as radiation severity and minor brain damage
+ name = "advanced health analyzer"
+ advscan = 2
+ icon_state = "advhealth"
+
+/obj/item/device/healthanalyzer/enhanced //reports all of the above, as well as name and quantity of nonmed reagents in stomach
+ name = "phasic health analyzer"
+ advscan = 3
+ icon_state = "advhealth"
/obj/item/device/analyzer
name = "analyzer"
@@ -235,7 +311,7 @@ REAGENT SCANNER
if (user.stat)
return
if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
- usr << "You don't have the dexterity to do this!"
+ to_chat(usr, "You don't have the dexterity to do this!")
return
analyze_gases(src, user)
@@ -274,14 +350,14 @@ REAGENT SCANNER
if (user.stat)
return
if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
- user << "You don't have the dexterity to do this!"
+ to_chat(user, "You don't have the dexterity to do this!")
return
if(reagents.total_volume)
var/list/blood_traces = list()
for(var/datum/reagent/R in reagents.reagent_list)
if(R.id != "blood")
reagents.clear_reagents()
- user << "The sample was contaminated! Please insert another sample"
+ to_chat(user, "The sample was contaminated! Please insert another sample")
return
else
blood_traces = params2list(R.data["trace_chem"])
@@ -292,7 +368,7 @@ REAGENT SCANNER
dat += "[R] ([blood_traces[R]] units) "
else
dat += "[R] "
- user << "[dat]"
+ to_chat(user, "[dat]")
reagents.clear_reagents()
return
@@ -325,7 +401,7 @@ REAGENT SCANNER
if (user.stat)
return
if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
- user << "You don't have the dexterity to do this!"
+ to_chat(user, "You don't have the dexterity to do this!")
return
if(!istype(O))
return
@@ -335,9 +411,9 @@ REAGENT SCANNER
if(O.reagents.reagent_list.len > 0)
var/one_percent = O.reagents.total_volume / 100
for (var/datum/reagent/R in O.reagents.reagent_list)
- dat += "\n \t [R][details ? ": [R.volume / one_percent]%" : ""]"
+ dat += "\n \t [R][details ? ": [R.volume / one_percent]%" : ""]"
if(dat)
- user << "Chemicals found: [dat]"
+ to_chat(user, "Chemicals found: [dat]")
else
user << "No active chemical agents found in [O]."
else
diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm
index d05289e2cb..159134f83c 100644
--- a/code/game/objects/items/devices/spy_bug.dm
+++ b/code/game/objects/items/devices/spy_bug.dm
@@ -1,115 +1,196 @@
-/obj/item/device/spy_bug
- name = "bug"
- desc = "" // Nothing to see here
+/obj/item/device/camerabug
+ name = "mobile camera pod"
+ desc = "A camera pod used by tactical operators. Must be linked to a camera scanner unit."
+ icon = 'icons/obj/grenade.dmi'
+ icon_state = "camgrenade"
+ item_state = "empgrenade"
+ flags = CONDUCT
+ w_class = ITEMSIZE_SMALL
+ force = 0
+ throwforce = 5.0
+ throw_range = 15
+ throw_speed = 3
+ origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1)
+ var/obj/item/device/bug_monitor/linkedmonitor
+ var/brokentype = /obj/item/brokenbug
+
+// var/obj/item/device/radio/bug/radio
+ var/obj/machinery/camera/bug/camera
+
+/obj/item/device/camerabug/New()
+ ..()
+// radio = new(src)
+ camera = new(src)
+
+/obj/item/device/camerabug/attack_self(mob/user)
+ if(user.a_intent == I_HURT)
+ to_chat(user, "You crush the [src] under your foot, breaking it.")
+ visible_message("[user.name] crushes the [src] under their foot, breaking it!")
+ new brokentype(get_turf(src))
+ spawn(0)
+ qdel(src)
+/* else
+ user.set_machine(radio)
+ radio.interact(user)
+*/
+/obj/item/device/camerabug/verb/reset()
+ set name = "Reset camera bug"
+ set category = "Object"
+ if(linkedmonitor)
+ linkedmonitor.unpair(src)
+ linkedmonitor = null
+ qdel(camera)
+ camera = new(src)
+ to_chat(usr, "You turn the [src] off and on again, delinking it from any monitors.")
+
+/obj/item/brokenbug
+ name = "broken mobile camera pod"
+ desc = "A camera pod formerly used by tactical operators. The lens is smashed, and the circuits are damaged beyond repair."
+ icon = 'icons/obj/grenade.dmi'
+ icon_state = "camgrenadebroken"
+ item_state = "empgrenade"
+ flags = CONDUCT
+ force = 5.0
+ w_class = ITEMSIZE_SMALL
+ throwforce = 5.0
+ throw_range = 15
+ throw_speed = 3
+ origin_tech = list(TECH_ENGINEERING = 1)
+
+/obj/item/brokenbug/spy
+ name = "broken bug"
+ desc = "" //Even when it's broken it's inconspicuous
icon = 'icons/obj/weapons.dmi'
icon_state = "eshield0"
item_state = "nothing"
layer = TURF_LAYER+0.2
-
- flags = CONDUCT
- force = 5.0
w_class = ITEMSIZE_TINY
slot_flags = SLOT_EARS
+ origin_tech = list(TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) //crush it and you lose the data
+ flags = CONDUCT
+ force = 0
throwforce = 5.0
throw_range = 15
throw_speed = 3
+/obj/item/device/camerabug/spy
+ name = "bug"
+ desc = "" //Nothing to see here
+ icon = 'icons/obj/weapons.dmi'
+ icon_state = "eshield0"
+ item_state = "nothing"
+ layer = TURF_LAYER+0.2
+ w_class = ITEMSIZE_TINY
+ slot_flags = SLOT_EARS
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3)
- var/obj/item/device/radio/spy/radio
- var/obj/machinery/camera/spy/camera
-
-/obj/item/device/spy_bug/New()
- ..()
- radio = new(src)
- camera = new(src)
-
-/obj/item/device/spy_bug/examine(mob/user)
+/obj/item/device/camerabug/examine(mob/user)
. = ..(user, 0)
if(.)
- user << "It's a tiny camera, microphone, and transmission device in a happy union."
- user << "Needs to be both configured and brought in contact with monitor device to be fully functional."
+ to_chat(user, "It has a tiny camera inside. Needs to be both configured and brought in contact with monitor device to be fully functional.")
-/obj/item/device/spy_bug/attack_self(mob/user)
- radio.attack_self(user)
-
-/obj/item/device/spy_bug/attackby(obj/W as obj, mob/living/user as mob)
- if(istype(W, /obj/item/device/spy_monitor))
- var/obj/item/device/spy_monitor/SM = W
- SM.pair(src, user)
+/obj/item/device/camerabug/attackby(obj/item/W as obj, mob/living/user as mob)
+ if(istype(W, /obj/item/device/bug_monitor))
+ var/obj/item/device/bug_monitor/SM = W
+ if(!linkedmonitor)
+ to_chat(user, "\The [src] has been paired with \the [SM].")
+ SM.pair(src)
+ linkedmonitor = SM
+ else if (linkedmonitor == SM)
+ to_chat(user, "\The [src] has been unpaired from \the [SM].")
+ linkedmonitor.unpair(src)
+ linkedmonitor = null
+ else
+ to_chat(user, "Error: The device is linked to another monitor.")
else
+ if(W.force >= 5)
+ visible_message("\The [src] lens shatters!")
+ new brokentype(get_turf(src))
+ if(linkedmonitor)
+ linkedmonitor.unpair(src)
+ linkedmonitor = null
+ spawn(0)
+ qdel(src)
..()
-/obj/item/device/spy_bug/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
+/obj/item/device/camerabug/bullet_act()
+ visible_message("The [src] lens shatters!")
+ new brokentype(get_turf(src))
+ if(linkedmonitor)
+ linkedmonitor.unpair(src)
+ linkedmonitor = null
+ spawn(0)
+ qdel(src)
+
+/obj/item/device/camerabug/Destroy()
+ if(linkedmonitor)
+ linkedmonitor.unpair(src)
+ linkedmonitor = null
+ ..()
+/*
+/obj/item/device/camerabug/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
radio.hear_talk(M, msg, speaking)
-
-
-/obj/item/device/spy_monitor
- name = "\improper PDA"
- desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."
- icon = 'icons/obj/pda.dmi'
- icon_state = "pda"
+*/
+/obj/item/device/bug_monitor
+ name = "mobile camera pod monitor"
+ desc = "A portable camera console designed to work with mobile camera pods."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "forensic0"
item_state = "electronic"
-
- w_class = ITEMSIZE_SMALL
-
- origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3)
+ w_class = ITEMSIZE_SMALL
+ origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1)
var/operating = 0
- var/obj/item/device/radio/spy/radio
- var/obj/machinery/camera/spy/selected_camera
- var/list/obj/machinery/camera/spy/cameras = new()
-
-/obj/item/device/spy_monitor/New()
+// var/obj/item/device/radio/bug/radio
+ var/obj/machinery/camera/bug/selected_camera
+ var/list/obj/machinery/camera/bug/cameras = new()
+/*
+/obj/item/device/bug_monitor/New()
radio = new(src)
-
-/obj/item/device/spy_monitor/examine(mob/user)
- . = ..(user, 1)
- if(.)
- user << "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made."
-
-/obj/item/device/spy_monitor/attack_self(mob/user)
+*/
+/obj/item/device/bug_monitor/attack_self(mob/user)
if(operating)
return
- radio.attack_self(user)
+// radio.attack_self(user)
view_cameras(user)
-/obj/item/device/spy_monitor/attackby(obj/W as obj, mob/living/user as mob)
- if(istype(W, /obj/item/device/spy_bug))
- pair(W, user)
+/obj/item/device/bug_monitor/attackby(obj/item/W as obj, mob/living/user as mob)
+ if(istype(W, /obj/item/device/camerabug))
+ W.attackby(src, user)
else
return ..()
-/obj/item/device/spy_monitor/proc/pair(var/obj/item/device/spy_bug/SB, var/mob/living/user)
+/obj/item/device/bug_monitor/proc/unpair(var/obj/item/device/camerabug/SB)
if(SB.camera in cameras)
- user << "\The [SB] has been unpaired from \the [src]."
cameras -= SB.camera
- else
- user << "\The [SB] has been paired with \the [src]."
- cameras += SB.camera
-/obj/item/device/spy_monitor/proc/view_cameras(mob/user)
+/obj/item/device/bug_monitor/proc/pair(var/obj/item/device/camerabug/SB)
+ cameras += SB.camera
+
+/obj/item/device/bug_monitor/proc/view_cameras(mob/user)
if(!can_use_cam(user))
return
selected_camera = cameras[1]
+ user.reset_view(selected_camera)
view_camera(user)
operating = 1
while(selected_camera && Adjacent(user))
- selected_camera = input("Select camera bug to view.") as null|anything in cameras
+ selected_camera = input("Select camera to view.") as null|anything in cameras
selected_camera = null
operating = 0
-/obj/item/device/spy_monitor/proc/view_camera(mob/user)
+/obj/item/device/bug_monitor/proc/view_camera(mob/user)
spawn(0)
while(selected_camera && Adjacent(user))
var/turf/T = get_turf(selected_camera)
if(!T || !is_on_same_plane_or_station(T.z, user.z) || !selected_camera.can_use())
user.unset_machine()
user.reset_view(null)
- user << "[selected_camera] unavailable."
+ to_chat(user, "Link to [selected_camera] has been lost.")
+ src.unpair(selected_camera.loc)
sleep(90)
else
user.set_machine(selected_camera)
@@ -118,37 +199,67 @@
user.unset_machine()
user.reset_view(null)
-/obj/item/device/spy_monitor/proc/can_use_cam(mob/user)
+/obj/item/device/bug_monitor/proc/can_use_cam(mob/user)
if(operating)
return
if(!cameras.len)
- user << "No paired cameras detected!"
- user << "Bring a bug in contact with this device to pair the camera."
+ to_chat(user, "No paired cameras detected!")
+ to_chat(user, "Bring a camera in contact with this device to pair the camera.")
return
return 1
-
-/obj/item/device/spy_monitor/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
+/*
+/obj/item/device/bug_monitor/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
return radio.hear_talk(M, msg, speaking)
+*/
+/obj/item/device/bug_monitor/spy
+ name = "\improper PDA"
+ desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."
+ icon = 'icons/obj/pda.dmi'
+ icon_state = "pda"
+ item_state = "electronic"
+ origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3)
+/obj/item/device/bug_monitor/spy/examine(mob/user)
+ . = ..(user, 1)
+ if(.)
+ to_chat(user, "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made.")
-/obj/machinery/camera/spy
- // These cheap toys are accessible from the mercenary camera console as well
+/obj/machinery/camera/bug/check_eye(var/mob/user as mob)
+ return 0
+
+/obj/machinery/camera/bug
+ network = list(NETWORK_SECURITY)
+
+/obj/machinery/camera/bug/New()
+ ..()
+ name = "Camera #[rand(1000,9999)]"
+ c_tag = name
+
+/obj/machinery/camera/bug/spy
+ // These cheap toys are accessible from the mercenary camera console as well - only the antag ones though!
network = list(NETWORK_MERCENARY)
-/obj/machinery/camera/spy/New()
+/obj/machinery/camera/bug/spy/New()
..()
name = "DV-136ZB #[rand(1000,9999)]"
c_tag = name
-/obj/machinery/camera/spy/check_eye(var/mob/user as mob)
- return 0
+/* //These were originally supposed to have radios in them. Doesn't work.
+/obj/item/device/radio/bug
+ listening = 0 //turn it on first
+ frequency = 1359 //sec comms
+ broadcasting = 0
+ canhear_range = 1
+ name = "camera bug device"
+ icon_state = "syn_cypherkey"
-/obj/item/device/radio/spy
+/obj/item/device/radio/bug/spy
listening = 0
frequency = 1473
broadcasting = 0
canhear_range = 1
name = "spy device"
icon_state = "syn_cypherkey"
+ */
\ No newline at end of file
diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm
index 0e063bd6ea..83024e16e2 100644
--- a/code/game/objects/items/devices/uplink.dm
+++ b/code/game/objects/items/devices/uplink.dm
@@ -19,13 +19,15 @@
/obj/item/device/uplink/nano_host()
return loc
-/obj/item/device/uplink/New(var/location, var/datum/mind/owner, var/telecrystals = DEFAULT_TELECRYSTAL_AMOUNT)
+/obj/item/device/uplink/New(var/location, var/datum/mind/owner = null, var/telecrystals = DEFAULT_TELECRYSTAL_AMOUNT)
..()
- if(owner) //VOREStation Edit - Owner optional
- src.uplink_owner = owner
- uses = owner.tcrystals
+ src.uplink_owner = owner
purchase_log = list()
world_uplinks += src
+ if(owner)
+ uses = owner.tcrystals
+ else
+ uses = telecrystals
processing_objects += src
/obj/item/device/uplink/Destroy()
diff --git a/code/game/objects/items/gunbox_vr.dm b/code/game/objects/items/gunbox_vr.dm
new file mode 100644
index 0000000000..1ddf43c272
--- /dev/null
+++ b/code/game/objects/items/gunbox_vr.dm
@@ -0,0 +1,18 @@
+/obj/item/gunbox
+ name = "security sidearm box"
+ desc = "A secure box containing a security sidearm."
+
+/obj/item/gunbox/attack_self(mob/living/user)
+ var/list/options = list()
+ options["M1911 (.45)"] = list(/obj/item/weapon/gun/projectile/colt/detective, /obj/item/ammo_magazine/m45/rubber, /obj/item/ammo_magazine/m45/rubber)
+ options["NT Mk58 (.45)"] = list(/obj/item/weapon/gun/projectile/sec, /obj/item/ammo_magazine/m45/rubber, /obj/item/ammo_magazine/m45/rubber)
+ options["SW 625 Revolver (.45)"] = list(/obj/item/weapon/gun/projectile/revolver/detective45, /obj/item/ammo_magazine/s45/rubber, /obj/item/ammo_magazine/s45/rubber)
+ options["P92X (9mm)"] = list(/obj/item/weapon/gun/projectile/p92x/sec, /obj/item/ammo_magazine/m9mm/rubber, /obj/item/ammo_magazine/m9mm/rubber)
+ var/choice = input(user,"Would you prefer a pistol or a revolver?") as null|anything in options
+ if(src && choice)
+ var/list/things_to_spawn = options[choice]
+ for(var/new_type in things_to_spawn) // Spawn all the things, the gun and the ammo.
+ var/atom/movable/AM = new new_type(get_turf(src))
+ if(istype(AM, /obj/item/weapon/gun))
+ to_chat(user, "You have chosen \the [AM]. Say hello to your new friend.")
+ qdel(src)
\ No newline at end of file
diff --git a/code/game/objects/items/paintkit.dm b/code/game/objects/items/paintkit.dm
index 78e42f71a8..259a054c16 100644
--- a/code/game/objects/items/paintkit.dm
+++ b/code/game/objects/items/paintkit.dm
@@ -1,15 +1,18 @@
/obj/item/device/kit
icon_state = "modkit"
icon = 'icons/obj/device.dmi'
- var/new_name = "mech" //What is the variant called?
- var/new_desc = "A mech." //How is the new mech described?
- var/new_icon = "ripley" //What base icon will the new mech use?
+ w_class = ITEMSIZE_SMALL
+ var/new_name = "custom item"
+ var/new_desc = "A custom item."
+ var/new_icon
var/new_icon_file
+ var/new_icon_override_file
var/uses = 1 // Uses before the kit deletes itself.
+ var/list/allowed_types = list()
/obj/item/device/kit/examine()
..()
- usr << "It has [uses] [uses>1?"uses":"use"] left."
+ to_chat(usr, "It has [uses] use\s left.")
/obj/item/device/kit/proc/use(var/amt, var/mob/user)
uses -= amt
@@ -18,6 +21,42 @@
user.drop_item()
qdel(src)
+/obj/item/device/kit/proc/can_customize(var/obj/item/I)
+ return is_type_in_list(I, allowed_types)
+
+/obj/item/device/kit/proc/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data)
+ new_name = kit_name
+ new_desc = kit_desc
+ new_icon = kit_icon
+ new_icon_file = kit_icon_file
+ new_icon_override_file = kit_icon_override_file
+
+ for(var/path in splittext(additional_data, ", "))
+ allowed_types |= text2path(path)
+
+/obj/item/device/kit/proc/customize(var/obj/item/I, var/mob/user)
+ if(can_customize(I))
+ I.name = new_name ? new_name : I.name
+ I.desc = new_desc ? new_desc : I.desc
+ I.icon = new_icon_file ? new_icon_file : I.icon
+ I.icon_override = new_icon_override_file ? new_icon_override_file : I.icon_override
+ if(new_icon)
+ I.icon_state = new_icon
+ var/obj/item/clothing/under/U = I
+ if(istype(U))
+ U.worn_state = I.icon_state
+ U.update_rolldown_status()
+ use(1, user)
+
+// Generic use
+/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(istype(W, /obj/item/device/kit))
+ var/obj/item/device/kit/K = W
+ K.customize(src, user)
+ return
+
+ ..()
+
// Root hardsuit kit defines.
// Icons for modified hardsuits need to be in the proper .dmis because suit cyclers may cock them up.
/obj/item/device/kit/suit
@@ -25,88 +64,141 @@
desc = "A kit for modifying a voidsuit."
uses = 2
var/new_light_overlay
- var/new_mob_icon_file
+
+/obj/item/device/kit/suit/can_customize(var/obj/item/I)
+ return istype(I, /obj/item/clothing/head/helmet/space/void) || istype(I, /obj/item/clothing/suit/space/void) || istype(I, /obj/item/clothing/suit/storage/hooded/explorer)
+
+/obj/item/device/kit/suit/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data)
+ ..()
+
+ new_light_overlay = additional_data
+
+
+/obj/item/device/kit/suit/customize(var/obj/item/I, var/mob/user)
+ if(can_customize(I))
+ if(istype(I, /obj/item/clothing/head/helmet/space/void))
+ var/obj/item/clothing/head/helmet/space/void/helmet = I
+ helmet.name = "[new_name] suit helmet"
+ helmet.desc = new_desc
+ helmet.icon_state = "[new_icon]_helmet"
+ helmet.item_state = "[new_icon]_helmet"
+ if(new_icon_file)
+ helmet.icon = new_icon_file
+ if(new_icon_override_file)
+ helmet.icon_override = new_icon_override_file
+ if(new_light_overlay)
+ helmet.light_overlay = new_light_overlay
+ to_chat(user, "You set about modifying the helmet into [helmet].")
+ var/mob/living/carbon/human/H = user
+ if(istype(H))
+ helmet.species_restricted = list(H.species.get_bodytype(H))
+ else if(istype(I, /obj/item/clothing/suit/storage/hooded))
+ var/obj/item/clothing/suit/storage/hooded/suit = I
+ suit.name = "[new_name] suit"
+ suit.desc = new_desc
+ suit.icon_state = "[new_icon]_suit"
+ suit.toggleicon = "[new_icon]_suit"
+ suit.item_state = "[new_icon]_suit"
+ var/obj/item/clothing/head/hood/S = suit.hood
+ S.icon_state = "[new_icon]_helmet"
+ S.item_state = "[new_icon]_helmet"
+ if(new_icon_file)
+ suit.icon = new_icon_file
+ S.icon = new_icon_file
+ if(new_icon_override_file)
+ suit.icon_override = new_icon_override_file
+ S.icon_override = new_icon_override_file
+ to_chat(user, "You set about modifying the suit into [suit].")
+ var/mob/living/carbon/human/H = user
+ if(istype(H))
+ suit.species_restricted = list(H.species.get_bodytype(H))
+ else
+ var/obj/item/clothing/suit/space/void/suit = I
+ suit.name = "[new_name] voidsuit"
+ suit.desc = new_desc
+ suit.icon_state = "[new_icon]_suit"
+ suit.item_state = "[new_icon]_suit"
+ if(new_icon_file)
+ suit.icon = new_icon_file
+ if(new_icon_override_file)
+ suit.icon_override = new_icon_override_file
+ to_chat(user, "You set about modifying the suit into [suit].")
+ var/mob/living/carbon/human/H = user
+ if(istype(H))
+ suit.species_restricted = list(H.species.get_bodytype(H))
+ use(1,user)
/obj/item/clothing/head/helmet/space/void/attackby(var/obj/item/O, var/mob/user)
if(istype(O,/obj/item/device/kit/suit))
var/obj/item/device/kit/suit/kit = O
- name = "[kit.new_name] suit helmet"
- desc = kit.new_desc
- icon_state = "[kit.new_icon]_helmet"
- item_state = "[kit.new_icon]_helmet"
- if(kit.new_icon_file)
- icon = kit.new_icon_file
- if(kit.new_mob_icon_file)
- icon_override = kit.new_mob_icon_file
- if(kit.new_light_overlay)
- light_overlay = kit.new_light_overlay
- user << "You set about modifying the helmet into [src]."
- var/mob/living/carbon/human/H = user
- if(istype(H))
- species_restricted = list(H.species.get_bodytype())
- kit.use(1,user)
- return 1
+ kit.customize(src, user)
+ return
return ..()
/obj/item/clothing/suit/space/void/attackby(var/obj/item/O, var/mob/user)
if(istype(O,/obj/item/device/kit/suit))
var/obj/item/device/kit/suit/kit = O
- name = "[kit.new_name] voidsuit"
- desc = kit.new_desc
- icon_state = "[kit.new_icon]_suit"
- item_state = "[kit.new_icon]_suit"
- if(kit.new_icon_file)
- icon = kit.new_icon_file
- if(kit.new_mob_icon_file)
- icon_override = kit.new_mob_icon_file
- user << "You set about modifying the suit into [src]."
- var/mob/living/carbon/human/H = user
- if(istype(H))
- species_restricted = list(H.species.get_bodytype())
- kit.use(1,user)
- return 1
+ kit.customize(src, user)
+ return
return ..()
+/obj/item/clothing/suit/storage/hooded/attackby(var/obj/item/O, var/mob/user)
+ if(istype(O,/obj/item/device/kit/suit))
+ var/obj/item/device/kit/suit/kit = O
+ kit.customize(src, user)
+ return
+ return ..()
+
+
/obj/item/device/kit/paint
name = "mecha customisation kit"
desc = "A kit containing all the needed tools and parts to repaint a mech."
var/removable = null
- var/list/allowed_types = list()
+
+/obj/item/device/kit/paint/can_customize(var/obj/mecha/M)
+ if(!istype(M))
+ return 0
+
+ for(var/type in allowed_types)
+ if(type == M.initial_icon)
+ return 1
+
+/obj/item/device/kit/paint/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data)
+ ..()
+
+ allowed_types = splittext(additional_data, ", ")
+
/obj/item/device/kit/paint/examine()
..()
- usr << "This kit will convert an exosuit into: [new_name]."
- usr << "This kit can be used on the following exosuit models:"
+ to_chat(usr, "This kit will convert an exosuit into: [new_name].")
+ to_chat(usr, "This kit can be used on the following exosuit models:")
for(var/exotype in allowed_types)
- usr << "- [capitalize(exotype)]"
+ to_chat(usr, "- [capitalize(exotype)]")
+
+/obj/item/device/kit/paint/customize(var/obj/mecha/M, var/mob/user)
+ if(!can_customize(M))
+ to_chat(user, "That kit isn't meant for use on this class of exosuit.")
+ return
+
+ if(M.occupant)
+ to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!")
+ return
+
+ user.visible_message("[user] opens [src] and spends some quality time customising [M].")
+ M.name = new_name
+ M.desc = new_desc
+ M.initial_icon = new_icon
+ if(new_icon_file)
+ M.icon = new_icon_file
+ M.reset_icon()
+ use(1, user)
/obj/mecha/attackby(var/obj/item/weapon/W, var/mob/user)
if(istype(W, /obj/item/device/kit/paint))
- if(occupant)
- user << "You can't customize a mech while someone is piloting it - that would be unsafe!"
- return
-
var/obj/item/device/kit/paint/P = W
- var/found = null
-
- for(var/type in P.allowed_types)
- if(type==src.initial_icon)
- found = 1
- break
-
- if(!found)
- user << "That kit isn't meant for use on this class of exosuit."
- return
-
- user.visible_message("[user] opens [P] and spends some quality time customising [src].")
- src.name = P.new_name
- src.desc = P.new_desc
- src.initial_icon = P.new_icon
- if(P.new_icon_file)
- src.icon = P.new_icon_file
- src.reset_icon()
- P.use(1, user)
- return 1
+ P.customize(src, user)
+ return
else
return ..()
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index cedd54ac6f..0a626b6a8e 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -160,6 +160,29 @@
to_chat(usr, "There's no mounting point for the module!")
return 0
+/obj/item/borg/upgrade/advhealth
+ name = "advanced health analyzer module"
+ desc = "A carbon dioxide jetpack suitable for low-gravity operations."
+ icon_state = "cyborg_upgrade3"
+ item_state = "cyborg_upgrade"
+ require_module = 1
+
+/obj/item/borg/upgrade/advhealth/action(var/mob/living/silicon/robot/R)
+ if(..()) return 0
+
+ var/obj/item/device/healthanalyzer/advanced/T = locate() in R.module
+ if(!T)
+ T = locate() in R.module.contents
+ if(!T)
+ T = locate() in R.module.modules
+ if(!T)
+ R.module.modules += new/obj/item/device/healthanalyzer/advanced
+ return 1
+ if(T)
+ to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!")
+ to_chat(usr, "There's no mounting point for the module!")
+ return 0
+
/obj/item/borg/upgrade/syndicate/
name = "scrambled equipment module"
desc = "Unlocks new and often deadly module specific items of a robot"
diff --git a/code/game/objects/items/robot/robot_upgrades_vr.dm b/code/game/objects/items/robot/robot_upgrades_vr.dm
new file mode 100644
index 0000000000..3baf8aa30e
--- /dev/null
+++ b/code/game/objects/items/robot/robot_upgrades_vr.dm
@@ -0,0 +1,8 @@
+/obj/item/borg/upgrade/language/action(var/mob/living/silicon/robot/R)
+ if(..())
+ R.add_language(LANGUAGE_BIRDSONG, 1)
+ R.add_language(LANGUAGE_SAGARU, 1)
+ R.add_language(LANGUAGE_CANILUNZT, 1)
+ R.add_language(LANGUAGE_ECUREUILIAN, 1)
+ R.add_language(LANGUAGE_DAEMON, 1)
+ R.add_language(LANGUAGE_ENOCHIAN, 1)
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/marker_beacons.dm b/code/game/objects/items/stacks/marker_beacons.dm
new file mode 100644
index 0000000000..9978ef6b0d
--- /dev/null
+++ b/code/game/objects/items/stacks/marker_beacons.dm
@@ -0,0 +1,138 @@
+/*****************Marker Beacons**************************/
+var/list/marker_beacon_colors = list(
+"Random" = FALSE, //not a true color, will pick a random color
+"Burgundy" = LIGHT_COLOR_FLARE,
+"Bronze" = LIGHT_COLOR_ORANGE,
+"Yellow" = LIGHT_COLOR_YELLOW,
+"Lime" = LIGHT_COLOR_SLIME_LAMP,
+"Olive" = LIGHT_COLOR_GREEN,
+"Jade" = LIGHT_COLOR_BLUEGREEN,
+"Teal" = LIGHT_COLOR_LIGHT_CYAN,
+"Cerulean" = LIGHT_COLOR_BLUE,
+"Indigo" = LIGHT_COLOR_DARK_BLUE,
+"Purple" = LIGHT_COLOR_PURPLE,
+"Violet" = LIGHT_COLOR_LAVENDER,
+"Fuchsia" = LIGHT_COLOR_PINK
+)
+
+/obj/item/stack/marker_beacon
+ name = "marker beacons"
+ singular_name = "marker beacon"
+ desc = "Prismatic path illumination devices. Used by explorers and miners to mark paths and warn of danger."
+ description_info = "Use inhand to drop one marker beacon. You can pick them up again with an empty hand or \
+ hitting them with this marker stack. Alt-click to select a specific color."
+ icon = 'icons/obj/lighting.dmi'
+ icon_state = "marker"
+ max_amount = 100
+ no_variants = TRUE
+ var/picked_color = "random"
+
+/obj/item/stack/marker_beacon/ten
+ amount = 10
+
+/obj/item/stack/marker_beacon/thirty
+ amount = 30
+
+/obj/item/stack/marker_beacon/hundred
+ amount = 100
+
+/obj/item/stack/marker_beacon/initialize()
+ . = ..()
+ update_icon()
+
+/obj/item/stack/marker_beacon/examine(mob/user)
+ ..()
+ to_chat(user, "Use in-hand to place a [singular_name].")
+ to_chat(user, "Alt-click to select a color. Current color is [picked_color].")
+
+/obj/item/stack/marker_beacon/update_icon()
+ icon_state = "[initial(icon_state)][lowertext(picked_color)]"
+
+/obj/item/stack/marker_beacon/attack_self(mob/user)
+ if(!isturf(user.loc))
+ to_chat(user, "You need more space to place a [singular_name] here.")
+ return
+ if(locate(/obj/structure/marker_beacon) in user.loc)
+ to_chat(user, "There is already a [singular_name] here.")
+ return
+ if(use(1))
+ to_chat(user, "You activate and anchor [amount ? "a":"the"] [singular_name] in place.")
+ playsound(user, 'sound/machines/click.ogg', 50, 1)
+ var/obj/structure/marker_beacon/M = new(user.loc, picked_color)
+ transfer_fingerprints_to(M)
+
+/obj/item/stack/marker_beacon/AltClick(mob/living/user)
+ if(user.incapacitated() || !istype(user))
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user))
+ return
+ var/input_color = input(user, "Choose a color.", "Beacon Color") as null|anything in marker_beacon_colors
+ if(user.incapacitated() || !istype(user) || !in_range(src, user))
+ return
+ if(input_color)
+ picked_color = input_color
+ update_icon()
+
+/obj/structure/marker_beacon
+ name = "marker beacon"
+ desc = "A prismatic path illumination device. It is anchored in place and glowing steadily."
+ icon = 'icons/obj/lighting.dmi'
+ icon_state = "marker"
+// layer = BELOW_OPEN_DOOR_LAYER
+ anchored = TRUE
+ light_range = 2
+ light_power = 3
+ var/remove_speed = 15
+ var/picked_color
+
+/obj/structure/marker_beacon/New(newloc, set_color)
+ . = ..()
+ picked_color = set_color
+ update_icon()
+
+/obj/structure/marker_beacon/examine(mob/user)
+ ..()
+ to_chat(user, "Alt-click to select a color. Current color is [picked_color].")
+
+/obj/structure/marker_beacon/update_icon()
+ while(!picked_color || !marker_beacon_colors[picked_color])
+ picked_color = pick(marker_beacon_colors)
+ icon_state = "[initial(icon_state)][lowertext(picked_color)]-on"
+ set_light(light_range, light_power, marker_beacon_colors[picked_color])
+
+/obj/structure/marker_beacon/attack_hand(mob/living/user)
+ to_chat(user, "You start picking [src] up...")
+ if(do_after(user, remove_speed, target = src))
+ var/obj/item/stack/marker_beacon/M = new(loc)
+ M.picked_color = picked_color
+ M.update_icon()
+ transfer_fingerprints_to(M)
+ if(user.put_in_hands(M, TRUE)) //delete the beacon if it fails
+ playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
+ qdel(src) //otherwise delete us
+
+/obj/structure/marker_beacon/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/stack/marker_beacon))
+ var/obj/item/stack/marker_beacon/M = I
+ to_chat(user, "You start picking [src] up...")
+ if(do_after(user, remove_speed, target = src) && M.amount + 1 <= M.max_amount)
+ M.add(1)
+ playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
+ qdel(src)
+ else
+ return ..()
+
+/obj/structure/marker_beacon/AltClick(mob/living/user)
+ ..()
+ if(user.incapacitated() || !istype(user))
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user))
+ return
+ var/input_color = input(user, "Choose a color.", "Beacon Color") as null|anything in marker_beacon_colors
+ if(user.incapacitated() || !istype(user) || !in_range(src, user))
+ return
+ if(input_color)
+ picked_color = input_color
+ update_icon()
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index d878dd74fe..eb62b5d674 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -164,7 +164,7 @@
singular_name = "advanced trauma kit"
desc = "An advanced trauma kit for severe injuries."
icon_state = "traumakit"
- heal_brute = 5
+ heal_brute = 3
origin_tech = list(TECH_BIO = 1)
/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob)
@@ -225,7 +225,7 @@
singular_name = "advanced burn kit"
desc = "An advanced treatment kit for severe burns."
icon_state = "burnkit"
- heal_burn = 5
+ heal_burn = 3
origin_tech = list(TECH_BIO = 1)
diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm
index 63a0df6d32..7b156c025f 100644
--- a/code/game/objects/items/stacks/nanopaste.dm
+++ b/code/game/objects/items/stacks/nanopaste.dm
@@ -34,7 +34,7 @@
if(!S.get_damage())
user << "Nothing to fix here."
else if(can_use(1))
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(src))
if(S.open >= 2)
if(do_after(user,5 * toolspeed))
S.heal_damage(20, 20, robo_repair = 1)
diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm
index 8ece302f13..531fcbf144 100644
--- a/code/game/objects/items/stacks/sheets/leather.dm
+++ b/code/game/objects/items/stacks/sheets/leather.dm
@@ -101,9 +101,8 @@
//Step one - dehairing.
/obj/item/stack/material/animalhide/attackby(obj/item/weapon/W as obj, mob/user as mob)
if( istype(W, /obj/item/weapon/material/knife) || \
- istype(W, /obj/item/weapon/material/kitchen/utensil/knife) || \
istype(W, /obj/item/weapon/material/twohanded/fireaxe) || \
- istype(W, /obj/item/weapon/material/hatchet) )
+ istype(W, /obj/item/weapon/material/knife/machete/hatchet) )
//visible message on mobs is defined as visible_message(var/message, var/self_message, var/blind_message)
usr.visible_message("\The [usr] starts cutting hair off \the [src]", "You start cutting the hair off \the [src]", "You hear the sound of a knife rubbing against flesh")
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index a79faa60d2..62f02816ed 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -771,13 +771,13 @@
user.visible_message("\The [user] pokes [src].","You poke [src].")
last_message = world.time
-obj/item/toy/plushie/verb/rename_plushie()
+/obj/item/toy/plushie/verb/rename_plushie()
set name = "Name Plushie"
set category = "Object"
set desc = "Give your plushie a cute name!"
- w_class = ITEMSIZE_TINY
var/mob/M = usr
- if(!M.mind) return 0
+ if(!M.mind)
+ return 0
var/input = sanitizeSafe(input("What do you want to name the plushie?", ,""), MAX_NAME_LEN)
diff --git a/code/game/objects/items/trash_vr.dm b/code/game/objects/items/trash_vr.dm
index 57d3bbbef0..58f8e23967 100644
--- a/code/game/objects/items/trash_vr.dm
+++ b/code/game/objects/items/trash_vr.dm
@@ -15,19 +15,19 @@
var/belly = H.vore_selected
var/datum/belly/selected = H.vore_organs[belly]
src.forceMove(H)
- selected.internal_contents += src
+ selected.internal_contents |= src
to_chat(H, "You can taste the flavor of garbage. Wait what?")
return
if(isrobot(M))
var/mob/living/silicon/robot/R = M
- if(R.module.type == /obj/item/weapon/robot_module/scrubpup) // You can now feed the trash borg yay.
+ if(R.module.type == /obj/item/weapon/robot_module/robot/scrubpup) // You can now feed the trash borg yay.
playsound(R.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
user.drop_item()
var/belly = R.vore_selected
var/datum/belly/selected = R.vore_organs[belly]
src.forceMove(R)
- selected.internal_contents += src // Too many hoops and obstacles to stick it into the sleeper module.
+ selected.internal_contents |= src // Too many hoops and obstacles to stick it into the sleeper module.
R.visible_message("[user] feeds [R] with [src]!")
return
..()
diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm
index 5c0f43e139..77c5b59364 100755
--- a/code/game/objects/items/weapons/AI_modules.dm
+++ b/code/game/objects/items/weapons/AI_modules.dm
@@ -25,51 +25,51 @@ AI MODULES
if (istype(AM, /obj/machinery/computer/aiupload))
var/obj/machinery/computer/aiupload/comp = AM
if(comp.stat & NOPOWER)
- usr << "The upload computer has no power!"
+ to_chat(usr, "The upload computer has no power!")
return
if(comp.stat & BROKEN)
- usr << "The upload computer is broken!"
+ to_chat(usr, "The upload computer is broken!")
return
if (!comp.current)
- usr << "You haven't selected an AI to transmit laws to!"
+ to_chat(usr, "You haven't selected an AI to transmit laws to!")
return
if (comp.current.stat == 2 || comp.current.control_disabled == 1)
- usr << "Upload failed. No signal is being detected from the AI."
+ to_chat(usr, "Upload failed. No signal is being detected from the AI.")
else if (comp.current.see_in_dark == 0)
- usr << "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power."
+ to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.")
else
src.transmitInstructions(comp.current, usr)
- comp.current << "These are your laws now:"
+ to_chat(comp.current, "These are your laws now:")
comp.current.show_laws()
for(var/mob/living/silicon/robot/R in mob_list)
if(R.lawupdate && (R.connected_ai == comp.current))
- R << "These are your laws now:"
+ to_chat(R, "These are your laws now:")
R.show_laws()
- usr << "Upload complete. The AI's laws have been modified."
+ to_chat(usr, "Upload complete. The AI's laws have been modified.")
else if (istype(AM, /obj/machinery/computer/borgupload))
var/obj/machinery/computer/borgupload/comp = AM
if(comp.stat & NOPOWER)
- usr << "The upload computer has no power!"
+ to_chat(usr, "The upload computer has no power!")
return
if(comp.stat & BROKEN)
- usr << "The upload computer is broken!"
+ to_chat(usr, "The upload computer is broken!")
return
if (!comp.current)
- usr << "You haven't selected a robot to transmit laws to!"
+ to_chat(usr, "You haven't selected a robot to transmit laws to!")
return
if (comp.current.stat == 2 || comp.current.emagged)
- usr << "Upload failed. No signal is being detected from the robot."
+ to_chat(usr, "Upload failed. No signal is being detected from the robot.")
else if (comp.current.connected_ai)
- usr << "Upload failed. The robot is slaved to an AI."
+ to_chat(usr, "Upload failed. The robot is slaved to an AI.")
else
src.transmitInstructions(comp.current, usr)
- comp.current << "These are your laws now:"
+ to_chat(comp.current, "These are your laws now:")
comp.current.show_laws()
- usr << "Upload complete. The robot's laws have been modified."
+ to_chat(usr, "Upload complete. The robot's laws have been modified.")
else if(istype(AM, /mob/living/silicon/robot))
var/mob/living/silicon/robot/R = AM
@@ -104,7 +104,7 @@ AI MODULES
laws.sync(target, 0)
addAdditionalLaws(target, sender)
- target << "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: "
+ to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ")
target.show_laws()
/obj/item/weapon/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender)
@@ -133,7 +133,7 @@ AI MODULES
/obj/item/weapon/aiModule/safeguard/install(var/obj/machinery/computer/C, var/mob/living/user)
if(!targetName)
- usr << "No name detected on module, please enter one."
+ to_chat(usr, "No name detected on module, please enter one.")
return 0
..()
@@ -159,18 +159,14 @@ AI MODULES
/obj/item/weapon/aiModule/oneHuman/install(var/obj/machinery/computer/C, var/mob/living/user)
if(!targetName)
- usr << "No name detected on module, please enter one."
+ to_chat(usr, "No name detected on module, please enter one.")
return 0
return ..()
/obj/item/weapon/aiModule/oneHuman/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender)
var/law = "Only [targetName] is an crew member."
- if (!target.is_malf_or_traitor()) // Makes sure the AI isn't a traitor before changing their law 0. --NeoFite
- target << law
- target.set_zeroth_law(law)
- lawchanges.Add("The law specified [targetName]")
- else
- lawchanges.Add("The law specified [targetName], but the AI's existing law 0 cannot be overriden.")
+ to_chat(target, law)
+ target.add_supplied_law(16, law)
/******************** ProtectStation ********************/
@@ -255,7 +251,7 @@ AI MODULES
/obj/item/weapon/aiModule/freeform/install(var/obj/machinery/computer/C, var/mob/living/user)
if(!newFreeFormLaw)
- usr << "No law detected on module, please create one."
+ to_chat(usr, "No law detected on module, please create one.")
return 0
..()
@@ -275,7 +271,7 @@ AI MODULES
target.laws.clear_supplied_laws()
target.laws.clear_ion_laws()
- target << "[sender.real_name] attempted to reset your laws using a reset module."
+ to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.")
target.show_laws()
/******************** Purge ********************/
@@ -294,7 +290,7 @@ AI MODULES
target.laws.clear_ion_laws()
target.laws.clear_inherent_laws()
- target << "[sender.real_name] attempted to wipe your laws using a purge module."
+ to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.")
target.show_laws()
/******************** Asimov ********************/
@@ -366,7 +362,7 @@ AI MODULES
/obj/item/weapon/aiModule/freeformcore/install(var/obj/machinery/computer/C, var/mob/living/user)
if(!newFreeFormLaw)
- usr << "No law detected on module, please create one."
+ to_chat(usr, "No law detected on module, please create one.")
return 0
..()
@@ -388,14 +384,14 @@ AI MODULES
log_law_changes(target, sender)
lawchanges.Add("The law is '[newFreeFormLaw]'")
- target << "BZZZZT"
+ to_chat(target, "BZZZZT")
var/law = "[newFreeFormLaw]"
target.add_ion_law(law)
target.show_laws()
/obj/item/weapon/aiModule/syndicate/install(var/obj/machinery/computer/C, var/mob/living/user)
if(!newFreeFormLaw)
- usr << "No law detected on module, please create one."
+ to_chat(usr, "No law detected on module, please create one.")
return 0
..()
diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm
index 48ab1df6bb..c8d4eed3ad 100644
--- a/code/game/objects/items/weapons/cigs_lighters.dm
+++ b/code/game/objects/items/weapons/cigs_lighters.dm
@@ -16,16 +16,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/weapon/flame
var/lit = 0
-/proc/isflamesource(A)
- if(istype(A, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = A
- return (WT.isOn())
- else if(istype(A, /obj/item/weapon/flame))
- var/obj/item/weapon/flame/F = A
- return (F.lit)
- else if(istype(A, /obj/item/device/assembly/igniter))
- return 1
- return 0
+/obj/item/weapon/flame/is_hot()
+ return lit
///////////
//MATCHES//
@@ -237,7 +229,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/clothing/mask/smokable/attackby(obj/item/weapon/W as obj, mob/user as mob)
..()
- if(isflamesource(W))
+ if(W.is_hot())
var/text = matchmes
if(istype(W, /obj/item/weapon/flame/match))
text = matchmes
diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm
index 3cbc54e855..b22c4ca649 100644
--- a/code/game/objects/items/weapons/circuitboards/frame.dm
+++ b/code/game/objects/items/weapons/circuitboards/frame.dm
@@ -37,7 +37,7 @@
matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50)
/obj/item/weapon/circuitboard/request
- name = T_BOARD("reques console")
+ name = T_BOARD("request console")
build_path = /obj/machinery/requests_console
board_type = new /datum/frame/frame_types/supply_request_console
matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50)
@@ -230,4 +230,4 @@
/obj/item/weapon/stock_parts/motor = 2,
/obj/item/weapon/stock_parts/capacitor = 1,
/obj/item/weapon/stock_parts/spring = 1,
- /obj/item/stack/cable_coil = 5)
\ No newline at end of file
+ /obj/item/stack/cable_coil = 5)
diff --git a/code/game/objects/items/weapons/circuitboards/machinery/unary_atmos.dm b/code/game/objects/items/weapons/circuitboards/machinery/unary_atmos.dm
index 07e9ae53dd..72febf566a 100644
--- a/code/game/objects/items/weapons/circuitboards/machinery/unary_atmos.dm
+++ b/code/game/objects/items/weapons/circuitboards/machinery/unary_atmos.dm
@@ -7,10 +7,10 @@
/obj/item/weapon/circuitboard/unary_atmos/construct(var/obj/machinery/atmospherics/unary/U)
//TODO: Move this stuff into the relevant constructor when pipe/construction.dm is cleaned up.
- U.initialize()
+ U.atmos_init()
U.build_network()
if (U.node)
- U.node.initialize()
+ U.node.atmos_init()
U.node.build_network()
/obj/item/weapon/circuitboard/unary_atmos/heater
diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm
index d15b78963d..7124cb489f 100644
--- a/code/game/objects/items/weapons/clown_items.dm
+++ b/code/game/objects/items/weapons/clown_items.dm
@@ -33,23 +33,23 @@
//I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing.
//So this is a workaround. This also makes more sense from an IC standpoint. ~Carn
if(user.client && (target in user.client.screen))
- user << "You need to take that [target.name] off before cleaning it."
+ to_chat(user, "You need to take that [target.name] off before cleaning it.")
else if(istype(target,/obj/effect/decal/cleanable/blood))
- user << "You scrub \the [target.name] out."
+ to_chat(user, "You scrub \the [target.name] out.")
target.clean_blood()
return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals.
else if(istype(target,/obj/effect/decal/cleanable))
- user << "You scrub \the [target.name] out."
+ to_chat(user, "You scrub \the [target.name] out.")
qdel(target)
else if(istype(target,/turf))
- user << "You scrub \the [target.name] clean."
+ to_chat(user, "You scrub \the [target.name] clean.")
var/turf/T = target
T.clean(src, user)
else if(istype(target,/obj/structure/sink))
- user << "You wet \the [src] in the sink."
+ to_chat(user, "You wet \the [src] in the sink.")
wet()
else
- user << "You clean \the [target.name]."
+ to_chat(user, "You clean \the [target.name].")
target.clean_blood()
return
diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm
index 9d3a9cb0a4..3d142b223a 100644
--- a/code/game/objects/items/weapons/cosmetics.dm
+++ b/code/game/objects/items/weapons/cosmetics.dm
@@ -50,7 +50,7 @@
user.visible_message("[user] does their lips with \the [src].", \
"You take a moment to apply \the [src]. Perfect!")
H.lip_style = colour
- H.update_body()
+ H.update_icons_body()
else
user.visible_message("[user] begins to do [H]'s lips with \the [src].", \
"You begin to apply \the [src].")
@@ -58,7 +58,7 @@
user.visible_message("[user] does [H]'s lips with \the [src].", \
"You apply \the [src].")
H.lip_style = colour
- H.update_body()
+ H.update_icons_body()
else
user << "Where are the lips on that?"
diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm
index 940029e735..5588089cef 100644
--- a/code/game/objects/items/weapons/grenades/flashbang.dm
+++ b/code/game/objects/items/weapons/grenades/flashbang.dm
@@ -16,10 +16,11 @@
for(var/mob/living/carbon/M in hear(7, get_turf(src)))
bang(get_turf(src), M)
- for(var/obj/effect/blob/B in hear(8,get_turf(src))) //Blob damage here
+ for(var/obj/structure/blob/B in hear(8,get_turf(src))) //Blob damage here
var/damage = round(30/(get_dist(B,get_turf(src))+1))
- B.health -= damage
- B.update_icon()
+ if(B.overmind)
+ damage *= B.overmind.blob_type.burn_multiplier
+ B.adjust_integrity(-damage)
new/obj/effect/effect/sparks(src.loc)
new/obj/effect/effect/smoke/illumination(src.loc, 5, range=30, power=30, color="#FFFFFF")
@@ -89,7 +90,7 @@
else
if (M.ear_damage >= 5)
M << "Your ears start to ring!"
- M.update_icons()
+ M.update_icons() //Forces matrix transform to proc if they are now laying, I guess?
/obj/item/weapon/grenade/flashbang/Destroy()
walk(src, 0) // Because we might have called walk_away, we must stop the walk loop or BYOND keeps an internal reference to us forever.
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 0b4700ca6a..b1f623081d 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -17,6 +17,7 @@
var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes
var/cuff_sound = 'sound/weapons/handcuffs.ogg'
var/cuff_type = "handcuffs"
+ var/use_time = 30
sprite_sheets = list("Teshari" = 'icons/mob/species/seromi/handcuffs.dmi')
/obj/item/weapon/handcuffs/attack(var/mob/living/carbon/C, var/mob/living/user)
@@ -69,7 +70,7 @@
user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!")
- if(!do_after(user,30))
+ if(!do_after(user,use_time))
return 0
if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime
@@ -80,7 +81,7 @@
msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(H)]")
feedback_add_details("handcuffs","H")
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(H)
user.visible_message("\The [user] has put [cuff_type] on \the [H]!")
@@ -199,6 +200,44 @@ var/last_chew = 0
elastic = 0
cuff_sound = 'sound/weapons/handcuffs.ogg' //This shold work for now.
+/obj/item/weapon/handcuffs/legcuffs/bola
+ name = "bola"
+ desc = "Keeps prey in line."
+ elastic = 1
+ use_time = 0
+ breakouttime = 30
+ cuff_sound = 'sound/weapons/towelwipe.ogg' //Is there anything this sound can't do?
+
+/obj/item/weapon/handcuffs/legcuffs/bola/can_place(var/mob/target, var/mob/user)
+ if(user) //A ranged legcuff, until proper implementation as items it remains a projectile-only thing.
+ return 1
+
+/obj/item/weapon/handcuffs/legcuffs/bola/dropped()
+ visible_message("\The [src] falls apart!")
+ qdel(src)
+
+/obj/item/weapon/handcuffs/legcuffs/bola/place_legcuffs(var/mob/living/carbon/target, var/mob/user)
+ playsound(src.loc, cuff_sound, 30, 1, -2)
+
+ var/mob/living/carbon/human/H = target
+ if(!istype(H))
+ src.dropped()
+ return 0
+
+ if(!H.has_organ_for_slot(slot_legcuffed))
+ H.visible_message("\The [src] slams into [H], but slides off!")
+ src.dropped()
+ return 0
+
+ H.visible_message("\The [H] has been snared by \the [src]!")
+
+ // Apply cuffs.
+ var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src
+ lcuffs.loc = target
+ target.legcuffed = lcuffs
+ target.update_inv_legcuffed()
+ return 1
+
/obj/item/weapon/handcuffs/legcuffs/attack(var/mob/living/carbon/C, var/mob/living/user)
if(!user.IsAdvancedToolUser())
return
@@ -236,7 +275,7 @@ var/last_chew = 0
user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!")
- if(!do_after(user,30))
+ if(!do_after(user,use_time))
return 0
if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime
@@ -247,7 +286,7 @@ var/last_chew = 0
msg_admin_attack("[key_name(user)] attempted to legcuff [key_name(H)]")
feedback_add_details("legcuffs","H")
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(H)
user.visible_message("\The [user] has put [cuff_type] on \the [H]!")
diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm
index ac433353e0..1d515e863b 100644
--- a/code/game/objects/items/weapons/implants/implant.dm
+++ b/code/game/objects/items/weapons/implants/implant.dm
@@ -52,6 +52,7 @@
/obj/item/weapon/implant/Destroy()
if(part)
part.implants.Remove(src)
+ part = null
return ..()
/obj/item/weapon/implant/attackby(obj/item/I, mob/user)
@@ -351,6 +352,7 @@ the implant may become unstable and either pre-maturely inject the subject or si
R << "You hear a faint *beep*."
if(!src.reagents.total_volume)
R << "You hear a faint click from your chest."
+ playsound(R, 'sound/weapons/empty.ogg', 10, 1)
spawn(0)
qdel(src)
return
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index 533f5b30aa..0db669545c 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -147,6 +147,76 @@