From 4498442e0d8e98aeab3cb4e9bc908fd93f02112e Mon Sep 17 00:00:00 2001 From: Katherine Reeve Date: Wed, 12 May 2021 21:49:39 +0100 Subject: [PATCH 001/246] Nacelle safety and efficiency updates --- maps/torch/torch3_deck3.dmm | 178 ++++++++++++++++------------ maps/torch/torch5_deck1.dmm | 230 ++++++++++++++++++++---------------- 2 files changed, 227 insertions(+), 181 deletions(-) diff --git a/maps/torch/torch3_deck3.dmm b/maps/torch/torch3_deck3.dmm index 7dbb45c20a8..a25274b0ef9 100644 --- a/maps/torch/torch3_deck3.dmm +++ b/maps/torch/torch3_deck3.dmm @@ -1938,15 +1938,15 @@ /turf/simulated/floor/plating, /area/maintenance/substation/thirddeck) "ee" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 6 - }, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, +/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ + dir = 8 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3starboard) "ef" = ( @@ -2714,6 +2714,11 @@ }, /turf/simulated/floor/tiled/techfloor, /area/maintenance/thirddeck/starboard) +"fI" = ( +/obj/structure/grille, +/obj/structure/lattice, +/turf/space, +/area/space) "fJ" = ( /turf/simulated/floor/wood/walnut, /area/crew_quarters/observation) @@ -8991,7 +8996,7 @@ /area/maintenance/thirddeck/starboard) "td" = ( /obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 2 + target_pressure = 3000 }, /turf/simulated/wall/ocp_wall, /area/thruster/d3starboard) @@ -10218,6 +10223,12 @@ }, /turf/simulated/floor/tiled/steel_ridged, /area/crew_quarters/observation) +"wU" = ( +/obj/structure/sign/warning/vent_port{ + dir = 1 + }, +/turf/simulated/wall/r_wall/hull, +/area/thruster/d3port) "wV" = ( /obj/structure/sign/deck/third{ dir = 1; @@ -11026,12 +11037,6 @@ }, /turf/simulated/open, /area/maintenance/thirddeck/starboard) -"zf" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 6 - }, -/turf/simulated/floor/tiled/techfloor/grid, -/area/thruster/d3starboard) "zg" = ( /obj/random_multi/single_item/runtime, /obj/structure/cable/green{ @@ -11252,6 +11257,10 @@ /obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/crew_quarters/sleep/cryo) +"zW" = ( +/obj/structure/sign/warning/hot_exhaust, +/turf/simulated/wall/ocp_wall, +/area/thruster/d3port) "zY" = ( /obj/machinery/door/firedoor, /obj/structure/cable/green{ @@ -11272,6 +11281,10 @@ /obj/machinery/door/firedoor, /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/thirddeck/port) +"Ac" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/turf/simulated/wall/ocp_wall, +/area/thruster/d3starboard) "Ae" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -11290,11 +11303,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d3starboard) "Al" = ( -/obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 4; - target_pressure = 15000 - }, -/obj/effect/floor_decal/industrial/outline/orange, /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d3starboard) "Ap" = ( @@ -11847,10 +11855,10 @@ pixel_x = 0; pixel_y = -24 }, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 2 - }, /obj/effect/floor_decal/industrial/warning, +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3starboard) "BZ" = ( @@ -13252,6 +13260,18 @@ }, /turf/simulated/floor/tiled/dark, /area/security/habcheck) +"FA" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume{ + controlled = 0; + dir = 1; + internal_pressure_bound = 35000; + internal_pressure_bound_default = 35000; + pressure_checks = 2; + pressure_checks_default = 2; + use_power = 1 + }, +/turf/simulated/floor/airless, +/area/thruster/d3port) "FC" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/civilian{ @@ -14650,12 +14670,6 @@ }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3starboard) -"Jj" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 5 - }, -/turf/simulated/floor/tiled/techfloor/grid, -/area/thruster/d3port) "Jk" = ( /obj/effect/floor_decal/industrial/warning, /obj/machinery/atmospherics/pipe/simple/visible/fuel{ @@ -14675,17 +14689,12 @@ /turf/simulated/floor/tiled/techfloor, /area/thruster/d3starboard) "Jo" = ( -/obj/machinery/atmospherics/binary/passive_gate/on{ - dir = 4; - max_pressure_setting = 35000; - regulate_mode = 2; - target_pressure = 35000; - use_power = 1 - }, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/effect/floor_decal/industrial/warning/corner{ dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3starboard) "Jp" = ( @@ -15281,11 +15290,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d3port) "Kr" = ( -/obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 4; - target_pressure = 15000 - }, -/obj/effect/floor_decal/industrial/outline/orange, /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d3port) "Ks" = ( @@ -15398,15 +15402,15 @@ /turf/simulated/open, /area/maintenance/thirddeck/starboard) "KE" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 5 - }, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ + dir = 8 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3port) "KF" = ( @@ -15653,7 +15657,6 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/machinery/meter, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3port) "Lp" = ( @@ -15661,14 +15664,9 @@ dir = 1; icon_state = "warningcorner" }, -/obj/machinery/atmospherics/binary/passive_gate/on{ - dir = 4; - max_pressure_setting = 35000; - regulate_mode = 2; - target_pressure = 35000; - use_power = 1 +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 }, -/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3port) "Lq" = ( @@ -16551,6 +16549,10 @@ }, /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/thirddeck/foreport) +"NK" = ( +/obj/structure/sign/warning/vent_port, +/turf/simulated/wall/r_wall/hull, +/area/thruster/d3starboard) "NL" = ( /obj/random/obstruction, /turf/simulated/floor/tiled/techfloor/grid, @@ -16573,6 +16575,24 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/vacant/mess) +"NQ" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume{ + controlled = 0; + internal_pressure_bound = 35000; + internal_pressure_bound_default = 35000; + pressure_checks = 2; + pressure_checks_default = 2; + use_power = 1 + }, +/turf/simulated/floor/airless, +/area/thruster/d3starboard) +"NS" = ( +/obj/structure/sign/warning/hot_exhaust{ + dir = 1; + icon_state = "fire" + }, +/turf/simulated/wall/ocp_wall, +/area/thruster/d3starboard) "NV" = ( /obj/structure/cable/green{ d1 = 1; @@ -16712,7 +16732,11 @@ /area/hallway/primary/thirddeck/aft) "Or" = ( /obj/machinery/atmospherics/pipe/simple/visible/fuel, -/turf/simulated/wall/r_wall/hull, +/obj/structure/sign/warning/hot_exhaust{ + dir = 1; + icon_state = "fire" + }, +/turf/simulated/wall/ocp_wall, /area/thruster/d3port) "Ot" = ( /obj/structure/railing/mapped{ @@ -17287,7 +17311,8 @@ /area/crew_quarters/observation) "Qi" = ( /obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 1 + dir = 1; + target_pressure = 3000 }, /turf/simulated/wall/ocp_wall, /area/thruster/d3port) @@ -17307,8 +17332,8 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3port) @@ -17774,7 +17799,8 @@ /area/thruster/d3port) "RI" = ( /obj/machinery/atmospherics/pipe/simple/visible/fuel, -/turf/simulated/wall/r_wall/hull, +/obj/structure/sign/warning/hot_exhaust, +/turf/simulated/wall/ocp_wall, /area/thruster/d3starboard) "RM" = ( /obj/machinery/atmospherics/valve/digital{ @@ -19453,12 +19479,10 @@ /area/tcommsat/chamber) "Wo" = ( /obj/machinery/meter, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 4 - }, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/machinery/atmospherics/pipe/simple/visible/fuel, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3starboard) "Wp" = ( @@ -19876,9 +19900,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 4 - }, +/obj/machinery/atmospherics/pipe/simple/visible/fuel, /turf/simulated/floor/tiled/techfloor, /area/thruster/d3port) "Xu" = ( @@ -19923,6 +19945,10 @@ /obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/dark/monotile, /area/engineering/hardstorage) +"XA" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/turf/simulated/wall/ocp_wall, +/area/thruster/d3port) "XB" = ( /obj/structure/table/woodentable/mahogany, /obj/machinery/alarm{ @@ -46925,7 +46951,7 @@ aa aa aa aV -kp +bd fY bd aT @@ -46967,7 +46993,7 @@ jZ EH pI Ku -tK +pI aV aa aa @@ -47127,7 +47153,7 @@ aa aa aa aV -kp +bd fY bd cv @@ -47169,7 +47195,7 @@ jZ jZ pI Ku -tK +pI aV aa aa @@ -48751,7 +48777,7 @@ td ZT KP bd -aa +fI aa aa aa @@ -48953,7 +48979,7 @@ te KV UR KP -bd +NK aa aa aa @@ -48979,7 +49005,7 @@ aa aa aa aa -pI +wU La LO Le @@ -49351,7 +49377,7 @@ bd SJ WF Jh -zf +Al BY KP KP @@ -49389,7 +49415,7 @@ La La La Qk -Jj +Kr Jk Ws MR @@ -49549,7 +49575,7 @@ aa aa aa aa -bd +NS bd bd KG @@ -49595,7 +49621,7 @@ Kr Jx pI pI -pI +zW aa aa aa @@ -49751,8 +49777,8 @@ aa aa aa aa -bd -bd +NQ +Ac ee lk Wo @@ -49796,8 +49822,8 @@ Pq Xt Kz KE -pI -pI +XA +FA aa aa aa @@ -49953,7 +49979,7 @@ aa aa aa aa -bd +NS bd cu cu @@ -49999,7 +50025,7 @@ pI Kf Kf pI -pI +zW aa aa aa diff --git a/maps/torch/torch5_deck1.dmm b/maps/torch/torch5_deck1.dmm index c88ac4ae5c9..01dcbfae6c2 100644 --- a/maps/torch/torch5_deck1.dmm +++ b/maps/torch/torch5_deck1.dmm @@ -993,15 +993,15 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/crew_quarters/safe_room/firstdeck) "abZ" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 6 - }, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, +/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ + dir = 8 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) "aca" = ( @@ -1078,18 +1078,6 @@ }, /turf/simulated/floor/plating, /area/thruster/d1starboard) -"aci" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 4 - }, -/obj/structure/cable/green{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/structure/catwalk, -/turf/simulated/floor/plating, -/area/thruster/d1starboard) "acj" = ( /obj/machinery/atmospherics/pipe/simple/visible/fuel{ dir = 4 @@ -1219,10 +1207,10 @@ pixel_x = 0; pixel_y = -24 }, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 2 - }, /obj/effect/floor_decal/industrial/warning, +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) "acv" = ( @@ -1557,22 +1545,12 @@ /obj/machinery/meter, /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d1starboard) -"add" = ( -/obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 4; - target_pressure = 15000 - }, -/obj/effect/floor_decal/industrial/outline/orange, -/turf/simulated/floor/tiled/techfloor/grid, -/area/thruster/d1starboard) "ade" = ( /obj/machinery/meter, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 4 - }, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/machinery/atmospherics/pipe/simple/visible/fuel, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) "adf" = ( @@ -1918,7 +1896,6 @@ dir = 6 }, /obj/effect/floor_decal/industrial/warning, -/obj/machinery/meter, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) "adH" = ( @@ -1944,17 +1921,12 @@ /turf/simulated/floor/tiled/white, /area/medical/sleeper) "adI" = ( -/obj/machinery/atmospherics/binary/passive_gate/on{ - dir = 4; - max_pressure_setting = 35000; - regulate_mode = 2; - target_pressure = 35000; - use_power = 1 - }, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/effect/floor_decal/industrial/warning/corner{ dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) "adJ" = ( @@ -4878,7 +4850,8 @@ /area/maintenance/firstdeck/aftstarboard) "aiI" = ( /obj/machinery/atmospherics/pipe/simple/visible/fuel, -/turf/simulated/wall/r_wall/hull, +/obj/structure/sign/warning/hot_exhaust, +/turf/simulated/wall/ocp_wall, /area/thruster/d1starboard) "aiK" = ( /obj/effect/floor_decal/techfloor{ @@ -11863,7 +11836,11 @@ /area/thruster/d1port) "aPs" = ( /obj/machinery/atmospherics/pipe/simple/visible/fuel, -/turf/simulated/wall/r_wall/hull, +/obj/structure/sign/warning/hot_exhaust{ + dir = 1; + icon_state = "fire" + }, +/turf/simulated/wall/ocp_wall, /area/thruster/d1port) "aPu" = ( /turf/simulated/floor/tiled/dark/monotile, @@ -13379,7 +13356,6 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/machinery/meter, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1port) "aTn" = ( @@ -13392,8 +13368,8 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1port) @@ -13402,14 +13378,9 @@ dir = 1; icon_state = "warningcorner" }, -/obj/machinery/atmospherics/binary/passive_gate/on{ - dir = 4; - max_pressure_setting = 35000; - regulate_mode = 2; - target_pressure = 35000; - use_power = 1 +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 }, -/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1port) "aTp" = ( @@ -13637,22 +13608,12 @@ /obj/machinery/meter, /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d1port) -"aTX" = ( -/obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 4; - target_pressure = 15000 - }, -/obj/effect/floor_decal/industrial/outline/orange, -/turf/simulated/floor/tiled/techfloor/grid, -/area/thruster/d1port) "aTY" = ( /obj/machinery/meter, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, -/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ - dir = 4 - }, +/obj/machinery/atmospherics/pipe/simple/visible/fuel, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1port) "aTZ" = ( @@ -13927,15 +13888,15 @@ /turf/simulated/floor/tiled/techfloor, /area/thruster/d1port) "aUy" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 5 - }, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/machinery/atmospherics/pipe/manifold/visible/fuel{ + dir = 8 + }, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1port) "aUz" = ( @@ -14626,7 +14587,7 @@ /area/hallway/primary/firstdeck/fore) "bbb" = ( /obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 2 + target_pressure = 3000 }, /turf/simulated/wall/ocp_wall, /area/thruster/d1starboard) @@ -14938,18 +14899,6 @@ "btE" = ( /turf/simulated/floor/tiled/dark, /area/security/questioning) -"bvI" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 4 - }, -/obj/structure/cable/green{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/structure/catwalk, -/turf/simulated/floor/plating, -/area/thruster/d1port) "bvY" = ( /obj/effect/floor_decal/industrial/warning{ dir = 4; @@ -14978,7 +14927,8 @@ /area/assembly/chargebay) "byb" = ( /obj/machinery/atmospherics/binary/pump/high_power/on{ - dir = 1 + dir = 1; + target_pressure = 3000 }, /turf/simulated/wall/ocp_wall, /area/thruster/d1port) @@ -17781,6 +17731,10 @@ /obj/item/device/integrated_circuit_printer, /turf/simulated/floor/tiled/dark, /area/rnd/misc_lab) +"fBP" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/turf/simulated/wall/ocp_wall, +/area/thruster/d1port) "fCb" = ( /obj/structure/table/standard, /obj/item/storage/firstaid/surgery, @@ -18786,6 +18740,10 @@ }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) +"hkQ" = ( +/obj/structure/sign/warning/vent_port, +/turf/simulated/wall/r_wall/hull, +/area/thruster/d1starboard) "hlb" = ( /obj/effect/floor_decal/corner/paleblue{ dir = 5 @@ -18867,6 +18825,18 @@ }, /turf/simulated/floor/plating, /area/security/opscheck) +"hqs" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 + }, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/structure/catwalk, +/turf/simulated/floor/plating, +/area/thruster/d1port) "hrb" = ( /obj/effect/wallframe_spawn/reinforced, /obj/machinery/door/blast/shutters{ @@ -19344,6 +19314,12 @@ /obj/random_multi/single_item/runtime, /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralport) +"hPp" = ( +/obj/structure/sign/warning/vent_port{ + dir = 1 + }, +/turf/simulated/wall/r_wall/hull, +/area/thruster/d1port) "hPt" = ( /obj/machinery/light, /obj/machinery/atmospherics/unary/vent_scrubber/on{ @@ -19361,6 +19337,13 @@ /obj/machinery/portable_atmospherics/powered/scrubber, /turf/simulated/floor/tiled/techfloor, /area/engineering/hardstorage/aux) +"hQd" = ( +/obj/structure/sign/warning/hot_exhaust{ + dir = 1; + icon_state = "fire" + }, +/turf/simulated/wall/ocp_wall, +/area/thruster/d1starboard) "hUb" = ( /obj/machinery/door/blast/shutters{ density = 0; @@ -21198,9 +21181,6 @@ /turf/simulated/floor/tiled, /area/assembly/robotics/office) "kfG" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 6 - }, /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d1starboard) "kgi" = ( @@ -26025,6 +26005,10 @@ /obj/effect/floor_decal/industrial/warning/corner, /turf/simulated/floor/tiled/white/monotile, /area/rnd/xenobiology/xenoflora) +"qhd" = ( +/obj/structure/sign/warning/hot_exhaust, +/turf/simulated/wall/ocp_wall, +/area/thruster/d1port) "qib" = ( /obj/effect/floor_decal/industrial/warning, /obj/structure/hygiene/sink{ @@ -26386,6 +26370,18 @@ }, /turf/simulated/floor/tiled/white, /area/rnd/research) +"qFY" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel{ + dir = 4 + }, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/structure/catwalk, +/turf/simulated/floor/plating, +/area/thruster/d1starboard) "qGb" = ( /obj/machinery/atmospherics/pipe/simple/visible/red, /obj/machinery/reagentgrinder, @@ -27681,9 +27677,6 @@ /turf/simulated/floor/tiled/freezer, /area/rnd/xenobiology/xenoflora) "rYd" = ( -/obj/machinery/atmospherics/pipe/simple/visible/fuel{ - dir = 5 - }, /turf/simulated/floor/tiled/techfloor/grid, /area/thruster/d1port) "rYN" = ( @@ -29102,6 +29095,17 @@ /obj/machinery/atmospherics/pipe/simple/hidden/universal, /turf/simulated/floor/reinforced, /area/rnd/xenobiology) +"tpJ" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume{ + controlled = 0; + internal_pressure_bound = 35000; + internal_pressure_bound_default = 35000; + pressure_checks = 2; + pressure_checks_default = 2; + use_power = 1 + }, +/turf/simulated/floor/airless, +/area/thruster/d1starboard) "tqb" = ( /obj/effect/floor_decal/industrial/hatch/yellow, /obj/machinery/door/window/southright{ @@ -30615,6 +30619,10 @@ /obj/item/storage/box/lights/mixed, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/centralport) +"wLC" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/turf/simulated/wall/ocp_wall, +/area/thruster/d1starboard) "wMh" = ( /obj/structure/bed/padded, /obj/item/bedsheet/orange, @@ -30963,6 +30971,18 @@ /obj/structure/disposalpipe/segment, /turf/simulated/floor/tiled, /area/assembly/robotics/office) +"xLj" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume{ + controlled = 0; + dir = 1; + internal_pressure_bound = 35000; + internal_pressure_bound_default = 35000; + pressure_checks = 2; + pressure_checks_default = 2; + use_power = 1 + }, +/turf/simulated/floor/airless, +/area/thruster/d1port) "xMx" = ( /obj/structure/disposalpipe/segment, /obj/structure/cable/green{ @@ -56483,7 +56503,7 @@ aaa aaa abM abN -aci +qFY mEV abL abL @@ -56527,7 +56547,7 @@ aSf dIA bUZ wEw -bvI +hqs xPU abM aaa @@ -56685,7 +56705,7 @@ aaa aaa abM abN -aci +qFY mEV abL abL @@ -56729,8 +56749,8 @@ tCu nPT xaC wEw -bvI -jbk +hqs +xPU abM aaa aaa @@ -56887,7 +56907,7 @@ aaa aaa abM abN -aci +qFY mEV abL abL @@ -56931,8 +56951,8 @@ aDB aDB aDB wEw -bvI -jbk +hqs +xPU abM aaa aaa @@ -57089,7 +57109,7 @@ aaa aaa abN abN -aci +qFY abN mpy mpy @@ -58712,7 +58732,7 @@ aet afw agw aet -abN +hkQ aaa acd acd @@ -58742,7 +58762,7 @@ acd acd acd aaa -xPU +hPp aPq aRg aSh @@ -59308,11 +59328,11 @@ aaa aaa aaa aaa -abN +hQd abN abN acp -add +kfG adI aey afz @@ -59354,11 +59374,11 @@ aRj aSk aSS aTo -aTX +rYd aUm xPU xPU -xPU +qhd aaa aaa aaa @@ -59510,8 +59530,8 @@ aaa aaa aaa aaa -abN -abN +tpJ +wLC abZ acq ade @@ -59559,8 +59579,8 @@ qDM aTY aUn aUy -xPU -xPU +fBP +xLj aaa aaa aaa @@ -59712,7 +59732,7 @@ aaa aaa aaa aaa -abN +hQd abN aca aca @@ -59762,7 +59782,7 @@ xPU aQo aQo xPU -xPU +qhd aaa aaa aaa From ba34c0f91b2ce9d5223bf2e0768907bcc95f9d40 Mon Sep 17 00:00:00 2001 From: Tennessee116 Date: Tue, 18 May 2021 18:05:57 -0500 Subject: [PATCH 002/246] Display disposal pipes above tiles in map editors --- code/modules/recycling/disposalpipe.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/modules/recycling/disposalpipe.dm b/code/modules/recycling/disposalpipe.dm index 8205a8b071d..32d1485bf0d 100644 --- a/code/modules/recycling/disposalpipe.dm +++ b/code/modules/recycling/disposalpipe.dm @@ -12,7 +12,7 @@ dir = 0 // dir will contain dominant direction for junction pipes var/health = 10 // health points 0-10 alpha = 192 // Plane and alpha modified for mapping, reset to normal on spawn. - layer = DISPOSALS_PIPE_LAYER + layer = ABOVE_TILE_LAYER var/base_icon_state // initial icon state on map var/sort_type = "" var/turn = DISPOSAL_FLIP_NONE @@ -22,7 +22,9 @@ /obj/structure/disposalpipe/Initialize() . = ..() alpha = 255 + layer = DISPOSALS_PIPE_LAYER base_icon_state = icon_state + update_icon() // pipe is deleted // ensure if holder is present, it is expelled From 3f874322f137e55cc8bef12ffe87cdae8423052e Mon Sep 17 00:00:00 2001 From: Hunttark Date: Wed, 19 May 2021 10:39:01 -0700 Subject: [PATCH 003/246] Aesthetic changes to the Infirmary/Resolutions to map issues/Hoping to not delete the deck1 of the torch in a PR or destroy my repo branch again --- maps/torch/torch5_deck1.dmm | 2948 +++++++++++++++++++---------------- 1 file changed, 1616 insertions(+), 1332 deletions(-) diff --git a/maps/torch/torch5_deck1.dmm b/maps/torch/torch5_deck1.dmm index c88ac4ae5c9..01a46394d10 100644 --- a/maps/torch/torch5_deck1.dmm +++ b/maps/torch/torch5_deck1.dmm @@ -515,12 +515,12 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "abf" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/pager/robotics{ pixel_y = 30 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/center) "abi" = ( @@ -553,7 +553,6 @@ /area/medical/counselor) "abl" = ( /obj/machinery/chemical_dispenser/full, -/obj/effect/floor_decal/corner/beige/mono, /obj/machinery/firealarm{ dir = 8; pixel_x = -24 @@ -561,6 +560,7 @@ /obj/structure/sign/warning/nosmoking_1{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abm" = ( @@ -571,14 +571,13 @@ /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/glass/beaker/insulated/large, /obj/item/reagent_containers/glass/beaker/large, -/obj/effect/floor_decal/corner/beige/mono, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abn" = ( -/obj/effect/floor_decal/corner/beige/mono, /obj/structure/closet/secure_closet/medical_wall{ name = "Pill Cabinet"; pixel_x = 0; @@ -586,6 +585,7 @@ }, /obj/machinery/reagentgrinder, /obj/item/reagent_containers/glass/beaker/large, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abo" = ( @@ -607,31 +607,28 @@ /area/medical/morgue/autopsy) "abr" = ( /obj/machinery/chem_master, -/obj/effect/floor_decal/corner/beige/mono, +/obj/item/device/radio/intercom/department/medbay{ + dir = 4; + pixel_x = -24 + }, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abs" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, /obj/structure/bed/chair/comfy/blue{ dir = 8 }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abt" = ( -/obj/effect/floor_decal/corner/beige/mono, /obj/structure/table/standard, /obj/machinery/reagent_temperature/cooler, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abu" = ( /obj/structure/table/standard, -/obj/effect/floor_decal/corner/beige/mono, /obj/machinery/power/apc{ dir = 1; name = "north bump"; @@ -643,19 +640,14 @@ icon_state = "0-2" }, /obj/machinery/reagent_temperature, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abv" = ( /obj/machinery/light{ dir = 1 }, -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abw" = ( @@ -703,6 +695,7 @@ /obj/machinery/door/airlock/glass/medical{ name = "Chemistry" }, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abz" = ( @@ -718,10 +711,6 @@ /turf/simulated/floor/tiled/freezer, /area/crew_quarters/head/aux) "abA" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -733,13 +722,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abB" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -751,13 +737,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abC" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -769,16 +752,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abD" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -787,6 +764,7 @@ d2 = 8; icon_state = "1-8" }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abE" = ( @@ -797,16 +775,10 @@ /turf/simulated/floor/reinforced/airless, /area/maintenance/firstdeck/forestarboard) "abG" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abH" = ( @@ -848,9 +820,6 @@ /turf/simulated/floor/tiled/dark, /area/rnd/development) "abK" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, /obj/structure/table/standard, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 @@ -858,7 +827,12 @@ /obj/machinery/light_switch{ pixel_y = -22 }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/yellow/mono, +/obj/structure/disposalpipe/segment{ + dir = 4; + icon_state = "pipe-c" + }, +/turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abL" = ( /turf/simulated/wall/r_wall/prepainted, @@ -889,9 +863,11 @@ /area/maintenance/firstdeck/centralstarboard) "abQ" = ( /obj/machinery/disposal, -/obj/effect/floor_decal/corner/beige/mono, +/obj/effect/floor_decal/corner/yellow/mono, /obj/effect/floor_decal/industrial/hatch/yellow, -/obj/structure/disposalpipe/trunk, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abR" = ( @@ -920,24 +896,16 @@ /turf/simulated/floor/tiled/freezer, /area/crew_quarters/head/aux) "abT" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, /obj/structure/table/standard, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/yellow/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "abU" = ( -/obj/effect/floor_decal/corner/beige{ - dir = 10 - }, -/obj/effect/floor_decal/corner/beige{ - dir = 5; - icon_state = "corner_white" - }, /obj/structure/bed/chair/padded, +/obj/effect/floor_decal/corner/yellow/diagonal, /turf/simulated/floor/tiled/white, /area/medical/chemistry) "abV" = ( @@ -1029,25 +997,21 @@ /turf/simulated/floor/reinforced/airless, /area/space) "ace" = ( -/obj/structure/closet/secure_closet/chemical, -/obj/item/storage/box/pillbottles, -/obj/item/device/radio/headset/headset_med, -/obj/item/book/manual/chemistry_recipes, -/obj/item/clothing/glasses/science, -/obj/effect/floor_decal/corner/beige/mono, -/obj/machinery/button/blast_door{ - id_tag = "chemcounter"; - name = "Chemistry Counter Lockdown Control"; - pixel_x = -6; - pixel_y = -24 - }, -/obj/machinery/camera/network/medbay{ - c_tag = "Infirmary - Chemistry"; +/obj/structure/table/rack{ dir = 8 }, -/obj/structure/extinguisher_cabinet{ - pixel_x = 32 +/obj/item/device/scanner/spectrometer, +/obj/item/storage/box/freezer, +/obj/item/storage/box/beakers/insulated, +/obj/item/reagent_containers/dropper, +/obj/item/storage/box/beakers, +/obj/item/hand_labeler, +/obj/item/stack/package_wrap/twenty_five, +/obj/item/reagent_containers/spray/cleaner{ + desc = "Someone has crossed out the 'Space' from Space Cleaner and written in Chemistry. Scrawled on the back is, 'Okay, whoever filled this with polytrinic acid, it was only funny the first time. It was hard enough replacing the CMO's first cat!'"; + name = "Chemistry Cleaner" }, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "acf" = ( @@ -1252,9 +1216,6 @@ /turf/simulated/floor/reinforced, /area/thruster/d1port) "acy" = ( -/obj/effect/floor_decal/corner/pink{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, @@ -1266,11 +1227,12 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/lime{ + icon_state = "corner_white"; + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) -"acz" = ( -/turf/simulated/wall/prepainted, -/area/medical/medicalhallway) "acB" = ( /obj/machinery/door/firedoor, /obj/structure/table/steel_reinforced, @@ -1387,7 +1349,7 @@ icon_state = "1-2" }, /turf/simulated/floor/tiled/freezer, -/area/medical/medicalhallway) +/area/medical/washroom) "acJ" = ( /obj/structure/closet/crate/freezer, /obj/random/firstaid, @@ -1422,7 +1384,6 @@ /area/maintenance/firstdeck/aftstarboard) "acO" = ( /obj/effect/wallframe_spawn/reinforced/no_grille, -/obj/structure/disposalpipe/segment, /obj/machinery/door/blast/shutters{ density = 0; dir = 2; @@ -1431,6 +1392,10 @@ name = "Chemistry Counter Shutters"; opacity = 0 }, +/obj/structure/disposalpipe/segment{ + dir = 4; + icon_state = "pipe-c" + }, /turf/simulated/floor/plating, /area/medical/chemistry) "acP" = ( @@ -1461,10 +1426,6 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) "acU" = ( -/obj/structure/disposalpipe/segment{ - dir = 4; - icon_state = "pipe-c" - }, /obj/effect/wallframe_spawn/reinforced/no_grille, /obj/machinery/door/blast/shutters{ density = 0; @@ -1474,6 +1435,10 @@ name = "Chemistry Counter Shutters"; opacity = 0 }, +/obj/structure/disposalpipe/segment{ + dir = 2; + icon_state = "pipe-c" + }, /turf/simulated/floor/plating, /area/medical/chemistry) "acV" = ( @@ -1634,21 +1599,18 @@ opacity = 0 }, /obj/item/material/bell, -/obj/structure/disposalpipe/segment{ - dir = 2; - icon_state = "pipe-c" - }, +/obj/effect/floor_decal/corner/yellow/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "adj" = ( /obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/disposal, -/obj/effect/floor_decal/industrial/hatch/yellow, /obj/structure/disposalpipe/trunk, /obj/machinery/firealarm{ dir = 2; pixel_y = 24 }, +/obj/effect/floor_decal/industrial/hatch/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/medicalhallway) "adk" = ( @@ -1671,10 +1633,6 @@ /turf/simulated/floor/tiled/freezer, /area/crew_quarters/head/aux) "adl" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/structure/bed/chair/padded/blue, /obj/machinery/power/apc{ dir = 1; name = "north bump"; @@ -1685,12 +1643,10 @@ d2 = 2; icon_state = "0-2" }, +/obj/structure/reagent_dispensers/water_cooler, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "adm" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 1 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -1701,12 +1657,17 @@ /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "adn" = ( -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/structure/reagent_dispensers/water_cooler, /obj/machinery/computer/guestpass{ pixel_y = 32 }, -/turf/simulated/floor/tiled/white/monotile, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "ado" = ( /obj/machinery/light/small{ @@ -1734,7 +1695,11 @@ /turf/simulated/floor/tiled/freezer, /area/crew_quarters/head/aux) "ads" = ( -/obj/effect/floor_decal/corner/paleblue, +/obj/effect/floor_decal/corner/blue, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 9 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "adt" = ( @@ -1757,9 +1722,6 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/aftstarboard) "adu" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 }, @@ -1771,6 +1733,14 @@ d2 = 4; icon_state = "2-4" }, +/obj/effect/floor_decal/corner/blue{ + dir = 10; + icon_state = "corner_white" + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "adv" = ( @@ -1866,9 +1836,6 @@ /turf/space, /area/space) "adB" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -1880,6 +1847,13 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 4 + }, +/obj/effect/floor_decal/borderfloorwhite/corner{ + icon_state = "borderfloorcorner_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "adC" = ( @@ -1922,7 +1896,6 @@ /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) "adH" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 2 }, @@ -1941,6 +1914,11 @@ dir = 5; icon_state = "intact" }, +/obj/effect/floor_decal/corner/blue/three_quarters, +/obj/effect/floor_decal/borderfloorwhite/corner{ + icon_state = "borderfloorcorner_white"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "adI" = ( @@ -1968,9 +1946,6 @@ /turf/simulated/wall/r_wall/prepainted, /area/crew_quarters/safe_room/firstdeck) "adL" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -1985,6 +1960,14 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "adM" = ( @@ -1999,14 +1982,14 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/forestarboard) "adO" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/machinery/button/alternate/door{ - id_tag = "ETC_hall"; - name = "Treatment Center Exit"; - pixel_x = 7; - pixel_y = 24 +/obj/machinery/door/firedoor, +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 4; + icon_state = "shutter0"; + id_tag = "infirm_pub"; + name = "Infirmary Public Hallway Lockdown"; + opacity = 0 }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -2022,7 +2005,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, +/turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "adP" = ( /obj/machinery/atmospherics/pipe/simple/hidden/universal{ @@ -2036,19 +2021,6 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/forestarboard) "adQ" = ( -/obj/effect/floor_decal/industrial/hatch/blue, -/obj/machinery/door/firedoor, -/obj/structure/sign/directions/infm{ - pixel_y = 32 - }, -/obj/machinery/door/blast/shutters{ - density = 0; - dir = 4; - icon_state = "shutter0"; - id_tag = "infirm_pub"; - name = "Infirmary Public Hallway Lockdown"; - opacity = 0 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -2063,8 +2035,23 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/sleeper) +/obj/structure/sign/directions/infm{ + pixel_y = 32 + }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "adR" = ( /obj/machinery/atmospherics/pipe/simple/visible{ dir = 10; @@ -2155,12 +2142,10 @@ /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/center) "aed" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/disposalpipe/segment{ +/obj/structure/disposalpipe/sortjunction/flipped{ dir = 1; - icon_state = "pipe-c" + name = "Chemistry"; + sort_type = "Chemistry" }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -2173,9 +2158,27 @@ d2 = 8; icon_state = "4-8" }, +/obj/structure/cable/green{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, +/obj/structure/cable/green{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aee" = ( @@ -2203,23 +2206,6 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/centralstarboard) "aei" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/item/reagent_containers/ivbag, -/obj/item/reagent_containers/ivbag, -/obj/item/reagent_containers/ivbag/nanoblood, -/obj/item/reagent_containers/ivbag/nanoblood, -/obj/item/reagent_containers/ivbag/nanoblood, -/obj/item/reagent_containers/ivbag/nanoblood, -/obj/structure/closet/secure_closet/medical_wall{ - name = "IV Equipment Cabinet"; - pixel_x = 0; - pixel_y = 32 - }, /obj/structure/cable/green{ d1 = 4; d2 = 8; @@ -2228,38 +2214,32 @@ /turf/simulated/floor/tiled/white, /area/medical/sleeper) "aej" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/disposalpipe/sortjunction/flipped{ - dir = 1; - name = "Chemistry"; - sort_type = "Chemistry" +/obj/structure/disposalpipe/junction{ + dir = 2; + icon_state = "pipe-j2" }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 1 }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 1 }, /obj/structure/cable/green{ d1 = 4; d2 = 8; icon_state = "4-8" }, -/obj/structure/cable/green{ - d1 = 2; - d2 = 4; - icon_state = "2-4" - }, -/obj/structure/cable/green{ - d1 = 2; - d2 = 8; - icon_state = "2-8" - }, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aek" = ( @@ -2268,9 +2248,6 @@ dir = 1; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/oxygen_pump{ pixel_x = -32; pixel_y = 32 @@ -2278,30 +2255,34 @@ /obj/machinery/body_scan_display{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "ael" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/item/modular_computer/telescreen/preset/medical{ + pixel_x = 32 + }, +/obj/structure/bed/chair/padded/blue{ + dir = 8; + icon_state = "chair_preview" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 }, -/obj/structure/disposalpipe/segment, -/obj/item/device/radio/beacon, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aem" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/disposalpipe/junction{ - dir = 2; - icon_state = "pipe-j2" +/obj/structure/disposalpipe/segment{ + dir = 4 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 }, /obj/structure/cable/green{ d1 = 4; @@ -2311,22 +2292,24 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aen" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/disposalpipe/segment{ +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, -/obj/structure/sign/chemistry{ - pixel_y = 32 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, /obj/structure/cable/green{ @@ -2334,15 +2317,23 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/hidden{ +/obj/structure/sign/chemistry{ + pixel_y = 32 + }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, +/obj/structure/disposalpipe/segment{ dir = 4 }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aeo" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/light/spot{ dir = 1 }, @@ -2366,12 +2357,17 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aep" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/structure/disposalpipe/segment{ dir = 8; icon_state = "pipe-c" @@ -2390,6 +2386,14 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aeq" = ( @@ -2437,9 +2441,6 @@ /turf/simulated/wall/ocp_wall, /area/thruster/d1starboard) "aeu" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, @@ -2464,6 +2465,14 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aev" = ( @@ -2477,9 +2486,6 @@ /turf/simulated/floor/tiled/freezer, /area/security/brig) "aew" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 2 }, @@ -2497,6 +2503,15 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/machinery/hologram/holopad, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 10; + icon_state = "corner_white" + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aex" = ( @@ -2567,12 +2582,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/crew_quarters/safe_room/firstdeck) "aeB" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -2587,6 +2596,17 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite/corner{ + icon_state = "borderfloorcorner_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aeC" = ( @@ -2937,10 +2957,6 @@ /turf/simulated/floor/tiled/white, /area/medical/foyer) "afa" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 8; - icon_state = "corner_white_three_quarters" - }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 }, @@ -2957,31 +2973,26 @@ d2 = 8; icon_state = "2-8" }, +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 8 + }, +/obj/effect/floor_decal/borderfloorwhite/corner, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "afb" = ( -/obj/effect/floor_decal/industrial/hatch/blue, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/multi_tile/glass/medical{ - dir = 4; - id_tag = "ETC_hall"; - name = "Treatment Center" +/obj/effect/floor_decal/corner/blue{ + dir = 5 }, -/obj/machinery/door/blast/shutters{ - density = 0; - dir = 4; - icon_state = "shutter0"; - id_tag = "infirm_pub"; - name = "Infirmary Public Hallway Lockdown"; - opacity = 0 +/obj/effect/floor_decal/borderfloorwhite, +/obj/structure/disposalpipe/segment{ + dir = 4 }, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/sleeper) +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "afc" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -2989,39 +3000,47 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, +/obj/effect/floor_decal/industrial/firstaid, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "afd" = ( -/obj/effect/floor_decal/spline/plain/blue{ - dir = 9; - icon_state = "spline_plain" +/obj/effect/floor_decal/industrial/firstaid/corner{ + icon_state = "stripecorner"; + dir = 8 }, -/obj/structure/bed/roller, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "afe" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) -"aff" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 1 - }, -/obj/structure/disposalpipe/segment, /obj/structure/cable/green{ d1 = 1; d2 = 2; icon_state = "1-2" }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) -"afg" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 1 + }, +/obj/effect/floor_decal/corner/blue{ dir = 8; - icon_state = "corner_white_three_quarters" + icon_state = "corner_white" + }, +/obj/effect/floor_decal/borderfloorwhite/corner{ + icon_state = "borderfloorcorner_white"; + dir = 8 }, +/obj/structure/disposalpipe/junction/mirrored{ + icon_state = "pipe-j2"; + dir = 1 + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) +"aff" = ( /obj/structure/disposalpipe/sortjunction/flipped{ dir = 2; name = "Medical Foyer"; @@ -3029,29 +3048,35 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 8 + }, +/obj/effect/floor_decal/corner/blue, +/obj/effect/floor_decal/borderfloorwhite/corner, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) -"afh" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, +"afg" = ( /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) -"afi" = ( -/obj/effect/floor_decal/corner/paleblue{ +"afh" = ( +/obj/machinery/camera/network/medbay{ + c_tag = "Infirmary - Lobby"; + dir = 1 + }, +/obj/effect/floor_decal/corner/blue{ dir = 5 }, +/obj/effect/floor_decal/borderfloorwhite, /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/hologram/holopad, -/obj/machinery/camera/network/medbay{ - c_tag = "Infirmary - Lobby"; - dir = 1 - }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "afj" = ( @@ -3060,9 +3085,6 @@ /turf/simulated/floor/tiled/monotile, /area/security/wing) "afk" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/disposalpipe/segment{ dir = 4 }, @@ -3073,27 +3095,21 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "afl" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 1 - }, /obj/structure/disposalpipe/segment{ dir = 4 }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) -"afm" = ( -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/structure/bed/chair/padded/blue{ - dir = 8; - icon_state = "chair_preview" - }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/effect/floor_decal/borderfloorwhite, +/obj/effect/floor_decal/corner/blue{ + dir = 5 }, -/turf/simulated/floor/tiled/white/monotile, +/turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "afn" = ( /obj/structure/closet/l3closet/general, @@ -3108,7 +3124,6 @@ /turf/simulated/floor/tiled/dark/monotile, /area/medical/foyer/storeroom) "afo" = ( -/obj/effect/wallframe_spawn/no_grille, /obj/structure/disposalpipe/segment{ dir = 4 }, @@ -3120,12 +3135,10 @@ name = "Infirmary Window Lockdown"; opacity = 0 }, +/obj/effect/wallframe_spawn/reinforced/no_grille, /turf/simulated/floor/plating, /area/medical/foyer) "afp" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/structure/disposalpipe/segment{ dir = 2; icon_state = "pipe-c" @@ -3133,6 +3146,9 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, /turf/simulated/floor/tiled/white, /area/medical/foyer) "afq" = ( @@ -3195,15 +3211,10 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/aftstarboard) "afu" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, +/obj/effect/floor_decal/corner/blue, /turf/simulated/floor/tiled/white, /area/medical/foyer) "afv" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/power/apc{ dir = 2; name = "south bump"; @@ -3213,6 +3224,10 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/foyer) "afw" = ( @@ -3225,9 +3240,6 @@ /turf/simulated/floor/reinforced, /area/thruster/d1starboard) "afx" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -3238,15 +3250,14 @@ /turf/simulated/floor/tiled/white, /area/medical/foyer) "afy" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/structure/closet/crate/secure/biohazard/alt, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/alarm{ dir = 1; pixel_y = -22 }, +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/foyer) "afz" = ( @@ -3265,17 +3276,21 @@ /turf/simulated/floor/reinforced, /area/thruster/d1starboard) "afB" = ( -/obj/structure/bed/roller, -/obj/effect/floor_decal/spline/plain/blue{ - dir = 5; - icon_state = "spline_plain" - }, /obj/machinery/firealarm{ dir = 1; pixel_x = 0; pixel_y = -24 }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/obj/structure/table/rack, +/obj/item/roller{ + pixel_y = 8 + }, +/obj/item/roller{ + pixel_y = 8 + }, +/obj/effect/floor_decal/industrial/outline/yellow, +/turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "afC" = ( /obj/effect/floor_decal/techfloor{ @@ -3289,18 +3304,10 @@ /turf/simulated/floor/tiled/techfloor, /area/crew_quarters/safe_room/firstdeck) "afD" = ( -/obj/item/modular_computer/telescreen/preset/medical{ - pixel_y = -32 - }, /obj/machinery/light/spot, /obj/structure/table/standard, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "afE" = ( /obj/machinery/hologram/holopad, @@ -3320,13 +3327,14 @@ /turf/simulated/floor/tiled/techfloor, /area/crew_quarters/safe_room/firstdeck) "afH" = ( -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/structure/table/reinforced, -/obj/machinery/cell_charger, -/obj/item/modular_computer/telescreen/preset/medical{ - pixel_y = 32 +/obj/structure/bed/chair/comfy/blue, +/obj/machinery/light{ + dir = 1 }, -/turf/simulated/floor/tiled/white/monotile, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/turf/simulated/floor/tiled/white, /area/medical/staging) "afI" = ( /turf/simulated/wall/prepainted, @@ -3345,14 +3353,10 @@ /turf/simulated/floor/tiled/steel_ridged, /area/hallway/primary/firstdeck/fore) "afK" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/structure/table/standard, -/turf/simulated/floor/tiled/white, +/obj/item/defibrillator/loaded, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "afL" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -3522,14 +3526,6 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) "afY" = ( -/obj/machinery/vending/medical/torch{ - dir = 1 - }, -/obj/effect/floor_decal/corner/paleblue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/sleeper) -"afZ" = ( -/obj/effect/wallframe_spawn/no_grille, /obj/machinery/door/blast/shutters{ density = 0; dir = 4; @@ -3538,20 +3534,21 @@ name = "Infirmary Window Lockdown"; opacity = 0 }, +/obj/effect/wallframe_spawn/reinforced/no_grille, /turf/simulated/floor/plating, /area/medical/sleeper) "aga" = ( /obj/machinery/door/firedoor, -/obj/effect/floor_decal/industrial/hatch/blue, /obj/machinery/door/airlock/multi_tile/glass/medical{ id_tag = "ETC_stage"; name = "Treatment Center" }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "agb" = ( /obj/machinery/door/firedoor, -/obj/effect/floor_decal/industrial/hatch/blue, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -3559,12 +3556,22 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "agc" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/obj/effect/floor_decal/corner/blue{ dir = 6 }, +/obj/effect/floor_decal/industrial/firstaid{ + icon_state = "stripe"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "agd" = ( @@ -3574,17 +3581,15 @@ /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "agf" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, /obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, +/obj/effect/floor_decal/corner/blue{ + dir = 9 }, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) @@ -3605,20 +3610,12 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/crew_quarters/safe_room/medical) "agh" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 8 }, -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "agi" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -3626,12 +3623,29 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/industrial/firstaid{ + icon_state = "stripe"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "agj" = ( -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/structure/bed/roller, -/turf/simulated/floor/tiled/white/monotile, +/obj/effect/floor_decal/industrial/firstaid/corner{ + icon_state = "stripecorner"; + dir = 1 + }, +/obj/item/stool/padded, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/turf/simulated/floor/tiled/white, /area/medical/staging) "agl" = ( /obj/effect/wallframe_spawn/reinforced/polarized/no_grille{ @@ -3648,9 +3662,6 @@ /turf/simulated/floor/tiled/dark, /area/medical/foyer/storeroom) "ago" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -3662,6 +3673,13 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "agp" = ( @@ -3698,10 +3716,6 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) "ags" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 }, @@ -3713,6 +3727,13 @@ d2 = 8; icon_state = "1-8" }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "agt" = ( @@ -3785,11 +3806,9 @@ /turf/simulated/floor/reinforced, /area/thruster/d1starboard) "agx" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 +/obj/structure/bed/roller, +/obj/effect/floor_decal/corner/blue{ + dir = 6 }, /turf/simulated/floor/tiled/white, /area/medical/staging) @@ -3817,10 +3836,11 @@ /turf/simulated/floor/tiled/techfloor, /area/crew_quarters/safe_room/firstdeck) "agB" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/machinery/atmospherics/pipe/manifold/hidden, +/obj/effect/floor_decal/corner/blue{ dir = 5 }, -/obj/machinery/atmospherics/pipe/manifold/hidden, +/obj/effect/floor_decal/borderfloorwhite, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "agC" = ( @@ -3860,20 +3880,21 @@ /turf/simulated/floor/tiled/dark/monotile, /area/command/conference) "agH" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/bed/chair/padded/blue, +/obj/structure/table/reinforced, +/obj/item/defibrillator/loaded, +/obj/item/auto_cpr, +/obj/item/modular_computer/laptop/preset/custom_loadout/standard, /turf/simulated/floor/tiled/white, /area/medical/staging) "agI" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 9 }, +/obj/effect/floor_decal/industrial/firstaid, /turf/simulated/floor/tiled/white, /area/medical/staging) "agJ" = ( @@ -3896,9 +3917,6 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/forestarboard) "agK" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/button/alternate/door{ id_tag = "inf_stage"; name = "Infirmary Staging Exit"; @@ -3910,6 +3928,13 @@ }, /obj/effect/floor_decal/sign/tr, /obj/machinery/light/spot, +/obj/effect/floor_decal/industrial/firstaid/corner{ + icon_state = "stripecorner"; + dir = 8 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "agM" = ( @@ -4003,15 +4028,14 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/centralstarboard) "agU" = ( -/obj/effect/floor_decal/industrial/hatch/blue, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/glass/medical{ - autoset_access = 0; - id_tag = "inf_recep"; - name = "Infirmary Reception"; - req_access = list("ACCESS_MEDICAL") +/obj/item/device/radio/intercom/department/medbay{ + dir = 1; + pixel_y = -28 }, -/turf/simulated/floor/tiled/white/monotile, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/turf/simulated/floor/tiled/white, /area/medical/staging) "agV" = ( /obj/effect/floor_decal/industrial/warning/corner{ @@ -4052,14 +4076,18 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/firstdeck/centralstarboard) "agY" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/glass/medical{ + autoset_access = 0; + id_tag = "inf_recep"; + name = "Infirmary Reception"; + req_access = list("ACCESS_MEDICAL") }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "agZ" = ( /obj/machinery/door/firedoor, -/obj/effect/floor_decal/industrial/hatch/blue, /obj/machinery/door/airlock/multi_tile/glass/medical{ id_tag = "inf_stage"; name = "Infirmary Staging" @@ -4072,11 +4100,12 @@ name = "Infirmary Staging Lockdown"; opacity = 0 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, /turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "aha" = ( /obj/machinery/door/firedoor, -/obj/effect/floor_decal/industrial/hatch/blue, /obj/machinery/door/blast/shutters{ density = 0; dir = 2; @@ -4085,6 +4114,8 @@ name = "Infirmary Staging Lockdown"; opacity = 0 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, /turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "ahb" = ( @@ -4093,18 +4124,6 @@ /obj/structure/catwalk, /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) -"ahc" = ( -/obj/effect/wallframe_spawn/reinforced/no_grille, -/obj/machinery/door/blast/shutters{ - density = 0; - dir = 2; - icon_state = "shutter0"; - id_tag = "inf_recep_window"; - name = "Infirmary Reception Window Lockdown"; - opacity = 0 - }, -/turf/simulated/floor/plating, -/area/medical/staging) "ahd" = ( /turf/simulated/wall/prepainted, /area/medical/counselor/therapy) @@ -5805,11 +5824,11 @@ /obj/item/storage/box/gloves, /obj/item/clothing/glasses/hud/health, /obj/item/clothing/glasses/hud/health, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/light_switch{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "alR" = ( @@ -6010,8 +6029,6 @@ /area/maintenance/firstdeck/centralstarboard) "amP" = ( /obj/structure/table/rack, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/requests_console{ announcementConsole = 1; department = "Medbay"; @@ -6034,6 +6051,8 @@ /obj/item/storage/box/autoinjectors, /obj/item/storage/box/beakers, /obj/item/storage/box/syringes, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "amZ" = ( @@ -6145,43 +6164,26 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "anH" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/machinery/button/alternate/door{ - id_tag = "inf_stage"; - name = "Infirmary Staging Exit"; - pixel_x = -7; - pixel_y = 23 - }, -/obj/machinery/button/alternate/door{ - id_tag = "ETC_stage"; - name = "Treatment to Staging"; - pixel_x = -7; - pixel_y = 33 - }, -/obj/machinery/button/alternate/door{ - id_tag = "ETC_hall"; - name = "Treatment Center Exit"; - pixel_x = 8; - pixel_y = 33 +/obj/structure/table/reinforced, +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 4; + icon_state = "shutter0"; + id_tag = "inf_recep_window"; + name = "Infirmary Reception Window Lockdown"; + opacity = 0 }, -/obj/machinery/button/alternate/door{ - id_tag = "inf_recep"; - name = "Reception Doors"; - pixel_x = 8; - pixel_y = 23 +/obj/machinery/door/window/eastright{ + base_state = "left"; + dir = 4; + icon_state = "left"; + name = "Medical Reception" }, -/obj/structure/bed/chair/padded/blue, -/turf/simulated/floor/tiled/white, +/obj/machinery/door/firedoor, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "anL" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -6190,6 +6192,13 @@ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/hidden, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "anM" = ( @@ -6357,19 +6366,13 @@ /obj/random/medical, /obj/random/medical, /obj/random/medical, -/obj/random/medical/lite, -/obj/random/medical/lite, -/obj/effect/floor_decal/corner/paleblue/mono, +/obj/random/medical, +/obj/effect/floor_decal/corner/blue/mono, /obj/effect/floor_decal/industrial/outline/yellow, +/obj/random/medical/lite, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "apR" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -6497,28 +6500,21 @@ /turf/simulated/floor/tiled/steel_grid, /area/turbolift/robotics_lift) "aqL" = ( -/obj/effect/floor_decal/corner/paleblue, -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) -"aqM" = ( /obj/machinery/status_display, /turf/simulated/wall/prepainted, +/area/medical/exam_room) +"aqM" = ( +/obj/structure/flora/pottedplant/flower, +/turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aqP" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ +/obj/structure/bed/chair/padded/blue{ + icon_state = "chair_preview"; dir = 4 }, -/obj/structure/table/standard, -/obj/item/reagent_containers/food/drinks/glass2/coffeecup/one{ - custom_desc = "A white coffee cup, prominently featuring a #1 Gorpsman. Someone has scratched the C into a G." - }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "aqQ" = ( -/obj/effect/wallframe_spawn/no_grille, /obj/machinery/door/blast/shutters{ density = 0; dir = 4; @@ -6527,26 +6523,27 @@ name = "Infirmary Window Lockdown"; opacity = 0 }, +/obj/effect/wallframe_spawn/reinforced/no_grille, /turf/simulated/floor/plating, /area/medical/foyer) "aqR" = ( /obj/machinery/disposal, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/hatch/yellow, /obj/structure/disposalpipe/trunk{ dir = 1 }, /obj/machinery/light, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/hatch/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/foyer) "aqS" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/structure/flora/pottedplant/orientaltree, /obj/machinery/firealarm{ dir = 1; pixel_x = 0; pixel_y = -24 }, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/foyer) "aqT" = ( @@ -6741,10 +6738,6 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "arT" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/sign/ex, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -6752,10 +6745,10 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/sign/ex, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "ase" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/atmospherics/unary/vent_pump/on, /obj/machinery/alarm{ pixel_y = 24 @@ -6765,6 +6758,8 @@ pixel_x = 32 }, /obj/machinery/papershredder, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "ask" = ( @@ -6890,28 +6885,21 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "ata" = ( -/obj/machinery/light_switch{ - pixel_x = -22; - pixel_y = 24 - }, -/obj/machinery/button/windowtint{ - id = "exam_windows"; - pixel_x = -30; - pixel_y = 24; - pixel_z = 0 +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/effect/floor_decal/corner/b_green/mono, +/obj/machinery/body_scanconsole{ + icon_state = "body_scannerconsole"; + dir = 8 }, -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 4 +/obj/effect/floor_decal/spline/plain{ + dir = 6; + icon_state = "spline_plain" }, -/turf/simulated/floor/tiled/white, +/turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "atb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 @@ -6921,6 +6909,9 @@ d2 = 4; icon_state = "1-4" }, +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 8 + }, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "atf" = ( @@ -6935,7 +6926,6 @@ /obj/item/folder/white, /obj/item/folder/white, /obj/item/folder/white, -/obj/effect/floor_decal/corner/paleblue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "atg" = ( @@ -6943,15 +6933,9 @@ dir = 5 }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /turf/simulated/floor/tiled/white, /area/medical/medpaperworkoffice) "atm" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 }, @@ -6959,6 +6943,9 @@ dir = 8; pixel_x = 24 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/locker) "atn" = ( @@ -7126,11 +7113,6 @@ }, /turf/simulated/floor/tiled/white, /area/assembly/robotics/laboratory) -"aua" = ( -/obj/structure/bed/padded, -/obj/item/bedsheet/medical, -/turf/simulated/floor/tiled/white, -/area/medical/exam_room) "aub" = ( /obj/machinery/vending/wallmed1{ dir = 8; @@ -7142,27 +7124,22 @@ /obj/structure/bed/chair/comfy/blue{ dir = 8 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "auc" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/structure/table/standard, /obj/item/paper_bin, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "aug" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/locker) "auj" = ( @@ -7420,42 +7397,36 @@ /turf/simulated/floor/reinforced, /area/rnd/misc_lab) "avl" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 +/obj/structure/bed/padded, +/obj/item/bedsheet/medical, +/obj/effect/floor_decal/corner/lime/mono, +/obj/effect/floor_decal/spline/plain{ + dir = 5; + icon_state = "spline_plain" }, -/obj/structure/closet/secure_closet/personal/patient, -/turf/simulated/floor/tiled/white, +/turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "avo" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/camera/network/medbay{ c_tag = "Infirmary - Examination Room"; dir = 1 }, -/obj/item/device/radio/intercom/department/medbay{ - dir = 1; - pixel_y = -28 +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 }, +/obj/machinery/light, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "avq" = ( /obj/item/stool/padded, -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/light/small, /obj/random_multi/single_item/runtime, -/obj/random_multi/single_item/skelestand, /obj/structure/curtain/open/bed, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "avs" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/computer/modular/preset/medical{ dir = 1 }, @@ -7464,15 +7435,12 @@ icon_state = "nosmoking"; pixel_x = -37 }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, +/turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "avu" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, +/obj/effect/floor_decal/corner/paleblue, /obj/machinery/computer/modular/preset/medical{ dir = 1 }, @@ -7480,7 +7448,9 @@ dir = 1; pixel_y = -28 }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, +/turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "avx" = ( /obj/structure/cable/green{ @@ -7520,10 +7490,10 @@ /turf/simulated/floor/tiled, /area/security/wing) "avO" = ( -/obj/effect/floor_decal/corner/pink/mono, /obj/machinery/body_scanconsole{ dir = 1 }, +/obj/effect/floor_decal/corner/b_green/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "avS" = ( @@ -7719,13 +7689,13 @@ /turf/simulated/floor/tiled/dark/monotile, /area/command/armoury/access) "awM" = ( -/obj/effect/floor_decal/corner/paleblue/half{ - dir = 1; - icon_state = "bordercolorhalf" - }, /obj/machinery/atm{ pixel_y = 32 }, +/obj/effect/floor_decal/corner/blue/half{ + dir = 1; + icon_state = "bordercolorhalf" + }, /turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/aft) "awV" = ( @@ -7794,7 +7764,7 @@ /area/hallway/primary/firstdeck/center) "axh" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/effect/floor_decal/corner/paleblue/half{ +/obj/effect/floor_decal/corner/blue/half{ dir = 1; icon_state = "bordercolorhalf" }, @@ -7835,46 +7805,21 @@ /turf/simulated/floor/plating, /area/security/bo) "axw" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /obj/structure/disposalpipe/segment, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) -"axy" = ( -/obj/structure/sign/directions/med{ - pixel_y = 32 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/turf/simulated/floor/tiled, -/area/hallway/primary/firstdeck/aft) "axB" = ( -/obj/effect/floor_decal/corner/paleblue/half{ - dir = 1; - icon_state = "bordercolorhalf" - }, /obj/machinery/newscaster{ pixel_y = 32 }, -/turf/simulated/floor/tiled/monotile, -/area/hallway/primary/firstdeck/aft) -"axD" = ( -/obj/structure/cable/green{ - d2 = 2; - icon_state = "0-2" - }, -/obj/machinery/power/apc{ +/obj/effect/floor_decal/corner/blue/half{ dir = 1; - name = "north bump"; - pixel_x = 0; - pixel_y = 24 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 + icon_state = "bordercolorhalf" }, -/turf/simulated/floor/tiled, +/turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/aft) "axF" = ( /obj/structure/cable/green{ @@ -8270,29 +8215,6 @@ /obj/effect/catwalk_plated, /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/aft) -"ayC" = ( -/obj/structure/cable/green{ - d1 = 1; - d2 = 8; - icon_state = "1-8" - }, -/obj/structure/cable/green{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/catwalk_plated, -/turf/simulated/floor/plating, -/area/hallway/primary/firstdeck/aft) "ayF" = ( /obj/structure/cable/green{ d1 = 4; @@ -8475,6 +8397,13 @@ }, /turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/center) +"azu" = ( +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite, +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "azN" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -9886,6 +9815,17 @@ }, /turf/simulated/floor/plating, /area/maintenance/firstdeck/foreport) +"aGp" = ( +/obj/structure/disposalpipe/segment, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "aGq" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9; @@ -9952,14 +9892,9 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralport) "aGx" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/item/stool/padded, -/turf/simulated/floor/tiled/white, +/obj/structure/table/reinforced, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "aGE" = ( /obj/effect/wallframe_spawn/reinforced/no_grille, @@ -10795,12 +10730,12 @@ /turf/simulated/floor/plating, /area/security/bo) "aKG" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/closet/hydrant{ pixel_y = 32 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) "aKH" = ( @@ -14679,17 +14614,21 @@ /turf/simulated/floor/reinforced, /area/thruster/d1starboard) "bhy" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/iv_drip, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/light{ dir = 1 }, /obj/item/device/radio/intercom/department/medbay{ pixel_y = 23 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white, /area/medical/foyer) "bhL" = ( @@ -14777,11 +14716,11 @@ /obj/item/reagent_containers/spray/cleaner{ pixel_x = -5 }, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/sign/warning/nosmoking_1{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "bmb" = ( @@ -15076,20 +15015,12 @@ dir = 8; icon_state = "pipe-c" }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, -/obj/structure/cable/green{ - d1 = 1; - d2 = 4; - icon_state = "1-4" - }, -/obj/structure/cable/green{ - d1 = 1; - d2 = 8; - icon_state = "1-8" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/aft) "bFb" = ( @@ -15132,6 +15063,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "bHb" = ( @@ -15203,13 +15138,19 @@ /obj/item/pen/crayon, /obj/item/pen, /obj/item/sticky_pad/random, -/obj/effect/floor_decal/corner/paleblue/mono, /obj/structure/table/glass, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "bMb" = ( /turf/simulated/wall/prepainted, /area/engineering/hardstorage/aux) +"bMp" = ( +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/turf/simulated/floor/tiled/white, +/area/medical/staging) "bMB" = ( /obj/effect/floor_decal/corner_techfloor_grid{ dir = 6; @@ -15449,12 +15390,12 @@ /area/security/detectives_office) "bWA" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) "bXb" = ( @@ -15528,7 +15469,6 @@ /turf/simulated/floor/plating, /area/shuttle/escape_pod17/station) "cbj" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/computer/modular/preset/medical, /obj/machinery/alarm{ pixel_y = 24 @@ -15537,6 +15477,7 @@ dir = 4; pixel_x = 21 }, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "ccb" = ( @@ -15601,6 +15542,10 @@ /obj/structure/catwalk, /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) +"cic" = ( +/obj/item/stool/padded, +/turf/simulated/floor/tiled/white, +/area/medical/staging) "cix" = ( /obj/effect/floor_decal/corner/red{ dir = 9; @@ -15656,9 +15601,6 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/aftstarboard) "cmY" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 10 }, @@ -15692,6 +15634,30 @@ /obj/item/storage/box/bodybags, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) +"cqc" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "csb" = ( /obj/structure/bed/padded, /obj/item/bedsheet/blue, @@ -15709,10 +15675,6 @@ dir = 8; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 6; - icon_state = "corner_white" - }, /obj/structure/hygiene/sink{ dir = 8; icon_state = "sink"; @@ -15726,6 +15688,10 @@ dir = 4; pixel_x = -24 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "cub" = ( @@ -16070,7 +16036,6 @@ /turf/simulated/floor/tiled/techfloor, /area/crew_quarters/safe_room/medical) "cNb" = ( -/obj/item/usedcryobag, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, @@ -16095,12 +16060,12 @@ dir = 9; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue, /obj/machinery/alarm{ dir = 4; pixel_x = -21; pixel_y = 0 }, +/obj/effect/floor_decal/corner/b_green, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "cOH" = ( @@ -16162,12 +16127,13 @@ dir = 1; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "cUb" = ( @@ -16434,9 +16400,6 @@ dir = 1; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/oxygen_pump{ pixel_x = -32; pixel_y = 32 @@ -16444,6 +16407,10 @@ /obj/machinery/body_scan_display{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "dkG" = ( @@ -16545,6 +16512,37 @@ }, /turf/simulated/floor/tiled/freezer, /area/medical/washroom) +"dpZ" = ( +/obj/machinery/button/alternate/door{ + id_tag = "ETC_hall"; + name = "Treatment Center Exit"; + pixel_x = 24; + pixel_y = 24 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 1 + }, +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "dqb" = ( /obj/structure/hygiene/sink{ dir = 1; @@ -16753,27 +16751,11 @@ /obj/random/medical, /obj/random/medical, /obj/random/medical, -/obj/random/medical/lite, -/obj/random/medical/lite, -/obj/random/medical/lite, -/obj/random/medical/lite, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/item/defibrillator/loaded, /obj/structure/closet/secure_closet/medical_wall{ name = "Medical Equipment Cabinet"; pixel_x = 0; pixel_y = 32 }, -/obj/structure/cable/green{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, /obj/item/reagent_containers/syringe/antiviral, /obj/item/reagent_containers/syringe/antiviral, /obj/item/reagent_containers/syringe/antiviral, @@ -16783,25 +16765,28 @@ /obj/item/reagent_containers/glass/bottle/antitoxin, /obj/item/reagent_containers/glass/bottle/antitoxin, /obj/item/reagent_containers/glass/bottle/inaprovaline, -/turf/simulated/floor/tiled/white, -/area/medical/sleeper) -"dMe" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/effect/floor_decal/spline/plain/blue{ + dir = 6; + icon_state = "spline_plain" }, -/obj/structure/sign/directions/med{ - pixel_y = 32 +/obj/random/medical/lite, +/obj/random/medical/lite, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" }, -/turf/simulated/floor/tiled, -/area/hallway/primary/firstdeck/aft) +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "dNb" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/alarm{ dir = 8; icon_state = "alarm0"; pixel_x = 24 }, /obj/machinery/atmospherics/unary/cryo_cell, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "dPA" = ( @@ -16829,17 +16814,17 @@ dir = 8; pixel_x = -24 }, -/obj/random/medical/lite, +/obj/random/medical, /obj/random/medical/lite, /turf/simulated/floor/tiled/dark/monotile, /area/medical/foyer/storeroom) "dRf" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/closet/hydrant{ pixel_y = 32 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/center) "dRs" = ( @@ -16865,13 +16850,13 @@ /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/firealarm{ dir = 4; pixel_x = 21 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/medpaperworkoffice) "dTb" = ( @@ -16905,10 +16890,11 @@ /turf/simulated/wall/r_wall/hull, /area/command/armoury/tactical) "dWb" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/machinery/atmospherics/unary/vent_scrubber/on, +/obj/effect/floor_decal/corner/blue{ dir = 5 }, -/obj/machinery/atmospherics/unary/vent_scrubber/on, +/obj/effect/floor_decal/borderfloorwhite, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "dWA" = ( @@ -16929,27 +16915,7 @@ /obj/machinery/door/firedoor, /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/firstdeck/forestarboard) -"dXM" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, -/obj/machinery/light/spot{ - dir = 4 - }, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 8 - }, -/obj/item/device/radio/intercom{ - dir = 8; - pixel_x = 21 - }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) "dYb" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ - dir = 8; - icon_state = "corner_white_three_quarters" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -16960,10 +16926,16 @@ /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 6 }, +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 8 + }, +/obj/effect/floor_decal/borderfloorwhite/corner, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "dZb" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/structure/disposalpipe/segment, +/obj/effect/floor_decal/borderfloorwhite, +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled/white, @@ -16986,12 +16958,6 @@ /turf/simulated/floor/tiled/monotile, /area/security/locker) "eab" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -17004,12 +16970,16 @@ d2 = 8; icon_state = "2-8" }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "eaF" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -17032,17 +17002,17 @@ /turf/simulated/floor/tiled/dark/monotile, /area/security/nuke_storage) "ebb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/unary/vent_pump/on, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 9; icon_state = "intact" }, -/obj/item/device/radio/intercom/department/medbay{ - dir = 8; - pixel_x = 24 +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite, +/obj/machinery/light/spot{ + dir = 4 }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) @@ -17209,7 +17179,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled/white, @@ -17282,9 +17252,6 @@ /turf/simulated/floor/tiled/dark/monotile, /area/medical/foyer/storeroom) "ewb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -17296,6 +17263,13 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "ewc" = ( @@ -17308,9 +17282,6 @@ /turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/center) "exb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9 - }, /obj/structure/cable/green{ d1 = 1; d2 = 2; @@ -17329,6 +17300,13 @@ icon_state = "2-4" }, /obj/machinery/atmospherics/pipe/simple/hidden, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "exc" = ( @@ -17422,7 +17400,8 @@ dir = 1; icon_state = "body_scanner_0" }, -/obj/effect/floor_decal/corner/pink/mono, +/obj/effect/floor_decal/corner/b_green/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "eIb" = ( @@ -17507,7 +17486,8 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/floor_decal/industrial/hatch/blue, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery) "eQK" = ( @@ -17595,14 +17575,11 @@ d2 = 8; icon_state = "2-8" }, +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery) "eXk" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/iv_drip, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/recharger/wallcharger{ pixel_y = 24 }, @@ -17610,6 +17587,14 @@ c_tag = "Medical - Foyer"; dir = 8 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white, /area/medical/foyer) "eZQ" = ( @@ -17720,13 +17705,14 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/light{ dir = 8 }, /obj/structure/bed/chair/comfy/blue, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/medpaperworkoffice) "frA" = ( @@ -17748,6 +17734,31 @@ }, /turf/simulated/floor/tiled, /area/security/locker) +"fsX" = ( +/obj/machinery/light_switch{ + pixel_x = -22; + pixel_y = 24 + }, +/obj/machinery/button/windowtint{ + id = "exam_windows"; + pixel_x = -30; + pixel_y = 24; + pixel_z = 0 + }, +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 4 + }, +/obj/effect/floor_decal/corner/b_green/mono, +/obj/machinery/bodyscanner{ + icon_state = "body_scanner_0"; + dir = 8 + }, +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/floor_decal/spline/plain, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/exam_room) "fuV" = ( /obj/machinery/atmospherics/unary/outlet_injector{ dir = 2; @@ -17758,19 +17769,13 @@ }, /turf/simulated/floor/reinforced/oxygen, /area/thruster/d1starboard) -"fvb" = ( -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/machinery/sleeper{ - dir = 4 - }, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/sleeper) "fxb" = ( /obj/machinery/meter, /obj/machinery/atmospherics/pipe/manifold/hidden, /turf/simulated/floor/tiled/dark, /area/medical/foyer/storeroom) "fxe" = ( +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery2) "fBb" = ( @@ -17788,12 +17793,13 @@ dir = 1; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "fDJ" = ( @@ -17814,9 +17820,13 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "fEb" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 6 }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "fHk" = ( @@ -17899,7 +17909,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury) "fMr" = ( -/obj/effect/wallframe_spawn/reinforced/no_grille, /obj/machinery/door/blast/shutters{ density = 0; dir = 2; @@ -17908,6 +17917,7 @@ name = "Infirmary Window Lockdown"; opacity = 0 }, +/obj/effect/wallframe_spawn/reinforced, /turf/simulated/floor/plating, /area/medical/staging) "fMI" = ( @@ -17917,13 +17927,6 @@ /obj/random/material, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/centralstarboard) -"fOb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/flora/pottedplant/flower, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) "fOr" = ( /obj/structure/table/steel, /obj/random/maintenance/solgov/clean, @@ -17949,15 +17952,16 @@ icon_state = "1-2" }, /obj/machinery/door/firedoor, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "fQf" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/locker) "fRN" = ( @@ -17986,15 +17990,19 @@ /turf/simulated/floor/tiled/monotile, /area/security/detectives_office) "fVb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/iv_drip, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/noticeboard{ pixel_x = 0; pixel_y = 32 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white, /area/medical/foyer) "fVw" = ( @@ -18020,18 +18028,16 @@ /turf/simulated/floor/plating, /area/security/wing) "fWb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, /obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 2 }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) @@ -18048,6 +18054,7 @@ icon_state = "1-2" }, /obj/machinery/door/firedoor, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "fYv" = ( @@ -18090,6 +18097,13 @@ /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/engineering/hardstorage/aux) +"gaB" = ( +/obj/effect/floor_decal/industrial/firstaid{ + icon_state = "stripe"; + dir = 1 + }, +/turf/simulated/floor/tiled, +/area/hallway/primary/firstdeck/center) "gbb" = ( /obj/structure/cable/green{ d1 = 1; @@ -18135,10 +18149,6 @@ dir = 8; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 6; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, @@ -18151,6 +18161,10 @@ d2 = 4; icon_state = "0-4" }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "ghI" = ( @@ -18165,6 +18179,7 @@ /turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/center) "gib" = ( +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery) "gig" = ( @@ -18198,9 +18213,9 @@ dir = 4; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 9 }, /turf/simulated/floor/tiled/white, /area/medical/surgery) @@ -18211,6 +18226,8 @@ /turf/simulated/floor/plating, /area/medical/surgery) "glb" = ( +/obj/structure/bed/roller, +/obj/structure/disposalpipe/segment, /obj/effect/floor_decal/spline/plain/blue{ dir = 6; icon_state = "spline_plain" @@ -18274,10 +18291,10 @@ pixel_y = 5 }, /obj/item/storage/firstaid/toxin, -/obj/random/medical/lite, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/light, +/obj/random/medical, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "gvC" = ( @@ -18338,33 +18355,12 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/engineering/hardstorage/aux) "gyV" = ( -/obj/effect/wallframe_spawn/no_grille, +/obj/effect/wallframe_spawn/reinforced/no_grille, /turf/simulated/floor/plating, /area/medical/sleeper) "gyY" = ( /turf/simulated/wall/r_wall/hull, /area/crew_quarters/safe_room/firstdeck) -"gzb" = ( -/obj/structure/table/reinforced, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/machinery/door/blast/shutters{ - density = 0; - dir = 4; - icon_state = "shutter0"; - id_tag = "inf_recep_window"; - name = "Infirmary Reception Window Lockdown"; - opacity = 0 - }, -/obj/machinery/door/window/eastright{ - base_state = "right"; - dir = 4; - icon_state = "right"; - name = "Medical Reception" - }, -/obj/item/storage/medical_lolli_jar, -/obj/machinery/door/firedoor, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/staging) "gzM" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -18394,16 +18390,6 @@ }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) -"gDb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, -/obj/structure/bed/chair/padded/blue{ - dir = 8; - icon_state = "chair_preview" - }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) "gDm" = ( /obj/effect/floor_decal/techfloor{ dir = 4; @@ -18446,7 +18432,8 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "gFb" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; dir = 10 }, /turf/simulated/floor/tiled/white, @@ -18472,6 +18459,15 @@ /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/engineering/hardstorage/aux) +"gMF" = ( +/obj/structure/table/reinforced, +/obj/machinery/cell_charger, +/obj/item/modular_computer/telescreen/preset/medical{ + pixel_y = 32 + }, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/staging) "gQb" = ( /obj/structure/dispenser{ oxygentanks = 0 @@ -18492,6 +18488,17 @@ /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /turf/simulated/floor/tiled/techfloor, /area/crew_quarters/safe_room/firstdeck) +"gQx" = ( +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/obj/effect/floor_decal/industrial/firstaid, +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "gQI" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 @@ -18507,13 +18514,10 @@ /turf/simulated/floor/tiled, /area/assembly/robotics/office) "gRb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/item/stool/padded, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 }, +/obj/effect/floor_decal/corner/blue/three_quarters, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "gVR" = ( @@ -18536,16 +18540,24 @@ /turf/simulated/floor/tiled/dark, /area/command/conference) "gWb" = ( -/obj/effect/wallframe_spawn/reinforced/no_grille, +/obj/structure/disposalpipe/segment, /obj/machinery/door/blast/shutters{ density = 0; dir = 2; icon_state = "shutter0"; - id_tag = "infirm_windows"; - name = "Infirmary Window Lockdown"; + id_tag = "infirm_pub"; + name = "Infirmary Public Hallway Lockdown"; opacity = 0 }, -/turf/simulated/floor/plating, +/obj/structure/cable/green{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/turf/simulated/floor/tiled/white/monotile, /area/medical/medicalhallway) "gWY" = ( /obj/machinery/mech_recharger, @@ -18596,10 +18608,11 @@ /turf/simulated/wall/prepainted, /area/rnd/development) "haV" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/machinery/atmospherics/pipe/manifold/hidden/supply, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; dir = 10 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply, /turf/simulated/floor/tiled/white, /area/medical/locker) "hbP" = ( @@ -18624,16 +18637,19 @@ /obj/machinery/camera/network/first_deck{ c_tag = "First Deck Hallway - Ladders" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) "hdb" = ( -/obj/effect/floor_decal/corner/paleblue/half{ +/obj/structure/sign/directions/med{ + pixel_y = 32 + }, +/obj/effect/floor_decal/corner/blue/half{ dir = 1; icon_state = "bordercolorhalf" }, @@ -18696,9 +18712,6 @@ /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/aft) "hgt" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 }, @@ -18706,6 +18719,10 @@ dir = 8; pixel_x = -24 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/locker) "hgP" = ( @@ -18714,12 +18731,12 @@ dir = 9; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue, /obj/machinery/alarm{ dir = 4; pixel_x = -21; pixel_y = 0 }, +/obj/effect/floor_decal/corner/b_green, /turf/simulated/floor/tiled/white, /area/medical/surgery) "hhb" = ( @@ -18733,9 +18750,6 @@ name = "Research Equipment"; sort_type = "Research Equipment" }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 1 - }, /obj/structure/cable/green{ d1 = 2; d2 = 8; @@ -18745,6 +18759,17 @@ /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, +/obj/structure/cable/green{ + d1 = 1; + d2 = 8; + icon_state = "1-8" + }, +/obj/structure/cable/green{ + d1 = 1; + d2 = 4; + icon_state = "1-4" + }, +/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers, /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/aft) "hhB" = ( @@ -18780,19 +18805,20 @@ dir = 4; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 9 }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "hlb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "hml" = ( @@ -19023,9 +19049,13 @@ /turf/simulated/wall/prepainted, /area/rnd/development) "hwx" = ( -/obj/effect/floor_decal/corner/paleblue/three_quarters{ +/obj/effect/floor_decal/corner/blue/three_quarters{ dir = 1 }, +/obj/effect/floor_decal/borderfloorwhite/corner{ + icon_state = "borderfloorcorner_white"; + dir = 8 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "hyb" = ( @@ -19070,6 +19100,25 @@ }, /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/fore) +"hyG" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 1 + }, +/obj/structure/cable/green{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "hzb" = ( /obj/structure/cable/green{ d1 = 1; @@ -19190,14 +19239,17 @@ /turf/simulated/floor/tiled/techfloor, /area/crew_quarters/safe_room/medical) "hEU" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/machinery/light/spot{ + dir = 4 }, -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 2 +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, +/obj/item/device/radio/intercom{ + dir = 8; + pixel_x = 21 }, +/obj/effect/floor_decal/corner/blue, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "hFb" = ( @@ -19241,6 +19293,14 @@ }, /turf/simulated/floor/tiled, /area/rnd/entry) +"hGo" = ( +/obj/machinery/door/airlock/medical{ + id_tag = "examdoor"; + name = "Examination Room" + }, +/obj/machinery/door/firedoor, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/exam_room) "hGs" = ( /turf/simulated/wall/prepainted, /area/security/locker) @@ -19349,6 +19409,8 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, +/obj/structure/table/standard, +/obj/item/modular_computer/laptop/preset/custom_loadout/standard, /turf/simulated/floor/tiled/white, /area/medical/staging) "hQb" = ( @@ -19484,6 +19546,27 @@ /obj/effect/wallframe_spawn/reinforced_phoron, /turf/simulated/floor/reinforced, /area/thruster/d1port) +"ieS" = ( +/obj/structure/table/reinforced, +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 4; + icon_state = "shutter0"; + id_tag = "inf_recep_window"; + name = "Infirmary Reception Window Lockdown"; + opacity = 0 + }, +/obj/machinery/door/window/eastright{ + base_state = "right"; + dir = 4; + icon_state = "right"; + name = "Medical Reception" + }, +/obj/item/storage/medical_lolli_jar, +/obj/machinery/door/firedoor, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/staging) "igb" = ( /obj/structure/cable/green{ d1 = 1; @@ -19496,6 +19579,22 @@ /obj/structure/catwalk, /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralport) +"igk" = ( +/obj/effect/floor_decal/corner/blue{ + dir = 1 + }, +/obj/structure/cable/green{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" + }, +/turf/simulated/floor/tiled, +/area/hallway/primary/firstdeck/aft) "iib" = ( /obj/structure/cable/green{ d1 = 4; @@ -19603,10 +19702,10 @@ pixel_x = 0; pixel_y = 32 }, -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/recharger, /obj/structure/table/glass, /obj/random/smokes, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "iqb" = ( @@ -19742,7 +19841,7 @@ /turf/simulated/wall/prepainted, /area/rnd/misc_lab) "ivT" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled, @@ -19765,9 +19864,6 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralport) "ixf" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9 - }, /obj/structure/cable/green{ d1 = 1; d2 = 2; @@ -19776,6 +19872,13 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "ixB" = ( @@ -19912,10 +20015,11 @@ /area/security/detectives_office) "iGb" = ( /obj/effect/floor_decal/floordetail/edgedrain, -/obj/effect/floor_decal/corner/paleblue{ +/obj/machinery/organ_printer/flesh/mapped, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; dir = 5 }, -/obj/machinery/organ_printer/flesh/mapped, /turf/simulated/floor/tiled/white, /area/medical/surgery) "iHb" = ( @@ -19944,6 +20048,7 @@ d2 = 8; icon_state = "4-8" }, +/obj/structure/disposalpipe/segment, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "iKb" = ( @@ -19981,14 +20086,14 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) "iMW" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/cable/green{ d1 = 4; d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "iNb" = ( @@ -20230,6 +20335,7 @@ d2 = 4; icon_state = "1-4" }, +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery) "jcg" = ( @@ -20240,10 +20346,6 @@ /turf/simulated/floor/tiled, /area/security/locker) "jcu" = ( -/obj/effect/floor_decal/spline/plain/blue{ - dir = 5; - icon_state = "spline_plain" - }, /obj/machinery/light/spot{ dir = 8 }, @@ -20251,6 +20353,12 @@ c_tag = "Infirmary - Treatment Centre"; dir = 4 }, +/obj/structure/bed/roller, +/obj/structure/disposalpipe/segment, +/obj/effect/floor_decal/spline/plain/blue{ + dir = 5; + icon_state = "spline_plain" + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "jcO" = ( @@ -20345,6 +20453,7 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery2) "jhS" = ( @@ -20364,10 +20473,6 @@ dir = 8; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 6; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, @@ -20380,6 +20485,10 @@ d2 = 4; icon_state = "0-4" }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "jjb" = ( @@ -20600,6 +20709,7 @@ dir = 1; pixel_y = -28 }, +/obj/effect/floor_decal/industrial/firstaid/corner, /turf/simulated/floor/tiled/white, /area/medical/staging) "jvb" = ( @@ -20631,6 +20741,7 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery) "jxG" = ( @@ -20752,38 +20863,23 @@ /turf/simulated/wall/prepainted, /area/medical/morgue/autopsy) "jCU" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/machinery/button/blast_door{ - id_tag = "infirm_pub"; - name = "Infirmary Public Hallway Lockdown"; - pixel_x = 7; - pixel_y = -34; - req_access = list("ACCESS_MEDICAL") - }, -/obj/machinery/button/blast_door{ - id_tag = "infirm_windows"; - name = "Infirmary Windows Lockdown"; - pixel_x = -7; - pixel_y = -34; +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/glass/medical{ + autoset_access = 0; + id_tag = "inf_recep"; + name = "Infirmary Reception"; req_access = list("ACCESS_MEDICAL") }, -/obj/machinery/button/blast_door{ +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 4; + icon_state = "shutter0"; id_tag = "inf_recep_window"; - name = "Infirmary Reception Lockdown"; - pixel_x = 7; - pixel_y = -24; - req_access = list("ACCESS_MEDICAL") - }, -/obj/machinery/button/blast_door{ - id_tag = "infirm_stage"; - name = "Infirmary Staging Lockdown"; - pixel_x = -7; - pixel_y = -24; - req_access = list("ACCESS_MEDICAL") + name = "Infirmary Reception Window Lockdown"; + opacity = 0 }, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "jDX" = ( /obj/machinery/atmospherics/pipe/manifold/visible/fuel{ @@ -21070,16 +21166,14 @@ d2 = 8; icon_state = "2-8" }, +/obj/effect/floor_decal/corner/white/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery2) "jYZ" = ( -/obj/effect/floor_decal/corner/paleblue/half{ +/obj/effect/floor_decal/corner/blue/half{ dir = 1; icon_state = "bordercolorhalf" }, -/obj/structure/extinguisher_cabinet{ - pixel_y = 32 - }, /turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/aft) "jZb" = ( @@ -21098,6 +21192,7 @@ /obj/effect/floor_decal/industrial/outline/grey, /obj/random/medical, /obj/random/medical, +/obj/random/medical/lite, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) "kbb" = ( @@ -21118,11 +21213,6 @@ dir = 4; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" - }, -/obj/effect/floor_decal/sign/or1, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -21134,6 +21224,11 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 9 + }, +/obj/effect/floor_decal/sign/or1, /turf/simulated/floor/tiled/white, /area/medical/surgery) "kdb" = ( @@ -21341,7 +21436,7 @@ dir = 2; pixel_y = 24 }, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled, @@ -21522,6 +21617,29 @@ }, /turf/simulated/floor/tiled/dark, /area/security/storage) +"kCZ" = ( +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/catwalk_plated, +/obj/structure/cable/green{ + d1 = 1; + d2 = 8; + icon_state = "1-8" + }, +/turf/simulated/floor/plating, +/area/hallway/primary/firstdeck/aft) "kDb" = ( /obj/effect/floor_decal/industrial/warning{ dir = 2; @@ -21547,9 +21665,6 @@ pixel_y = -24; pixel_z = 0 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/button/alternate/door{ id_tag = "examdoor"; name = "Examination Room Door Control"; @@ -21557,6 +21672,10 @@ pixel_y = -24; req_access = list("ACCESS_MEDICAL") }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "kGb" = ( @@ -21572,17 +21691,16 @@ /turf/simulated/floor/tiled/white, /area/security/brig) "kGX" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/firealarm{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) "kHb" = ( /obj/structure/bed/padded, -/obj/effect/floor_decal/corner/pink/mono, /obj/machinery/alarm{ dir = 4; pixel_x = -23; @@ -21591,21 +21709,27 @@ /obj/structure/closet/hydrant{ pixel_y = 32 }, +/obj/item/bedsheet/medical, +/obj/effect/floor_decal/corner/lime/mono, +/obj/effect/floor_decal/spline/plain{ + dir = 10; + icon_state = "spline_plain" + }, /turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "kIb" = ( /obj/structure/closet/secure_closet/personal/patient, -/obj/effect/floor_decal/corner/pink/mono, /obj/machinery/body_scan_display{ pixel_y = 24 }, /obj/random/plushie, +/obj/effect/floor_decal/corner/lime/mono, +/obj/effect/floor_decal/spline/plain, /turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "kJb" = ( /obj/structure/bed/padded, /obj/item/bedsheet/medical, -/obj/effect/floor_decal/corner/pink/mono, /obj/machinery/power/apc{ dir = 1; name = "north bump"; @@ -21616,6 +21740,11 @@ d2 = 2; icon_state = "0-2" }, +/obj/effect/floor_decal/corner/lime/mono, +/obj/effect/floor_decal/spline/plain{ + dir = 6; + icon_state = "spline_plain" + }, /turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "kKb" = ( @@ -21625,14 +21754,15 @@ /turf/simulated/floor/plating, /area/medical/staging) "kLb" = ( -/obj/machinery/bodyscanner{ - dir = 1; - icon_state = "body_scanner_0" - }, -/obj/effect/floor_decal/corner/pink/mono, /obj/machinery/light{ dir = 1 }, +/obj/machinery/bodyscanner{ + icon_state = "body_scanner_0"; + dir = 8 + }, +/obj/effect/floor_decal/corner/b_green/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "kLY" = ( @@ -21646,6 +21776,16 @@ }, /turf/simulated/floor/tiled/white, /area/assembly/robotics/laboratory) +"kNi" = ( +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloorwhite, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "kOb" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 @@ -21688,10 +21828,6 @@ /turf/simulated/wall/r_wall/hull, /area/thruster/d1port) "kQb" = ( -/obj/effect/floor_decal/corner/pink{ - dir = 5 - }, -/obj/effect/floor_decal/sign/pop, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -21703,12 +21839,14 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/lime{ + icon_state = "corner_white"; + dir = 5 + }, +/obj/effect/floor_decal/sign/pop, /turf/simulated/floor/tiled/white, /area/medical/staging) "kRb" = ( -/obj/effect/floor_decal/corner/pink{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -21725,13 +21863,14 @@ d2 = 4; icon_state = "1-4" }, +/obj/effect/floor_decal/corner/lime{ + icon_state = "corner_white"; + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "kSb" = ( /obj/structure/curtain/open/privacy, -/obj/effect/floor_decal/corner/pink{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -21743,6 +21882,14 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 4 + }, +/obj/effect/floor_decal/corner/lime{ + icon_state = "corner_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "kSc" = ( @@ -21770,7 +21917,6 @@ /turf/simulated/floor/tiled/dark/monotile, /area/security/armoury) "kTb" = ( -/obj/effect/floor_decal/corner/pink/mono, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, @@ -21782,21 +21928,24 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/body_scanconsole{ - dir = 1 +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 5 }, -/turf/simulated/floor/tiled/white/monotile, +/obj/effect/floor_decal/sign/d, +/turf/simulated/floor/tiled/white, /area/medical/staging) "kTl" = ( /obj/structure/table/rack, /obj/item/defibrillator/compact/loaded, /obj/item/auto_cpr, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/item/device/radio/intercom/department/medbay{ dir = 8; pixel_x = 24 }, +/obj/item/auto_cpr, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "kUb" = ( @@ -22005,12 +22154,16 @@ /turf/simulated/floor/tiled/white, /area/security/detectives_office) "lhb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/structure/sign/emergonly{ pixel_y = 26 }, +/obj/effect/floor_decal/industrial/firstaid/corner{ + icon_state = "stripecorner"; + dir = 4 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 1 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/center) "lib" = ( @@ -22120,6 +22273,10 @@ }, /turf/simulated/floor/reinforced/hydrogen, /area/thruster/d1port) +"loS" = ( +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/exam_room) "lpb" = ( /obj/structure/disposalpipe/segment, /obj/structure/cable/green{ @@ -22159,9 +22316,8 @@ /turf/simulated/floor/tiled/dark, /area/command/armoury) "lsT" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 1; - icon_state = "corner_white" +/obj/effect/floor_decal/corner/blue{ + dir = 1 }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) @@ -22354,18 +22510,15 @@ name = "north bump"; pixel_y = 24 }, +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/hatch/yellow, /obj/structure/cable/green{ d2 = 4; icon_state = "0-4" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/structure/table/standard, -/turf/simulated/floor/tiled/white, +/turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "lAC" = ( /obj/structure/table/standard, @@ -22417,6 +22570,17 @@ /obj/structure/catwalk, /turf/simulated/floor/plating, /area/engineering/hardstorage/aux) +"lCo" = ( +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/industrial/firstaid, +/turf/simulated/floor/tiled/white, +/area/medical/staging) "lCN" = ( /obj/structure/table/standard, /obj/item/device/tape/random, @@ -22424,10 +22588,10 @@ /turf/simulated/floor/tiled/dark, /area/security/evidence) "lEb" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/machinery/atmospherics/unary/vent_pump/on, +/obj/effect/floor_decal/corner/blue{ dir = 5 }, -/obj/machinery/atmospherics/unary/vent_pump/on, /turf/simulated/floor/tiled/white, /area/medical/equipstorage) "lGw" = ( @@ -22567,14 +22731,15 @@ dir = 10; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 4 - }, /obj/machinery/organ_printer/robot/mapped, /obj/machinery/firealarm{ dir = 8; pixel_x = -24 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "mbM" = ( @@ -22750,14 +22915,15 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/light{ dir = 4 }, /obj/structure/bed/chair/comfy/blue, /obj/random_multi/single_item/runtime, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/medpaperworkoffice) "mlw" = ( @@ -22782,12 +22948,6 @@ /turf/simulated/floor/tiled/white/monotile, /area/rnd/office) "mmb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 8 @@ -22798,8 +22958,19 @@ pixel_x = -24 }, /obj/structure/cable/green, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/medpaperworkoffice) +"mmA" = ( +/obj/machinery/vending/medical/torch{ + dir = 1 + }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/machinery/light/spot, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/sleeper) "mmP" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, /obj/effect/floor_decal/corner/research{ @@ -22838,11 +23009,11 @@ /area/command/armoury/tactical) "mrW" = ( /obj/structure/closet/secure_closet/medical_torchsenior, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/alarm{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/locker) "mrX" = ( @@ -22914,9 +23085,6 @@ /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/center) "mvE" = ( -/obj/machinery/light/spot{ - dir = 4 - }, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, @@ -23019,10 +23187,6 @@ dir = 6; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 1; - icon_state = "corner_white" - }, /obj/effect/floor_decal/sign/or2, /obj/machinery/button/holosign{ id_tag = "or2"; @@ -23049,18 +23213,21 @@ c_tag = "Infirmary - Operating Theatre 2"; dir = 1 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "mzb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, /obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/item/device/radio/beacon, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 4 }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) @@ -23202,10 +23369,11 @@ /area/command/armoury) "mJY" = ( /obj/structure/closet/wardrobe/chemistry_white, -/obj/effect/floor_decal/corner/beige/mono, /obj/item/device/radio/intercom{ pixel_y = 23 }, +/obj/effect/floor_decal/corner/yellow/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "mKb" = ( @@ -23523,7 +23691,8 @@ dir = 5; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; dir = 8 }, /turf/simulated/floor/tiled/white, @@ -23805,6 +23974,28 @@ /obj/effect/floor_decal/industrial/outline/orange, /turf/simulated/floor/tiled/techfloor, /area/thruster/d1starboard) +"nzg" = ( +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/multi_tile/glass/medical{ + dir = 4; + id_tag = "ETC_hall"; + name = "Treatment Center" + }, +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 4; + icon_state = "shutter0"; + id_tag = "infirm_pub"; + name = "Infirmary Public Hallway Lockdown"; + opacity = 0 + }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/sleeper) "nAb" = ( /obj/effect/floor_decal/corner/red{ dir = 9; @@ -23829,6 +24020,19 @@ }, /turf/simulated/floor/tiled/dark, /area/security/opscheck) +"nAm" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 4 + }, +/obj/machinery/pager/medical{ + pixel_x = -21; + pixel_y = -10 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "nAp" = ( /obj/structure/table/rack, /obj/random/maintenance/solgov, @@ -23853,7 +24057,6 @@ /turf/simulated/wall/prepainted, /area/medical/exam_room) "nDd" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 }, @@ -23861,6 +24064,8 @@ pixel_y = 24 }, /obj/machinery/photocopier, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "nDx" = ( @@ -23883,7 +24088,8 @@ d2 = 8; icon_state = "4-8" }, -/obj/effect/floor_decal/industrial/hatch/blue, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, /turf/simulated/floor/tiled/white/monotile, /area/medical/surgery2) "nDB" = ( @@ -23995,11 +24201,6 @@ /turf/simulated/floor/tiled, /area/rnd/entry) "nLo" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/item/storage/pill_bottle/tramadol, -/obj/item/storage/pill_bottle/antitox, /obj/machinery/alarm{ dir = 8; icon_state = "alarm0"; @@ -24010,8 +24211,15 @@ pixel_x = 0; pixel_y = 32 }, -/obj/item/storage/pill_bottle, -/obj/item/storage/pill_bottle, +/obj/random/medical, +/obj/random/medical, +/obj/random/medical, +/obj/random/medical, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/random/medical/lite, +/obj/random/medical/lite, /turf/simulated/floor/tiled/white, /area/medical/equipstorage) "nOO" = ( @@ -24373,29 +24581,6 @@ }, /turf/simulated/floor/tiled/white, /area/rnd/research) -"ofb" = ( -/obj/structure/table/reinforced, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/machinery/door/blast/shutters{ - density = 0; - dir = 4; - icon_state = "shutter0"; - id_tag = "inf_recep_window"; - name = "Infirmary Reception Window Lockdown"; - opacity = 0 - }, -/obj/machinery/door/window/eastright{ - base_state = "left"; - dir = 4; - icon_state = "left"; - name = "Medical Reception" - }, -/obj/machinery/door/firedoor, -/obj/item/modular_computer/telescreen/preset/medical{ - pixel_y = 32 - }, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/staging) "ogb" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -24458,7 +24643,7 @@ icon_state = "4-8" }, /obj/structure/bed/chair/padded/blue, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled/white, @@ -24553,32 +24738,13 @@ /obj/structure/catwalk, /turf/simulated/floor/plating, /area/maintenance/firstdeck/foreport) -"ovb" = ( -/obj/effect/floor_decal/industrial/hatch/blue, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/glass/medical{ - autoset_access = 0; - id_tag = "inf_recep"; - name = "Infirmary Reception"; - req_access = list("ACCESS_MEDICAL") - }, -/obj/machinery/door/blast/shutters{ - density = 0; - dir = 4; - icon_state = "shutter0"; - id_tag = "inf_recep_window"; - name = "Infirmary Reception Window Lockdown"; - opacity = 0 - }, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/staging) "ovZ" = ( /obj/structure/closet/secure_closet/medical_torchsenior, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/sign/warning/nosmoking_1{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/locker) "owb" = ( @@ -24600,11 +24766,11 @@ /turf/simulated/floor/tiled/dark/monotile, /area/medical/morgue/autopsy) "oxb" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/structure/table/standard, +/obj/structure/flora/pottedplant/flower, +/obj/effect/floor_decal/corner/blue{ + dir = 6 }, -/obj/structure/disposalpipe/segment, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "oyb" = ( @@ -24688,11 +24854,16 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/crew_quarters/safe_room/firstdeck) "oCf" = ( -/obj/effect/floor_decal/corner/pink{ - dir = 9 +/obj/machinery/body_scanconsole{ + icon_state = "body_scannerconsole"; + dir = 8 }, -/obj/effect/floor_decal/sign/d, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/b_green/mono, +/obj/effect/floor_decal/industrial/firstaid/corner{ + icon_state = "stripecorner"; + dir = 4 + }, +/turf/simulated/floor/tiled/white/monotile, /area/medical/staging) "oDg" = ( /turf/simulated/wall/prepainted, @@ -24754,13 +24925,6 @@ /turf/simulated/floor/tiled/dark, /area/security/armoury) "oLK" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/item/device/radio/intercom/department/medbay{ - dir = 1; - pixel_y = -28 - }, /turf/simulated/floor/tiled/white, /area/medical/staging) "oMb" = ( @@ -24788,10 +24952,6 @@ dir = 8; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 6; - icon_state = "corner_white" - }, /obj/structure/hygiene/sink{ dir = 8; icon_state = "sink"; @@ -24805,6 +24965,10 @@ dir = 4; pixel_x = -24 }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 6 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "oMq" = ( @@ -24843,9 +25007,6 @@ /area/security/opscheck) "oOt" = ( /obj/effect/floor_decal/floordetail/edgedrain, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 }, @@ -24858,6 +25019,10 @@ dir = 5 }, /obj/structure/closet/crate/secure/biohazard/alt, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "oPb" = ( @@ -24911,6 +25076,10 @@ }, /turf/simulated/floor/tiled/white, /area/rnd/locker) +"oRI" = ( +/obj/effect/floor_decal/plaque, +/turf/simulated/floor/tiled/white, +/area/medical/exam_room) "oSb" = ( /obj/effect/catwalk_plated, /obj/structure/cable/green{ @@ -25059,7 +25228,8 @@ /turf/simulated/floor/tiled/dark/monotile, /area/security/armoury) "oZb" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; dir = 10 }, /turf/simulated/floor/tiled/white, @@ -25209,10 +25379,6 @@ /turf/simulated/wall/r_wall/prepainted, /area/security/detectives_office) "pjV" = ( -/obj/effect/floor_decal/corner/pink{ - dir = 1; - icon_state = "corner_white" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -25224,6 +25390,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "pkb" = ( @@ -25246,12 +25416,12 @@ /turf/simulated/floor/tiled/white, /area/rnd/research) "pmz" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) "pnb" = ( @@ -25457,17 +25627,20 @@ /turf/simulated/floor/tiled, /area/security/detectives_office) "pzZ" = ( -/obj/effect/floor_decal/industrial/hatch/blue, -/obj/structure/disposalpipe/segment, /obj/machinery/door/blast/shutters{ density = 0; dir = 2; icon_state = "shutter0"; - id_tag = "infirm_pub"; - name = "Infirmary Public Hallway Lockdown"; + id_tag = "infirm_windows"; + name = "Infirmary Window Lockdown"; opacity = 0 }, -/turf/simulated/floor/tiled/white, +/obj/effect/wallframe_spawn/reinforced, +/obj/structure/disposalpipe/segment{ + dir = 2; + icon_state = "pipe-c" + }, +/turf/simulated/floor/plating, /area/medical/medicalhallway) "pAb" = ( /obj/machinery/newscaster{ @@ -25534,9 +25707,6 @@ /turf/simulated/floor/tiled/dark, /area/rnd/misc_lab) "pEr" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -25622,9 +25792,9 @@ dir = 4; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 9; - icon_state = "corner_white" +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 9 }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) @@ -25765,6 +25935,15 @@ /obj/machinery/atmospherics/pipe/simple/visible/red, /turf/simulated/floor/tiled/white, /area/rnd/misc_lab) +"pTB" = ( +/obj/structure/closet/secure_closet/personal/patient, +/obj/effect/floor_decal/corner/lime/mono, +/obj/effect/floor_decal/spline/plain{ + dir = 9; + icon_state = "spline_plain" + }, +/turf/simulated/floor/tiled/white/monotile, +/area/medical/exam_room) "pUb" = ( /obj/structure/closet/toolcloset, /obj/effect/floor_decal/industrial/outline/grey, @@ -25807,12 +25986,12 @@ }, /obj/item/storage/firstaid/fire, /obj/random/medical/lite, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/firealarm{ dir = 8; pixel_x = -24 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "pWW" = ( @@ -25980,12 +26159,6 @@ /turf/simulated/floor/tiled/white, /area/rnd/xenobiology/xenoflora) "qfa" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -26059,9 +26232,6 @@ /area/rnd/misc_lab) "qju" = ( /obj/effect/floor_decal/floordetail/edgedrain, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/light, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -26074,6 +26244,10 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "qkb" = ( @@ -26121,14 +26295,15 @@ /area/medical/surgery2) "qnA" = ( /obj/effect/floor_decal/floordetail/edgedrain, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/light, /obj/structure/closet/crate/freezer, /obj/item/device/mmi, /obj/item/reagent_containers/ivbag/nanoblood, /obj/item/reagent_containers/ivbag/nanoblood, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "qqb" = ( @@ -26145,10 +26320,37 @@ /turf/simulated/floor/tiled/white, /area/rnd/research) "qrq" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/machinery/button/blast_door{ + id_tag = "infirm_pub"; + name = "Infirmary Public Hallway Lockdown"; + pixel_x = 7; + pixel_y = -34; + req_access = list("ACCESS_MEDICAL") + }, +/obj/machinery/button/blast_door{ + id_tag = "infirm_windows"; + name = "Infirmary Windows Lockdown"; + pixel_x = -7; + pixel_y = -34; + req_access = list("ACCESS_MEDICAL") + }, +/obj/machinery/button/blast_door{ + id_tag = "inf_recep_window"; + name = "Infirmary Reception Lockdown"; + pixel_x = 7; + pixel_y = -24; + req_access = list("ACCESS_MEDICAL") + }, +/obj/machinery/button/blast_door{ + id_tag = "infirm_stage"; + name = "Infirmary Staging Lockdown"; + pixel_x = -7; + pixel_y = -24; + req_access = list("ACCESS_MEDICAL") + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 }, -/obj/machinery/hologram/holopad, /turf/simulated/floor/tiled/white, /area/medical/staging) "qsO" = ( @@ -26416,12 +26618,6 @@ /turf/simulated/floor/tiled/white, /area/rnd/xenobiology/xenoflora) "qGx" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/structure/cable/green{ d1 = 4; d2 = 8; @@ -26434,12 +26630,16 @@ /obj/structure/sign/warning/nosmoking_1{ pixel_y = 24 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "qGE" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /turf/simulated/floor/tiled/white, /area/medical/equipstorage) "qHb" = ( @@ -26486,12 +26686,13 @@ /turf/simulated/floor/tiled/white/monotile, /area/rnd/xenobiology/xenoflora) "qKx" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, /turf/simulated/floor/tiled/white, /area/medical/locker) "qKH" = ( @@ -26548,14 +26749,13 @@ /turf/simulated/floor/tiled/freezer, /area/rnd/xenobiology/xenoflora) "qLr" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/structure/table/standard, /obj/item/reagent_containers/food/snacks/lunacake/mooncake, /obj/machinery/alarm{ dir = 1; pixel_y = -22 }, -/turf/simulated/floor/tiled/white/monotile, +/turf/simulated/floor/tiled/white, /area/medical/medicalhallway) "qMb" = ( /obj/machinery/portable_atmospherics/hydroponics{ @@ -26589,6 +26789,22 @@ /obj/machinery/meter, /turf/simulated/floor/tiled/white, /area/rnd/misc_lab) +"qPj" = ( +/obj/machinery/power/apc{ + dir = 1; + name = "north bump"; + pixel_x = 0; + pixel_y = 24 + }, +/obj/structure/cable/green{ + d2 = 2; + icon_state = "0-2" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/turf/simulated/floor/tiled, +/area/hallway/primary/firstdeck/aft) "qQb" = ( /obj/machinery/atmospherics/valve{ dir = 8; @@ -27037,7 +27253,8 @@ dir = 5; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; dir = 8 }, /turf/simulated/floor/tiled/white, @@ -27550,16 +27767,10 @@ /turf/simulated/floor/tiled/white, /area/rnd/research) "rPz" = ( -/obj/item/roller{ - pixel_y = 8 - }, -/obj/item/roller{ - pixel_y = 16 +/obj/effect/floor_decal/corner/blue{ + dir = 9 }, -/obj/item/roller, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/structure/table/reinforced, -/turf/simulated/floor/tiled/white/monotile, +/turf/simulated/floor/tiled/white, /area/medical/staging) "rQb" = ( /obj/structure/ladder, @@ -27799,6 +28010,7 @@ /obj/random/medical, /obj/effect/floor_decal/industrial/outline/grey, /obj/random/medical, +/obj/random/medical/lite, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) "scr" = ( @@ -27932,17 +28144,34 @@ /turf/simulated/wall/r_wall/hull, /area/security/locker) "siI" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/machinery/button/alternate/door{ + id_tag = "inf_stage"; + name = "Infirmary Staging Exit"; + pixel_x = -7; + pixel_y = 23 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 +/obj/machinery/button/alternate/door{ + id_tag = "ETC_stage"; + name = "Treatment to Staging"; + pixel_x = -7; + pixel_y = 33 + }, +/obj/machinery/button/alternate/door{ + id_tag = "ETC_hall"; + name = "Treatment Center Exit"; + pixel_x = 8; + pixel_y = 33 + }, +/obj/machinery/button/alternate/door{ + id_tag = "inf_recep"; + name = "Reception Doors"; + pixel_x = 8; + pixel_y = 23 + }, +/obj/structure/bed/chair/comfy/blue, +/obj/effect/floor_decal/corner/blue{ + dir = 6 }, -/obj/structure/table/standard, -/obj/item/defibrillator/loaded, -/obj/item/bodybag/rescue/loaded, -/obj/item/auto_cpr, -/obj/item/auto_cpr, /turf/simulated/floor/tiled/white, /area/medical/staging) "sjb" = ( @@ -28157,15 +28386,12 @@ /turf/simulated/floor/tiled/white, /area/rnd/research) "ssg" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/effect/floor_decal/corner/blue{ + dir = 4 }, -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/structure/disposalpipe/segment{ + dir = 2; + icon_state = "pipe-c" }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) @@ -28454,12 +28680,14 @@ dir = 1; pixel_y = -28 }, +/obj/structure/bed/chair/comfy/blue{ + icon_state = "comfychair_preview"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/staging) "sHb" = ( /obj/structure/closet/secure_closet/medical_torchsenior, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/power/apc{ dir = 8; name = "west bump"; @@ -28472,6 +28700,8 @@ d2 = 4; icon_state = "0-4" }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/locker) "sHN" = ( @@ -28497,9 +28727,20 @@ /area/maintenance/firstdeck/aftport) "sLb" = ( /obj/machinery/atmospherics/unary/cryo_cell, -/obj/effect/floor_decal/corner/paleblue/mono, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) +"sLS" = ( +/obj/structure/bed/chair/padded/blue{ + dir = 8; + icon_state = "chair_preview" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "sMf" = ( /obj/effect/floor_decal/corner/red/diagonal, /obj/machinery/alarm{ @@ -28586,6 +28827,23 @@ /obj/structure/catwalk, /turf/simulated/floor/plating, /area/maintenance/firstdeck/aftport) +"sPa" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/cable/green{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 + }, +/turf/simulated/floor/tiled/white, +/area/medical/medicalhallway) "sPb" = ( /obj/machinery/embedded_controller/radio/airlock/access_controller{ id_tag = "xeno_airlock_control"; @@ -28743,15 +29001,31 @@ }, /turf/simulated/floor/tiled/white, /area/rnd/xenobiology) -"sWP" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +"sWJ" = ( +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 2; + icon_state = "shutter0"; + id_tag = "infirm_windows"; + name = "Infirmary Window Lockdown"; + opacity = 0 }, +/obj/effect/wallframe_spawn/reinforced, +/turf/simulated/floor/plating, +/area/medical/medicalhallway) +"sWP" = ( /obj/structure/bed/chair/wheelchair, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/closet/shipping_wall/filled{ pixel_y = 32 }, +/obj/effect/floor_decal/corner/blue{ + icon_state = "corner_white"; + dir = 10 + }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white, /area/medical/foyer) "sXb" = ( @@ -28905,15 +29179,6 @@ }, /turf/simulated/floor/tiled/white, /area/rnd/xenobiology) -"tgg" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 6 - }, -/obj/item/modular_computer/telescreen/preset/medical{ - pixel_x = 32 - }, -/turf/simulated/floor/tiled/white, -/area/medical/medicalhallway) "tgs" = ( /obj/machinery/door/airlock/security{ name = "Brig Chief" @@ -29051,9 +29316,6 @@ /turf/simulated/floor/tiled/white/monotile, /area/rnd/xenobiology) "tmc" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/green{ @@ -29264,9 +29526,6 @@ dir = 10; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 4 - }, /obj/machinery/firealarm{ dir = 8; pixel_x = -24 @@ -29275,15 +29534,13 @@ /obj/item/device/mmi, /obj/item/reagent_containers/ivbag/nanoblood, /obj/item/reagent_containers/ivbag/nanoblood, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery2) "tCO" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/item/auto_cpr, /obj/item/bodybag/rescue/loaded, /obj/item/reagent_containers/glass/beaker/cryoxadone{ @@ -29298,7 +29555,8 @@ pixel_y = 32 }, /obj/structure/table/standard, -/turf/simulated/floor/tiled/white, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "tEs" = ( /obj/structure/filingcabinet/chestdrawer{ @@ -29435,6 +29693,10 @@ }, /turf/simulated/floor/tiled/dark, /area/security/evidence) +"tVf" = ( +/obj/effect/floor_decal/industrial/firstaid/corner, +/turf/simulated/floor/tiled/white, +/area/medical/sleeper) "tVi" = ( /obj/structure/sign/warning/hot_exhaust{ dir = 8; @@ -29591,21 +29853,22 @@ /obj/item/device/radio/intercom{ pixel_y = 23 }, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/center) "umT" = ( -/obj/effect/floor_decal/spline/plain/blue{ - dir = 4; - icon_state = "spline_plain" - }, /obj/structure/bed/roller, /obj/item/device/radio/intercom/department/medbay{ dir = 4; pixel_x = -24 }, +/obj/structure/disposalpipe/segment, +/obj/effect/floor_decal/spline/plain/blue{ + dir = 4; + icon_state = "spline_plain" + }, /turf/simulated/floor/tiled/white, /area/medical/sleeper) "uoC" = ( @@ -29638,18 +29901,16 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/aftstarboard) "uuS" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 +/obj/effect/floor_decal/corner/blue/three_quarters{ + dir = 4 }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 +/obj/item/device/radio/intercom/department/medbay{ + dir = 1; + pixel_y = -28 }, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "uvj" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 10 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -29759,12 +30020,22 @@ /turf/simulated/floor/plating, /area/maintenance/firstdeck/foreport) "uKs" = ( -/obj/machinery/atmospherics/unary/vent_pump/on{ +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/pager/medical{ - pixel_x = -23; - pixel_y = -10 +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/cable/green{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/obj/effect/floor_decal/corner/blue{ + dir = 6 + }, +/obj/effect/floor_decal/borderfloorwhite{ + icon_state = "borderfloor_white"; + dir = 8 }, /turf/simulated/floor/tiled/white, /area/medical/medicalhallway) @@ -29859,25 +30130,27 @@ /turf/simulated/floor/tiled/monotile, /area/security/brig) "uUK" = ( -/obj/structure/table/rack{ - dir = 8 +/obj/structure/closet/secure_closet/chemical, +/obj/item/storage/box/pillbottles, +/obj/item/device/radio/headset/headset_med, +/obj/item/book/manual/chemistry_recipes, +/obj/item/clothing/glasses/science, +/obj/machinery/button/blast_door{ + id_tag = "chemcounter"; + name = "Chemistry Counter Lockdown Control"; + pixel_x = -6; + pixel_y = -24 }, -/obj/item/device/scanner/spectrometer, -/obj/item/storage/box/freezer, -/obj/item/storage/box/beakers/insulated, -/obj/item/reagent_containers/dropper, -/obj/item/storage/box/beakers, -/obj/item/hand_labeler, -/obj/item/stack/package_wrap/twenty_five, -/obj/item/reagent_containers/spray/cleaner{ - desc = "Someone has crossed out the 'Space' from Space Cleaner and written in Chemistry. Scrawled on the back is, 'Okay, whoever filled this with polytrinic acid, it was only funny the first time. It was hard enough replacing the CMO's first cat!'"; - name = "Chemistry Cleaner" +/obj/machinery/camera/network/medbay{ + c_tag = "Infirmary - Chemistry"; + dir = 8 }, -/obj/effect/floor_decal/corner/beige/mono, -/obj/item/device/radio/intercom/department/medbay{ - dir = 8; - pixel_x = 24 +/obj/structure/extinguisher_cabinet{ + pixel_x = 32 }, +/obj/effect/floor_decal/corner/yellow/mono, +/obj/effect/floor_decal/industrial/outline, +/obj/item/screwdriver, /turf/simulated/floor/tiled/white/monotile, /area/medical/chemistry) "uVf" = ( @@ -30027,7 +30300,7 @@ /area/command/conference) "vii" = ( /obj/structure/closet/secure_closet/medical_torch, -/obj/effect/floor_decal/corner/paleblue/mono, +/obj/effect/floor_decal/corner/blue/mono, /obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/locker) @@ -30074,7 +30347,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury) "vnC" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/machinery/power/apc{ dir = 1; name = "north bump"; @@ -30088,6 +30360,7 @@ /obj/structure/table/standard, /obj/item/device/flashlight/pen, /obj/item/clothing/accessory/stethoscope, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "vpA" = ( @@ -30177,7 +30450,7 @@ /turf/simulated/floor/tiled/white, /area/security/detectives_office) "vEa" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled/white, @@ -30217,14 +30490,12 @@ /area/thruster/d1port) "vHB" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) "vIn" = ( -/obj/effect/floor_decal/industrial/hatch/blue, -/obj/structure/disposalpipe/segment, /obj/machinery/door/blast/shutters{ density = 0; dir = 2; @@ -30233,13 +30504,13 @@ name = "Infirmary Public Hallway Lockdown"; opacity = 0 }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/firstaid/fulltile, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" }, -/turf/simulated/floor/tiled/white, +/turf/simulated/floor/tiled/white/monotile, /area/medical/medicalhallway) "vIP" = ( /obj/structure/filingcabinet/chestdrawer, @@ -30337,9 +30608,6 @@ /turf/simulated/floor/reinforced/hydrogen, /area/thruster/d1starboard) "vZI" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/unary/vent_scrubber/on, /obj/machinery/camera/network/medbay{ c_tag = "Infirmary - Equipment Storage"; @@ -30354,18 +30622,21 @@ d2 = 2; icon_state = "1-2" }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/equipstorage) "vZK" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/light{ dir = 8 }, /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 }, +/obj/effect/floor_decal/corner/blue{ + dir = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/locker) "wcQ" = ( @@ -30377,6 +30648,18 @@ }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/fore) +"wdq" = ( +/obj/machinery/door/blast/shutters{ + density = 0; + dir = 2; + icon_state = "shutter0"; + id_tag = "infirm_windows"; + name = "Infirmary Window Lockdown"; + opacity = 0 + }, +/obj/effect/wallframe_spawn/reinforced, +/turf/simulated/floor/plating, +/area/medical/medpaperworkoffice) "whd" = ( /obj/effect/floor_decal/corner/blue{ dir = 6 @@ -30460,11 +30743,11 @@ /obj/item/storage/box/rxglasses, /obj/item/clothing/accessory/stethoscope, /obj/item/device/flashlight/pen, -/obj/effect/floor_decal/corner/paleblue/mono, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/white/monotile, /area/medical/equipstorage) "wmK" = ( @@ -30792,10 +31075,6 @@ dir = 6; icon_state = "edge" }, -/obj/effect/floor_decal/corner/paleblue{ - dir = 1; - icon_state = "corner_white" - }, /obj/machinery/button/windowtint{ id = "or1"; pixel_x = -6; @@ -30811,6 +31090,10 @@ dir = 1 }, /obj/structure/closet/crate/secure/biohazard/alt, +/obj/effect/floor_decal/corner/b_green{ + icon_state = "corner_white"; + dir = 1 + }, /turf/simulated/floor/tiled/white, /area/medical/surgery) "xxB" = ( @@ -30855,7 +31138,6 @@ /turf/simulated/floor/tiled, /area/security/wing) "xzN" = ( -/obj/effect/floor_decal/corner/paleblue/mono, /obj/item/flame/candle/scented/incense{ desc = "An incense cone. It produces fragrant smoke when burned. This one is branded 'IQ-incense, for the smart and sensual.' " }, @@ -30865,6 +31147,7 @@ }, /obj/structure/table/glass, /obj/random_multi/single_item/memo_medical, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, /area/medical/medpaperworkoffice) "xBk" = ( @@ -30946,14 +31229,6 @@ /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) -"xKz" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/glass/civilian, -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, -/turf/simulated/floor/tiled/steel_ridged, -/area/hallway/primary/firstdeck/center) "xKY" = ( /obj/effect/floor_decal/techfloor{ dir = 8; @@ -30981,6 +31256,9 @@ /area/maintenance/firstdeck/centralport) "xMy" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/effect/floor_decal/corner/blue{ + dir = 9 + }, /turf/simulated/floor/tiled/white, /area/medical/exam_room) "xMP" = ( @@ -31034,6 +31312,10 @@ /obj/structure/sign/emergonly{ pixel_y = 26 }, +/obj/effect/floor_decal/industrial/firstaid/corner{ + icon_state = "stripecorner"; + dir = 1 + }, /turf/simulated/floor/tiled/monotile, /area/hallway/primary/firstdeck/center) "xTc" = ( @@ -31057,7 +31339,8 @@ /obj/machinery/sleeper{ dir = 4 }, -/obj/effect/floor_decal/corner/paleblue/mono, +/obj/effect/floor_decal/corner/blue/mono, +/obj/effect/floor_decal/industrial/outline, /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "xTT" = ( @@ -31096,7 +31379,7 @@ /turf/simulated/floor/tiled/white/monotile, /area/rnd/misc_lab) "ycz" = ( -/obj/effect/floor_decal/corner/paleblue{ +/obj/effect/floor_decal/corner/blue{ dir = 5 }, /turf/simulated/floor/tiled, @@ -31144,10 +31427,11 @@ }, /turf/simulated/floor/plating, /area/hallway/primary/firstdeck/aft) +"yhw" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/wall/prepainted, +/area/medical/chemistry) "yii" = ( -/obj/effect/floor_decal/corner/paleblue{ - dir = 5 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /turf/simulated/floor/tiled/white, @@ -49636,7 +49920,7 @@ kHb acy sGs leb -xKz +axa aym jsk aAK @@ -50640,7 +50924,7 @@ jcu umT glb bGZ -ezb +tVf gyV oCf pjV @@ -50841,14 +51125,14 @@ etb eRb anM ads -ewb -fEb +cqc +gQx aga agc ago -agc +lCo agZ -ycz +gaB heb ure auN @@ -51038,7 +51322,7 @@ cFb deb poS dMb -dZb +azu eTb eHb avO @@ -51050,7 +51334,7 @@ agi ags agI aha -ycz +gaB ayg ure auN @@ -51245,7 +51529,7 @@ ewb fEb fEb adB -dZb +kNi afd gyV agj @@ -51447,13 +51731,13 @@ exb ixf anL adH -dZb +kNi afB lHO -agj +gMF aGx agY -fMr +leb ivT gxX dDc @@ -51647,9 +51931,9 @@ sLb agB pPa xTh -fvb +xTh adL -dZb +kNi afD lHO afH @@ -51851,13 +52135,13 @@ pPa ezb fKb adL -dZb +kNi afK gyV agH -agx +cic oLK -leb +fMr ivT hgb flB @@ -52052,14 +52336,14 @@ ebb eAb eUb mvE -adL -dZb -afK -gyV +dpZ +kNi +mmA +lHO siI -aGx +bMp qrq -ahc +leb vHB ayB nWl @@ -52255,11 +52539,11 @@ aby abH acf adO -dZb +nzg afY lHO anH -agx +ieS jCU leb hdb @@ -52455,16 +52739,16 @@ abl abr abA abK -acf +yhw adQ afb -afZ -lHO -ofb -gzb -ovb -leb -dMe +agd +nAm +orb +orb +owb +sWJ +ivT hnN mpq htb @@ -52660,13 +52944,13 @@ abQ acO aed afe -agd +hyG uKs -orb -orb -owb +sPa +sPa +sPa gWb -ivT +igk hhb iVb hub @@ -52850,8 +53134,8 @@ mpy mpy uXe adw -sde -sde +uXe +uXe uXe scd acf @@ -52865,8 +53149,8 @@ aff agf fWb mzb -mzb -mzb +aGp +aGp vIn ssg bEQ @@ -53052,10 +53336,10 @@ mpy mpy acJ adw -sde uXe uXe -sde +uXe +uXe acf acf abu @@ -53068,7 +53352,7 @@ agh hEU ael oxb -oxb +sLS pzZ axw iTC @@ -53267,11 +53551,11 @@ acf aen afh aqL -dXM -tgg -gDb -gDb -gWb +nCb +nCb +gEb +gEb +nCb hdb jAM equ @@ -53456,9 +53740,9 @@ mpy mpy xTT adv -sde -sde -sde +uXe +uXe +uXe dEb dQb acf @@ -53467,14 +53751,14 @@ uUK acf acf aeo -afi +afg aqM -nCb -nCb -gEb -gEb -nCb -axy +nBb +fsX +loS +pTB +gXb +ivT hib oAp haT @@ -53666,14 +53950,14 @@ rAu rAu rAu rAu -acz +rAu adj aep -afh -fOb +afb +owb nBb ata -aua +loS avl gXb vHB @@ -53868,7 +54152,7 @@ cZb rAu ejb eIb -acz +rAu adl aeu afk @@ -53877,7 +54161,7 @@ fYb atb xMy gRb -gXb +hGo ivT hnN gAU @@ -54077,7 +54361,7 @@ afl aqP nBb iMW -twz +oRI oZb gXb ivT @@ -54272,10 +54556,10 @@ rAu rAu udY aOt -acz +rAu adn aeB -afm +afl qLr nCb omb @@ -55493,7 +55777,7 @@ nDd eoj gFb bLJ -xYM +wdq jYZ ugg jlb @@ -55695,9 +55979,9 @@ ipc eoj gFb xzN -xYM -axD -ayC +wdq +ivT +hnN job abk abk @@ -55898,8 +56182,8 @@ dSR mlb avu xYM -ivT -hnN +qPj +kCZ cgu ahd ahk From 74bab0e9f63b2398b6cce6f696c0a35c6579d6fb Mon Sep 17 00:00:00 2001 From: Sierra Brown Date: Tue, 18 May 2021 16:38:41 -0700 Subject: [PATCH 004/246] Update gitignore - Allow VSC's `settings.json` - Block `dmdoc/` and all `*.code-workspace` files --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 57fa28b45a8..51b8ab89874 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ Thumbs.db *.backup *.before data/ +dmdoc/ cfg/ build_log.txt use_map @@ -20,8 +21,10 @@ atupdate config/* sql/test_db -# vscode +# VisualStudioCode .vscode/* +!.vscode/settings.json +*.code-workspace .history # swap @@ -39,7 +42,6 @@ Session.vim # auto-generated tag files tags -baystation12.code-workspace # ignore built libs lib/*.dll From d01dbd9c2dd4c930339c560131037c5c3a214d3a Mon Sep 17 00:00:00 2001 From: Sierra Brown Date: Tue, 18 May 2021 16:38:51 -0700 Subject: [PATCH 005/246] Apply general/common VSC settings/configs --- .vscode/settings.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..36f0b567369 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.detectIndentation": false, + "editor.insertSpaces": false, + "editor.tabSize": 4, + "files.eol": "\r\n", + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true +} From 5d5be95aca060f2fb757ede44523af336d2a1900 Mon Sep 17 00:00:00 2001 From: The Spanish Inquisition Date: Wed, 19 May 2021 17:34:26 -0400 Subject: [PATCH 006/246] Adds New Guns and Ammo To Support Them Added two new guns and the ammo to support them, as well as spawning for Raiders and off torch sites=, being: - A Battle Rifle, Uses Military Rifle Mags, has both Semi and Automatic fire, though Automatic is highly inaccurate. - A Carbine Rifle, uses 15mm Pistol rounds, 10 round internal capacity, loaded with individual bullets or 5 round stripper clips. - Pistol Stripper clips - 5 rounds of 15mm pistol rounds on a stripper clip. --- code/game/antagonist/outsider/raider.dm | 3 +- code/game/objects/random/random.dm | 3 +- .../designs/general/designs_arms_ammo.dm | 8 ++- code/modules/item_worth/worths_list.dm | 2 + code/modules/projectiles/ammunition/boxes.dm | 14 +++- .../projectiles/guns/projectile/automatic.dm | 60 ++++++++++++++++++ icons/mob/onmob/items/lefthand_guns.dmi | Bin 71356 -> 71531 bytes icons/mob/onmob/items/righthand_guns.dmi | Bin 74148 -> 75154 bytes icons/mob/onmob/onmob_back.dmi | Bin 87727 -> 90058 bytes icons/obj/ammo.dmi | Bin 24887 -> 25097 bytes icons/obj/guns/battlerifle.dmi | Bin 0 -> 368 bytes icons/obj/guns/semistrip.dmi | Bin 0 -> 343 bytes 12 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 icons/obj/guns/battlerifle.dmi create mode 100644 icons/obj/guns/semistrip.dmi diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm index fc1e13edfa1..e58368a287e 100644 --- a/code/game/antagonist/outsider/raider.dm +++ b/code/game/antagonist/outsider/raider.dm @@ -314,7 +314,8 @@ GLOBAL_DATUM_INIT(raiders, /datum/antagonist/raider, new) /obj/item/gun/energy/sniperrifle, /obj/item/gun/projectile/shotgun/doublebarrel, /obj/item/gun/energy/xray, - /obj/item/gun/projectile/heavysniper/boltaction, + /obj/item/gun/projectile/automatic/battlerifle, + /obj/item/gun/projectile/automatic/semistrip, /obj/item/gun/projectile/automatic/assault_rifle, /obj/item/gun/projectile/automatic/sec_smg, /obj/item/gun/energy/crossbow/largecrossbow, diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index be5c76ce147..f042bab12a8 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -274,7 +274,8 @@ icon_state = "revolver" /obj/random/projectile/spawn_choices() - return list(/obj/item/gun/projectile/heavysniper/boltaction = 4, + return list(/obj/item/gun/projectile/automatic/battlerifle = 4, + /obj/item/gun/projectile/automatic/semistrip = 4, /obj/item/gun/projectile/shotgun/pump = 3, /obj/item/gun/projectile/automatic/merc_smg = 2, /obj/item/gun/projectile/automatic/assault_rifle = 2, diff --git a/code/modules/fabrication/designs/general/designs_arms_ammo.dm b/code/modules/fabrication/designs/general/designs_arms_ammo.dm index 61bc4eb158d..95700530fbc 100644 --- a/code/modules/fabrication/designs/general/designs_arms_ammo.dm +++ b/code/modules/fabrication/designs/general/designs_arms_ammo.dm @@ -116,5 +116,9 @@ path = /obj/item/magnetic_ammo/skrell/slug /datum/fabricator_recipe/arms_ammo/hidden/stripperclip - name = "ammunition (stripper clip)" - path = /obj/item/ammo_magazine/speedloader/clip \ No newline at end of file + name = "ammunition (rifle stripper clip)" + path = /obj/item/ammo_magazine/speedloader/clip + +/datum/fabricator_recipe/arms_ammo/hidden/pistolstripperclip + name = "ammunition (pistol stripper clip)" + path = /obj/item/ammo_magazine/speedloader/pclip \ No newline at end of file diff --git a/code/modules/item_worth/worths_list.dm b/code/modules/item_worth/worths_list.dm index b4933a2c6b9..d159e5a3b99 100644 --- a/code/modules/item_worth/worths_list.dm +++ b/code/modules/item_worth/worths_list.dm @@ -46,6 +46,8 @@ var/list/worths = list( /obj/item/gun/projectile/automatic/merc_smg = 3250, /obj/item/gun/projectile/automatic/assault_rifle = 3800, /obj/item/gun/projectile/automatic/bullpup_rifle = 3100, + /obj/item/gun/projectile/automatic/battlerifle = 1750, + /obj/item/gun/projectile/automatic/semistrip = 1250, /obj/item/gun/projectile/automatic/l6_saw = 13400, /obj/item/gun/projectile/automatic/machine_pistol/usi = 2000, /obj/item/gun/projectile/automatic = 3000, diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm index 113d2bc2cf3..f770be34584 100644 --- a/code/modules/projectiles/ammunition/boxes.dm +++ b/code/modules/projectiles/ammunition/boxes.dm @@ -29,9 +29,19 @@ max_ammo = 6 multiple_sprites = 1 +/obj/item/ammo_magazine/speedloader/pclip + name = "pistol stripper clip" + desc = "A stripper clip for pistol caliber weapons." + icon_state = "pclip" + caliber = CALIBER_PISTOL_MAGNUM + ammo_type = /obj/item/ammo_casing/pistol/magnum + matter = list(MATERIAL_STEEL = 1300) + max_ammo = 5 + multiple_sprites = 1 + /obj/item/ammo_magazine/speedloader/clip - name = "stripper clip" - desc = "A stripper clip for bolt action rifles." + name = "rifle stripper clip" + desc = "A stripper clip for rifle caliber weapons." icon_state = "clip" caliber = CALIBER_RIFLE ammo_type = /obj/item/ammo_casing/rifle diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index e5f4fa7cf3b..ef52ad8e6da 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -325,3 +325,63 @@ to_chat(user, "You need to open the cover to unload [src].") return ..() + +/obj/item/gun/projectile/automatic/battlerifle + name = "battle rifle" + desc = "The battle rifle hasn't changed much since its inception in the mid 20th century. Built to last in the toughest conditions, you can't tell if this one was even made this century." + icon = 'icons/obj/guns/battlerifle.dmi' + icon_state = "battlerifle" + item_state = null + w_class = ITEM_SIZE_HUGE + force = 12 + caliber = CALIBER_RIFLE_MILITARY + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 1, TECH_ESOTERIC = 5) + slot_flags = SLOT_BACK + load_method = MAGAZINE + magazine_type = /obj/item/ammo_magazine/mil_rifle + allowed_magazines = /obj/item/ammo_magazine/mil_rifle + one_hand_penalty = 10 + accuracy_power = 9 + accuracy = 4 + bulk = GUN_BULK_RIFLE + 1 + wielded_item_state = "battlerifle-wielded" + mag_insert_sound = 'sound/weapons/guns/interaction/ltrifle_magin.ogg' + mag_remove_sound = 'sound/weapons/guns/interaction/ltrifle_magout.ogg' + + //Battle Rifle is only accurate in semi-automatic fire. + firemodes = list( + list(mode_name="semi auto", burst=1, fire_delay=null, move_delay=null, one_hand_penalty=8, burst_accuracy=null, dispersion=null), + list(mode_name="full auto", can_autofire=1, burst=1, fire_delay=1, one_hand_penalty=12, burst_accuracy = list(0,-1,-2,-3,-4,-4,-4,-4,-4), dispersion = list(1.0, 1.0, 1.0, 1.0, 1.2)), + ) + +/obj/item/gun/projectile/automatic/battlerifle/on_update_icon() + ..() + if(ammo_magazine) + icon_state = "battlerifle" + wielded_item_state = "battlerifle-wielded" + else + icon_state = "battlerifle-empty" + wielded_item_state = "battlerifle-wielded-empty" + +/obj/item/gun/projectile/automatic/semistrip + name = "Carbine Rifle" + desc = "An old semi-automatic carbine chambered in large pistol rounds, this thing looks older than the SCG." + icon = 'icons/obj/guns/semistrip.dmi' + icon_state = "semistrip" + item_state = "semistrip" + w_class = ITEM_SIZE_LARGE + force = 10 + origin_tech = list(TECH_COMBAT = 2) + slot_flags = SLOT_BACK + caliber = CALIBER_PISTOL_MAGNUM + ammo_type = /obj/item/ammo_casing/pistol/magnum + load_method = SINGLE_CASING|SPEEDLOADER + max_shells = 10 + accuracy = 5 + scope_zoom = 0 + scoped_accuracy = 0 + wielded_item_state = "semistrip-wielded" + + firemodes = list( + list(mode_name="semi auto", burst=1, fire_delay=2, move_delay=null, one_hand_penalty=8, burst_accuracy=null, dispersion=null) + ) diff --git a/icons/mob/onmob/items/lefthand_guns.dmi b/icons/mob/onmob/items/lefthand_guns.dmi index 74a617a5d779f04bc6f3a9b83bbdf021f052e08f..1a8e13916d25fcbe7015c564ddacb71e2d0991be 100644 GIT binary patch literal 71531 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ℑ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%0DqsI_@@sVFHoi_VE)k|&rZDp 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 WWROs6b%7HE{As9atCTBSKKlfiT&?88%f^?*p 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≶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 diff --git a/icons/mob/onmob/items/righthand_guns.dmi b/icons/mob/onmob/items/righthand_guns.dmi index 019559255112a14628f410baa0398bacfa3cb4d8..93bd80c3686e9c6f018c81a1108e414eeecd665d 100644 GIT binary patch delta 15763 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 delta 14749 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 a7(=X`9G#tt@{fXmuUFEFQpFO6AN~(T24Z{w diff --git a/icons/mob/onmob/onmob_back.dmi b/icons/mob/onmob/onmob_back.dmi index 0314523600fa8930869c9da2ace587d8c1d0b250..f8ec26e9aef25efff8b55ef4a2793e5ea464f7b6 100644 GIT binary patch delta 26748 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 E1H7u(<^TWy delta 24395 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 diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index fcb9204076f90cb9b2da029758d6012612e123de..eaa06c378e6e8d9f5a6564386ec2736d82c27212 100644 GIT binary patch delta 4240 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 Nnx2_%xwdWC{{V4rc1QpK delta 4043 zcmY*b2{@Er`yN|ikewpLkV1*FgvK)b?6PFZHiZxx6tc~WiY!?x!bBr`_BGo~C{dPd zWgSb_F)79}li_>${=e(`{_l0Y%Q??E&-0x7+|Pa9*iO2JcDiI3P#I!n`vBo~-^Jh4 z_kkzs9t83*`^75_)TBIH^wMO7eCC+$>F#Q^Neh^?LT%d4quWI9x>O(irh3ZG={2JZ zeaD>>kE_4^nEn>FT3&++BCG><8sNWz0OO~Dr)t8b;J}z8t!?(|ckQ_?$fYX1?noIklGe|WdK>=*zS>VT<$u_4gIYqZ$He_ZXIJ-APKR!>9 zgtSmlmF^vd?DcuOWL~FSyd}0Opn@p}??@wIwoxKlJy`ySksw)g?c2!#8q=3Q=-;EW zFtK?aXgPfupVOV_?tr0{D4AQPhjcVtKw{n9|870ECK#-N^ziNMy1b-!@+MR zmBENZRz*HDMOTf(nAoz$D|W1?IqJcljDCfDA13R8d;6IXl5l!I-$c}f_%bF(A$#b} zT<|Sv4{frl=U?*{K9RkYP0(lX|ZhN{q)Ay$9m02=#OMHvM z?1S#RK)WZ;!|!()!@aLyj|C3v4Qoc9GG{RGM6myS!*xY0T;i@KNpCm;|Ews*aEErr zM6qW1^KfXs)CR~$sWhKun1jsS^eB;8hVEzt;y)|q_L;M|C-=?pG- zgr%>UN#r^m2o^X;qsMGjb6@nsuDAQi<7*p`6X}^dZHbgM)oXbj+2z8^TXbY9e&ebO zVCrtYw{kg(HQQid(}R0)mn{nY?0A66qayC44~eba_NOf>9Z?&3H8kH_(gdZo2l`~F zQ=v{A{xmSVZ0(>zeU0-adp7!#9lY-dOZ`&<>h(_g7vHmC+RSG<>48+3+ZxZv=;-3Oh!;7u zqn|N>^U&bd$R%^;F{c7UDy&;lQc_)>pG z(Qtg!hg=Q0oPvgi?}O@MR2u1@=CTlE;m2UH!;t{(YHe(Ez2fVYY16oGxa|c4SlWw+ zKfBu(fdyg%&smz?4qX-;Xxp+LXQu6MyR_PB(8xpUE;x>S%MlLUSHSz78r;&Zy708> zV_#2~5Ao+)_NeSZ?G2?oZNLHAsXvcEKu=tIT(L+|r>+zdaA;(E|5I4B=oT)Nv*;RVPg0CbvzCVHq@ai!tozoC|3VHa`eI>@+?4VVY!8z-fJYiia0K-h` zrFNW^k{TEp$=};M>Fjz2omc<#w^s#_&g==asiDQT>K6a}UB}QXIc1OR+pgUfo8(I| zhyJKPIe%j&)&*a86B2W0-})ngEIa7bABl?J5&(wzvuH0T=5h&E_6{3D^|}#oFMCEx z4)xAS$V^WS3j$j+0p}U-9ubemZ~Td$j&-Ufm}ZC3Jn!S1nhT*BJIz%jHLohtZqTIw zjdCY1(eF;_0ivHeI}x8uW*2+23KAQ;zoUlvxzMC7<({Ug9R@p5oM3NwXN(8#X4q*R z;I1HcVB)fw!+MIK_kA)PNq=HD|4;)~Y_8rgPa9ad*cOie+J?T* zWP3Rd$p4bg04WJfdb7~WxV@MAb=dNFqO74~wo2qIX~IM!22HKIJc30YRF&ZqEgKHW zN|k%_U>PeFIRq9zh0!~6;bI$O3#ByXyDCm}cizTDunj{1&_}$ct_Un#{KOc&Kli(k z57`_@V)ZzC+kJi;W#-3gebm3v0|3Ncquh<7lTmWX(n%61|u3+2gyqFQ3PGU)|xzz#^Ga|FS6Tk=lLF zb7XSoFSMThwk`*Z(z8VrKhxPfW3yxgr$ux>li0+s#aukX5WtBTq`J!JTn`M`Cx=d; zY$Lcp`GxTqq$;o?)YQVLwAlmWUnsE7!S2BJf7LnTXxQV%goW@!HU6OkLx$ zQmD-v_@$TKyX(0w#RINO#0}<%H%WSbew32v8q%b{p*65HFu}MJpb}PxA84sg)Op;j zFVpMITPPO)O4lP$`*|THHDRu+3}#sCoa6xqKv?EFhi^@?v$Jn37u9hTfXCUl7s+E% zQ)VV6?^89s3BYwH>g15%N<0JP<*=2@Zo6Cd?wD6Qcd~{%{Kbp8()GTsADdZ1tGKNo zOxM^ll&PUCOe`On3IXffag zB0lZ6VpIloo>cyY+ONN1M4SI$sqj z9A#`i<;VM{Da7038Utk4$Wa3*QA=dK$2SmEcD8b9t#Bi~5LYijIn}Hzb>cN4rsRg( z%EzQsL~EuYHaj!ziqKwsTcb2->FNJ11aO2xH`40M;e`Fa@on5Q>91;OsJj)qZDcQ29VW*`g=f4}U?RuN`Sc)@bfI!IcyZ?xQqS@W^ZLL3!lk)ddCdgG;YJf#9$? zHzv$N(}zSh6{xToKqgR7>Jq5_wFeb86@pTJ_dtw0&lj-Dlj^zAIgd}^eVGSOxC(Ln zow34%6&}ovf=apm@(?81@%6V!oO6!G>{^DYhsRET$r*jXJneU6j61xqxP%^Zg~A=o zn=;`ll<42InU}Law%Q-0lB$#Zcvs+yyH=)Ld-PQq4f}``Uej_8@UIfSYZt*a=PcVx zm*t~2OGI@#)^=RF7lZ}_2Eos~Z9F&5XgqvgA0$6i3JLl!a@V?B(QDjpCMEOdb6THC z``O2w0ou&x^o;qCK0ZElM?#$3?aB={5+yAxEiH|k4^`3PdB%A>#Ff_){1>JU6Q3zh zBh3+qhExX2wBIa8iW+{Xs4A*0`bRSA_OA?oJ)pZ!Wb1CBAlx9}WuR+{DAz_t{Rf$5 B{}BKH diff --git a/icons/obj/guns/battlerifle.dmi b/icons/obj/guns/battlerifle.dmi new file mode 100644 index 0000000000000000000000000000000000000000..47d1ffc92de6d3eefffac3a6d05d443acf08dbf0 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!VDw>HYaZfQW60^A+8Mn8KkA8+}zysb#(e{ zgxAN3mY0=AXbTzDR9FGkFqQ=Q1v5B2yO9RuR91yVl(?i8Cl_TFlw{`TF)&oj2@WeL zD*gT?xZva0CtBXRTIbH358e=JaMAd|Bc1a;nkN~GdU|(Q7zY_&Huh4O^JvnMNg)cs znwlGzsHkt%FxFKM?d&#I-Mm6VBkRP`nPF3A%;ri=4qiQbR&+p6r+53Nrq%PgZ&)&v z*Xt;rVQ~@$I-=3j#WBR9H#tFqwVB=Ekc7{Tr&Fi+#5gyfnbgb5x-mPz+pN5h*T~E) z%#4ROF`=L_!yut8J0ZbLU!OB5NG!;#gp-x2gf}46?G)E49v;i#LH?z2;D6>F`i!&v&s2C__$iW*DDA zz1YMxG|kx9I5i|Km4P7?ozK9)Y~tb)mKN%k8UoarmWIKH03&4v1qBAzFqi;3pMlvJ p>?W7cH0*phU~$0D9|eON007v<9&)RGDEa^Z002ovPDHLkV1mFEgJu8# literal 0 HcmV?d00001 From 0719157f2aa474b62b7b17e29a9a215c57fd3c51 Mon Sep 17 00:00:00 2001 From: Cody Brittain Date: Wed, 19 May 2021 13:37:50 -0400 Subject: [PATCH 007/246] Various outfit tweaks, and description changes. --- code/datums/appearances/automatic/cardborg.dm | 4 +- code/datums/outfits/jobs/_defines.dm | 5 + code/datums/outfits/jobs/command.dm | 4 +- .../objects/items/weapons/storage/backpack.dm | 106 +++++++++--------- .../crates_lockers/closets/secure/security.dm | 4 +- code/modules/clothing/under/jobs/medsci.dm | 2 +- maps/torch/job/outfits/command_outfits.dm | 8 +- maps/torch/structures/closets/command.dm | 4 +- 8 files changed, 74 insertions(+), 63 deletions(-) diff --git a/code/datums/appearances/automatic/cardborg.dm b/code/datums/appearances/automatic/cardborg.dm index 861012811f6..c79e36fb1b2 100644 --- a/code/datums/appearances/automatic/cardborg.dm +++ b/code/datums/appearances/automatic/cardborg.dm @@ -89,10 +89,10 @@ /decl/cardborg_appearance/centcom icon_state = "centcomborg" - backpack_type = /obj/item/storage/backpack/captain + backpack_type = /obj/item/storage/backpack/command /decl/cardborg_appearance/centcom/satchel - backpack_type = /obj/item/storage/backpack/satchel/cap + backpack_type = /obj/item/storage/backpack/satchel/com /decl/cardborg_appearance/syndicate icon_state = "droid-combat" diff --git a/code/datums/outfits/jobs/_defines.dm b/code/datums/outfits/jobs/_defines.dm index 3b94c4a3cbf..7ebec214b07 100644 --- a/code/datums/outfits/jobs/_defines.dm +++ b/code/datums/outfits/jobs/_defines.dm @@ -34,3 +34,8 @@ backpack_overrides[/decl/backpack_outfit/messenger_bag] = /obj/item/storage/back backpack_overrides[/decl/backpack_outfit/backpack] = /obj/item/storage/backpack/virology; \ backpack_overrides[/decl/backpack_outfit/satchel] = /obj/item/storage/backpack/satchel/vir; \ backpack_overrides[/decl/backpack_outfit/messenger_bag] = /obj/item/storage/backpack/messenger/viro; + +#define BACKPACK_OVERRIDE_COMMAND \ +backpack_overrides[/decl/backpack_outfit/backpack] = /obj/item/storage/backpack/command; \ +backpack_overrides[/decl/backpack_outfit/satchel] = /obj/item/storage/backpack/satchel/com; \ +backpack_overrides[/decl/backpack_outfit/messenger_bag] = /obj/item/storage/backpack/messenger/com; \ No newline at end of file diff --git a/code/datums/outfits/jobs/command.dm b/code/datums/outfits/jobs/command.dm index 80fde158bc6..a7a202df377 100644 --- a/code/datums/outfits/jobs/command.dm +++ b/code/datums/outfits/jobs/command.dm @@ -11,8 +11,8 @@ /decl/hierarchy/outfit/job/captain/New() ..() - backpack_overrides[/decl/backpack_outfit/backpack] = /obj/item/storage/backpack/captain - backpack_overrides[/decl/backpack_outfit/satchel] = /obj/item/storage/backpack/satchel/cap + backpack_overrides[/decl/backpack_outfit/backpack] = /obj/item/storage/backpack/command + backpack_overrides[/decl/backpack_outfit/satchel] = /obj/item/storage/backpack/satchel/com backpack_overrides[/decl/backpack_outfit/messenger_bag] = /obj/item/storage/backpack/messenger/com /decl/hierarchy/outfit/job/captain/post_equip(var/mob/living/carbon/human/H) diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index d63d562710d..774407ffc91 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -84,71 +84,72 @@ /obj/item/storage/backpack/cultpack name = "trophy rack" - desc = "It's useful for both carrying extra gear and proudly declaring your insanity." + desc = "It's useful for both carrying extra gear, and proudly declaring your dedication to your chosen malevolent deity." icon_state = "cultpack" /obj/item/storage/backpack/clown - name = "Giggles von Honkerton" - desc = "It's a backpack made by Honk! Co." + name = "\improper Giggles von Honkerton" + desc = "It's a very vibrant backpack, made for a clown." icon_state = "clownpack" item_state_slots = null /obj/item/storage/backpack/medic name = "medical backpack" - desc = "It's a backpack especially designed for use in a sterile environment." + desc = "It's a backpack, designed for use in the sterile confines of the infirmary." icon_state = "medicalpack" item_state_slots = null /obj/item/storage/backpack/security name = "security backpack" - desc = "It's a very robust backpack." + desc = "It's a very robust backpack, for security-related needs." icon_state = "securitypack" item_state_slots = null /obj/item/storage/backpack/security/exo name = "corporate security backpack" + desc = "It's a very robust backpack, for security-related needs. This one is in EXO colors." icon_state = "securitypack_exo" -/obj/item/storage/backpack/captain - name = "captain's backpack" - desc = "It's a special backpack made exclusively for officers." +/obj/item/storage/backpack/command + name = "command backpack" + desc = "It's a special backpack, made exclusively for senior officers." icon_state = "captainpack" item_state_slots = null /obj/item/storage/backpack/industrial name = "industrial backpack" - desc = "It's a tough backpack for the daily grind of industrial life." + desc = "It's a tough backpack, made for the daily grind of industrial life." icon_state = "engiepack" item_state_slots = null /obj/item/storage/backpack/toxins name = "science backpack" - desc = "It's a stain-resistant light backpack modeled for use in laboratories and other scientific institutions." + desc = "It's a stain-resistant light backpack, modeled for use in laboratories and other scientific settings." icon_state = "ntpack" /obj/item/storage/backpack/hydroponics - name = "herbalist's backpack" - desc = "It's a green backpack with many pockets to store plants and tools in." + name = "hydroponics backpack" + desc = "It's a green backpack, with many pockets to store plants and tools in." icon_state = "hydpack" /obj/item/storage/backpack/genetics - name = "geneticist backpack" - desc = "It's a backpack fitted with slots for diskettes and other workplace tools." + name = "genetics backpack" + desc = "It's a backpack, fitted with slots for diskettes and other workplace tools." icon_state = "genpack" /obj/item/storage/backpack/virology name = "sterile backpack" - desc = "It's a sterile backpack able to withstand different pathogens from entering its fabric." + desc = "It's a sterile backpack, specially designed for work in areas with a biosafety classification level." icon_state = "viropack" /obj/item/storage/backpack/chemistry - name = "pharmacist's backpack" - desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles." + name = "pharmacy backpack" + desc = "It's a sterile orange backpack, which was designed to hold beakers, pill bottles, and other reagent containers." icon_state = "chempack" /obj/item/storage/backpack/rucksack name = "black rucksack" - desc = "A sturdy military-grade backpack with low-profile straps. Designed to work well with armor." + desc = "A sturdy, military-grade backpack with low-profile straps. Designed to work well with armor." icon_state = "rucksack" item_state_slots = list(slot_l_hand_str = "rucksack", slot_r_hand_str = "rucksack") @@ -212,9 +213,9 @@ icon_state = "duffle_syndieammo" item_state_slots = list(slot_l_hand_str = "duffle_syndieammo", slot_r_hand_str = "duffle_syndieammo") -/obj/item/storage/backpack/dufflebag/captain - name = "captain's dufflebag" - desc = "A large dufflebag for holding extra captainly goods." +/obj/item/storage/backpack/dufflebag/com + name = "command dufflebag" + desc = "A large dufflebag for holding extra goods for senior command." icon_state = "duffle_captain" item_state_slots = list(slot_l_hand_str = "duffle_captain", slot_r_hand_str = "duffle_captain") @@ -237,6 +238,8 @@ item_state_slots = list(slot_l_hand_str = "duffle_eng", slot_r_hand_str = "duffle_eng") /obj/item/storage/backpack/dufflebag/firefighter + name = "firefighter's dufflebag" + desc = "A large dufflebag containing equipment to fight fires with." startswith = list( /obj/item/storage/belt/fire_belt/full, /obj/item/clothing/suit/fire/firefighter, @@ -253,7 +256,7 @@ /obj/item/storage/backpack/satchel name = "satchel" - desc = "A trendy looking satchel." + desc = "A trendy-looking satchel." icon_state = "satchel-norm" /obj/item/storage/backpack/satchel/grey @@ -317,7 +320,7 @@ /obj/item/storage/backpack/satchel/med name = "medical satchel" - desc = "A sterile satchel used in medical departments." + desc = "A sterile satchel designed for use in the sterile confines of the infirmary." icon_state = "satchel-med" item_state_slots = list( slot_l_hand_str = "medicalpack", @@ -325,28 +328,28 @@ ) /obj/item/storage/backpack/satchel/vir - name = "virologist satchel" - desc = "A sterile satchel with virologist colours." + name = "sterile satchel" + desc = "It's a sterile satchel, rated for use in areas with a biosafety classification level." icon_state = "satchel-vir" /obj/item/storage/backpack/satchel/chem - name = "pharmacist satchel" - desc = "A sterile satchel with pharmacist colours." + name = "pharmacy satchel" + desc = "It's a sterile orange satchel, designed to hold beakers, pill bottles, and other reagent containers." icon_state = "satchel-chem" /obj/item/storage/backpack/satchel/gen - name = "geneticist satchel" - desc = "A sterile satchel with geneticist colours." + name = "genetics satchel" + desc = "A green satchel, filled with slots for diskettes and other workplace tools." icon_state = "satchel-gen" /obj/item/storage/backpack/satchel/tox - name = "corporate satchel" - desc = "Useful for holding research materials. The colors on it denote it as a corporate bag." + name = "science satchel" + desc = "It's a stain-resistant satchel, modeled for use in laboratories and other scientific settings." icon_state = "satchel-nt" /obj/item/storage/backpack/satchel/sec name = "security satchel" - desc = "A robust satchel for security related needs." + desc = "A robust satchel for security-related needs." icon_state = "satchel-sec" item_state_slots = list( slot_l_hand_str = "securitypack", @@ -355,6 +358,7 @@ /obj/item/storage/backpack/satchel/sec/exo name = "corporate security satchel" + desc = "A robust satchel for corporate security-related needs. This one is in EXO colors." icon_state = "satchel-sec_exo" /obj/item/storage/backpack/satchel/hyd @@ -362,9 +366,9 @@ desc = "A green satchel for plant related work." icon_state = "satchel_hyd" -/obj/item/storage/backpack/satchel/cap - name = "captain's satchel" - desc = "An exclusive satchel for officers." +/obj/item/storage/backpack/satchel/com + name = "command satchel" + desc = "An exclusive satchel for senior officers." icon_state = "satchel-cap" item_state_slots = list( slot_l_hand_str = "satchel-cap", @@ -374,7 +378,7 @@ //Smuggler's satchel /obj/item/storage/backpack/satchel/flat name = "\improper Smuggler's satchel" - desc = "A very slim satchel that can easily fit into tight spaces." + desc = "A very slim satchel, that can easily fit into tight spaces." icon_state = "satchel-flat" item_state = "satchel-norm" level = 1 @@ -435,8 +439,8 @@ //Medical /obj/item/storage/backpack/ert/medical - name = "emergency response team medical backpack" - desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." + name = "emergency response team corpsman backpack" + desc = "A spacious backpack with lots of pockets, worn by the corpsmen of an Emergency Response Team." icon_state = "ert_medical" /* @@ -445,51 +449,51 @@ /obj/item/storage/backpack/messenger name = "messenger bag" - desc = "A sturdy backpack worn over one shoulder." + desc = "A small, sturdy backpack, worn over one shoulder." icon_state = "courierbag" /obj/item/storage/backpack/messenger/chem name = "pharmacy messenger bag" - desc = "A serile backpack worn over one shoulder. This one is in Chemsitry colors." + desc = "A small, sterile backpack, worn over one shoulder. This one was designed to hold beakers, pill bottles, and other reagent containers." icon_state = "courierbagchem" /obj/item/storage/backpack/messenger/med name = "medical messenger bag" - desc = "A sterile backpack worn over one shoulder used in medical departments." + desc = "A small, sterile backpack worn over one shoulder. This one was designed for use in the sterile confines of the infirmary." icon_state = "courierbagmed" /obj/item/storage/backpack/messenger/viro name = "virology messenger bag" - desc = "A sterile backpack worn over one shoulder. This one is in Virology colors." + desc = "A small, sterile backpack worn over one shoulder. This one was designed for work in areas with a biosafety classification level." icon_state = "courierbagviro" /obj/item/storage/backpack/messenger/tox - name = "corporate messenger bag" - desc = "A backpack worn over one shoulder. Useful for holding science materials. The colors on it denote it as a corporate bag." + name = "science messenger bag" + desc = "A small, stain-resistant backpack worn over one shoulder. This one was modeled for use in laboratories and other scientific settings." icon_state = "courierbagnt" /obj/item/storage/backpack/messenger/com - name = "captain's messenger bag" - desc = "A special backpack worn over one shoulder. This one is made specifically for officers." + name = "command messenger bag" + desc = "A small backpack worn over one shoulder. This one was made specifically for senior officers." icon_state = "courierbagcom" /obj/item/storage/backpack/messenger/engi - name = "engineering messenger bag" - desc = "A strong backpack worn over one shoulder. This one is designed for Industrial work." + name = "industrial messenger bag" + desc = "A small, tough backpack worn over one shoulder. This one was designed for industrial work." icon_state = "courierbagengi" /obj/item/storage/backpack/messenger/hyd name = "hydroponics messenger bag" - desc = "A backpack worn over one shoulder. This one is designed for plant-related work." + desc = "A small backpack worn over one shoulder. This one was designed for plant-related work." icon_state = "courierbaghyd" /obj/item/storage/backpack/messenger/sec name = "security messenger bag" - desc = "A tactical backpack worn over one shoulder. This one is in Security colors." + desc = "A small, tactical backpack worn over one shoulder." icon_state = "courierbagsec" /obj/item/storage/backpack/messenger/sec/exo name = "corporate security messenger bag" - desc = "A tactical backpack worn over one shoulder. This is black and bottle green." + desc = "A small, tactical backpack worn over one shoulder. This one is in EXO colors." icon_state = "courierbagsec_exo" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 71ab2100f2c..9e648896cb3 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -5,8 +5,8 @@ /obj/structure/closet/secure_closet/captains/WillContain() return list( - new/datum/atom_creator/weighted(list(/obj/item/storage/backpack/captain, /obj/item/storage/backpack/satchel/cap)), - new/datum/atom_creator/simple(/obj/item/storage/backpack/dufflebag/captain, 50), + new/datum/atom_creator/weighted(list(/obj/item/storage/backpack/command, /obj/item/storage/backpack/satchel/com)), + new/datum/atom_creator/simple(/obj/item/storage/backpack/dufflebag/com, 50), /obj/item/clothing/suit/captunic, /obj/item/clothing/suit/captunic/capjacket, /obj/item/clothing/head/caphat/cap, diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index 37cc793d743..ece9943509f 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -33,7 +33,7 @@ body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS /obj/item/clothing/under/rank/chemist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + desc = "It's made of a special fiber that gives special protection against biohazards. The striping on the suit denotes the wearer as a trained pharmacist." name = "pharmacist's jumpsuit" icon_state = "chemistry" item_state = "w_suit" diff --git a/maps/torch/job/outfits/command_outfits.dm b/maps/torch/job/outfits/command_outfits.dm index 945077ea87f..788b80921cb 100644 --- a/maps/torch/job/outfits/command_outfits.dm +++ b/maps/torch/job/outfits/command_outfits.dm @@ -15,9 +15,7 @@ /decl/hierarchy/outfit/job/torch/crew/command/CO/New() ..() - backpack_overrides[/decl/backpack_outfit/backpack] = /obj/item/storage/backpack/captain - backpack_overrides[/decl/backpack_outfit/satchel] = /obj/item/storage/backpack/satchel/cap - backpack_overrides[/decl/backpack_outfit/messenger_bag] = /obj/item/storage/backpack/messenger/com + BACKPACK_OVERRIDE_COMMAND /decl/hierarchy/outfit/job/torch/crew/command/XO name = OUTFIT_JOB_NAME("Executive Officer") @@ -26,6 +24,10 @@ shoes = /obj/item/clothing/shoes/dutyboots id_types = list(/obj/item/card/id/torch/silver) pda_type = /obj/item/modular_computer/pda/heads/hop + +/decl/hierarchy/outfit/job/torch/crew/command/XO/New() + ..() + BACKPACK_OVERRIDE_COMMAND /decl/hierarchy/outfit/job/torch/crew/command/XO/fleet name = OUTFIT_JOB_NAME("Executive Officer - Fleet") diff --git a/maps/torch/structures/closets/command.dm b/maps/torch/structures/closets/command.dm index 25afb2ee923..3c495b7d941 100644 --- a/maps/torch/structures/closets/command.dm +++ b/maps/torch/structures/closets/command.dm @@ -47,7 +47,7 @@ /obj/item/device/holowarrant, /obj/item/folder/blue, /obj/item/material/knife/folding/swiss/officer, - /obj/item/storage/backpack/satchel/cap, + /obj/item/storage/backpack/satchel/com, /obj/item/clothing/suit/armor/pcarrier/medium/command, /obj/item/clothing/head/helmet/solgov/command, ) @@ -75,7 +75,7 @@ /obj/item/device/holowarrant, /obj/item/folder/blue, /obj/item/material/knife/folding/swiss/officer, - /obj/item/storage/backpack/satchel/cap, + /obj/item/storage/backpack/satchel/com, /obj/item/storage/box/imprinting, /obj/item/clothing/suit/armor/pcarrier/medium/command, /obj/item/clothing/head/helmet/solgov/command From 142c9edab18a5ee4e6cdc19da1562ad1328fab36 Mon Sep 17 00:00:00 2001 From: Archemagus <32466328+Archemagus@users.noreply.github.com> Date: Thu, 20 May 2021 20:51:10 +0300 Subject: [PATCH 008/246] Making area able to raise Entered event --- code/game/area/areas.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 28f3711f1c2..ca0105fe64b 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -218,6 +218,7 @@ var/list/mob/living/forced_ambiance_list = new /area/Entered(A) + ..() if(!istype(A,/mob/living)) return var/mob/living/L = A @@ -334,4 +335,4 @@ var/list/mob/living/forced_ambiance_list = new /area/proc/can_modify_area() if (src && src.area_flags & AREA_FLAG_NO_MODIFY) return FALSE - return TRUE \ No newline at end of file + return TRUE From 5a6164b72a288887198aa3658d0b20de1a156729 Mon Sep 17 00:00:00 2001 From: Imienny Date: Wed, 19 May 2021 03:59:07 +0200 Subject: [PATCH 009/246] Stomach Tweak --- code/modules/mob/living/carbon/human/human.dm | 5 ++++- code/modules/organs/internal/stomach.dm | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 77adec9bba9..1b47202c1d4 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -84,7 +84,10 @@ return ..() var/obj/item/organ/internal/stomach/stomach = internal_organs_by_name[BP_STOMACH] if(stomach) - return nutrition + (stomach.ingested.total_volume * 10) + var/food_volume = 0 + for(var/datum/reagent/nutriment/A in stomach.ingested.reagent_list) + food_volume += A.volume + return nutrition + food_volume * 15 return 0 //Always hungry, but you can't actually eat. :( /mob/living/carbon/human/Stat() diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index 12035c1cd33..d43ed8b1862 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -80,7 +80,7 @@ /obj/item/organ/internal/stomach/attack_self(mob/user) . = ..() if(. && action_button_name == PUKE_ACTION_NAME && owner && !owner.incapacitated()) - owner.vomit(deliberate = TRUE) + owner.empty_stomach() refresh_action_button() /obj/item/organ/internal/stomach/return_air() From 618ba290ea06c58c44753634326fc6a31b8042ae Mon Sep 17 00:00:00 2001 From: Mucker Date: Fri, 21 May 2021 11:45:24 -0700 Subject: [PATCH 010/246] fix runtime on exiting buildmode --- code/modules/admin/buildmode/radiation.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/admin/buildmode/radiation.dm b/code/modules/admin/buildmode/radiation.dm index 0977b92471e..418c9da7339 100644 --- a/code/modules/admin/buildmode/radiation.dm +++ b/code/modules/admin/buildmode/radiation.dm @@ -22,7 +22,8 @@ Build Mode: Radiation - Visualizer for radiation sources & affected turfs. /datum/build_mode/radiation/Unselected() - overlay.Hide() + if (overlay) + overlay.Hide() /datum/build_mode/radiation/UpdateOverlay(atom/movable/M, turf/T) From 0cdc84d0c29d55080fda59744ac6db6d67d03d4d Mon Sep 17 00:00:00 2001 From: Sierra Brown Date: Fri, 21 May 2021 12:14:34 -0700 Subject: [PATCH 011/246] Removes asset protection agents --- maps/torch/icons/assignment_hud.dmi | Bin 2894 -> 2841 bytes maps/torch/items/cards_ids.dm | 1 - maps/torch/items/items.dm | 4 -- maps/torch/items/memos.dm | 16 ------ maps/torch/job/corporate_jobs.dm | 63 ---------------------- maps/torch/job/outfits/command_outfits.dm | 8 --- maps/torch/job/torch_jobs.dm | 4 +- maps/torch/loadout/_defines.dm | 6 +-- maps/torch/loadout/loadout_uniform.dm | 2 +- maps/torch/structures/closets/misc.dm | 33 ------------ maps/torch/torch6_bridge.dmm | 10 +--- 11 files changed, 7 insertions(+), 140 deletions(-) diff --git a/maps/torch/icons/assignment_hud.dmi b/maps/torch/icons/assignment_hud.dmi index 0d19c61a9edb694bf7182e768b1fbfb2364e7d92..a6b325ca917d75c548ef2449db6018e5470651b2 100644 GIT binary patch literal 2841 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 C0U}HQ literal 2894 zcmaJ@4K&m1AOCH(re;W*|3$XCx+J0GC4_}E#LSW;L_#IPrH$B2HPtOzQG`tqy?9qE z@1={BcemwrTxP;7#*nuC-GBGobN>B1|8t(_JfHLZKIeO$&-s2n-}79-xjG`&sjmY7 z0D(PVj|Tt{`mZf72LQm&$9&6Hk3gIY!9fN9t1BA^Z;nw|$WvbpBa9BpWHMu8;|&Fpwf8vipxew3aHj!4JP`5@07S8X zh^Z5123sw=*xt(f7JTQ_&D`=Oi}aaJ_Sp1v)AUMHd-q$)9B#IOUEGbmMDQG$%|R%qBnN-eyS{5d z)Q#UU-YG2qq^-l9yJpyp9m#2I5*dHhW=+l&f~qXn`aDgW<>_5JBo5bwWj>%}C##_h zq>;D>oQ)sRYab$J0NBLVk8LwgExpzf9w7SuxG#^3&EDr1GaPjV)Jqwop zE38yjFg=l}Aawa84lt)vD7&BKce+RFINfiuL)?!y>v$gGJg-u+{c1Hl-#H;~e0INT z!IM3%1}<+8-;(;N7UPh@bSOo<^Z_32;8l^)F}XPC9_Xt6zG`OJDdl&zch#Sdwl?-U za%Rtz*WUE#4;;N2+F)odQmLYesv61r?Q9D-xZm28A__sot{G1e_TucjgI$f-y`H)u zN8JXl7N-d>VIG>E%62%+xJUPS#d;d}n#ekllJq+2arW2djAgCDeui<;ZS&rOX@e`b zb#D&K_4yz##mvkf(6DEiWdDkf&d(`?x$Odtl^N~CHXbP19WgYE?o178MJ~PYJg3rn z=YIY1DJuwnW&L9LvCl@$jqa4j-cy`jf?2weLx6&cCXSXf|bR040dhxm} zAWezp`23c#?ETaxaIt?)uGcodL-D7`K$ z5hy>k9o&=Ox~gATFV-GIIG-uVLU?T1qw3%oour#|^@g8|Kq~WliOoI9Zy4zQ-FOW> z?%~zKEayr;6r+Nn5Wfu0%$#O=p3LXDSr*Se&0-$vTjsP#m#e$lu594hUM`;Q*^PLg zKP?QusNRBeRa zv^Ij01&4}e8kXhUWu+xy6TU36B9}k;tYr|91l3EBn!odJX(ct@~1&sE^9*j*s@&{!6sfKA0?+KF|8Xl+uSYDMAYe)GR>B-S0&YNm3D}` z{dF1>G6*K(pUofrR`h2|DXN~jTPr}_txKIdZX(6FkNTPHmIfUfWn1p1n7zM2tv_1i zh#5P$5{Rytj)J^FD#{Y5&h3eNJ6qw2dq)I@Of7HosVqN2#yN1Uk7r*~@_5C(_gtP8{=5sLD0nBmn;T!Gb zn1s|&z9mKgFW7rvT-%GU_N0sExf64kqIHovsm!2`t@#<58|QUCsTAF;BagRZ%`iLd zP8~({6>x5$lF{W(2#n zf_QMdZqIGwtw7f(K;F#j5a>>lDqstXqx$q7!Y!Ohg1?MlI zCF>?OdrfholoxEzg9>1=*|di)uRF0{joUG>M2(q2E$AjoB*aVE2)3VNrujebCiluR zrNjAkn#Z^i|1kLuB)x*iE+xl&iX9&!4qgDA=9WYs{TL_6gtV$%Td)v!{*+T)bnFS- z{6g#TMM#)UC_5N)q}|(RSUpn3g=;9*vkfCZCbA`F9;B2R(DIi&RwPL>YmM))oUUzN z^N09O28@I=$%W|WlKUDy%#Fs4EnZ92vbB@Hj6U<~-aCoi$mZt&C{MPd0=+B=5SGwf z*mP`si?W6VHhCz~j(V9p?ntBF*z}E?Kj^ojaPz+y`(wyZ(0_s9y^QZe5ld8-V!Vf> zvdcPwuQ^rqs3O^I&yE^Ah{NX1J|{vs?B%uWU4F$uMagapgJ(>)g_$0dGYh>n+GA?Z zZKY9Lc-)5xRD3AcT3RLVgvaf1Ie?S6iNJKYUh+Piz}87Qo;L7VOI0i2I3{S_q+Wq? zvuHMuYN}!K=2Q`%^-+7U2KGrT?4sR9bH_L9l#pL36o;P&(S5m}!HPAtBT!A!1-9)X zn2hMC`Jw9cV+voaTt`_74D|$~i*4b)iCfg1%Yy`- z;~F1*wi^oaIv+o8R?CcTpo|>q=r*8q^3eiDzmlWLu@j=NvJM zsWG*Wr?Y}F>EUHCduSLT4taZF;5sswO|Pc0#BegTjCm$xOxc;F@nmb- zFv4^9lE+|)_fUv+7BS!CMc!&uSv&HhHu+=fp+Cye4U;D2$Cr}NqE?B(qO4?r4nEw- zmtGTjM_S%Z5{>i0sO%4J>Ej!%t+qBZ@hW?=WV0{3OjXq?DyxoOf)sMRQ&g3a1XL<( zP>WI{s@$7bvl75ynaDfUK=W=;XXd3@Sn7T;%c{1kTxEAck^>)|?zVnjKMohq@O``^ z5```hY76?U2FBxPP$`dZvjlcz%7)>Z7o;@9i23E4I;_QICS{24_??0NZrm_v;)2w1 z_0Ib5Y&a}}-{(W4;=8}*by-U%stty&2~f#Fp?>-^HV^0I;w*JAhW%kq{bA*=?}QQ4 zTG;(@z1B>0iW&xc3=Mw_fl$cz!|(M#Puk#Zua4= zF{PW40*uj{5?E@o(p3fbtiz!8fR!egLg}3PjA6kEMP;Ge#_eEz*h-o+Vr16d< z5)My{oz$Ecl1?arF&0AC-Zw8c9v!$0cOofHC5MZ4;yOwLzd8T?{~6Y`au237{-*WM fpoo~JO64Rf@1^^G2@PERu>x2JSNo!U{?va1$|pgq diff --git a/maps/torch/items/cards_ids.dm b/maps/torch/items/cards_ids.dm index 7cd053e176d..aea078813c0 100644 --- a/maps/torch/items/cards_ids.dm +++ b/maps/torch/items/cards_ids.dm @@ -200,7 +200,6 @@ /obj/item/card/id/torch/passenger/corporate color = COLOR_BOTTLE_GREEN detail_color = COLOR_OFF_WHITE - job_access_type = /datum/job/bodyguard /obj/item/card/id/torch/passenger/corporate/liaison job_access_type = /datum/job/liaison diff --git a/maps/torch/items/items.dm b/maps/torch/items/items.dm index 2bf7dcc6c2c..5f3bf3cce3e 100644 --- a/maps/torch/items/items.dm +++ b/maps/torch/items/items.dm @@ -96,10 +96,6 @@ Unique items Weapons ******/ -/obj/item/gun/energy/gun/small/secure/corporate - desc = "An access-locked EXO-branded LAEP90-CS. It's designed to please paranoid corporate liaisons. Body cam not included." - req_access = list(access_liaison) - /obj/item/gun/projectile/revolver/medium/captain name = "\improper Final Argument" icon = 'maps/torch/icons/obj/uniques.dmi' diff --git a/maps/torch/items/memos.dm b/maps/torch/items/memos.dm index fe03909209c..de994a601f5 100644 --- a/maps/torch/items/memos.dm +++ b/maps/torch/items/memos.dm @@ -478,18 +478,10 @@ // corporate memos -/obj/item/paper/memo/corporate/bodyarmourbad - name = "\improper LPA recommendations" - info = {"Some "internal use only" corporate documents, detailing that having loss prevention associates openly wear body armor and a smartgun is an extreme violation of trust between EXO and the SCG and will result in harsh penalties. It also notes that this issue will be brought up at every 3 months during contract renewal."} - /obj/item/paper/memo/corporate/stipendcut name = "corporate spending records" info = {"A "confidential" memo on the current usage of funds provided to SEV Torch corporate liaisons; apparently there's threats of a strike over a cut to the cost-of-living stipends by the head office."} -/obj/item/paper/memo/corporate/LPArenting - name = "\improper LPA usage notice" - info = {"A "confidential" notice to the corporate liaison aboard the SEV Torch that their security staff are for their use only, and renting them out as extra manpower to security is a gross violation of their contract."} - /obj/item/paper/memo/corporate/uniondues name = "union dues reminder" info = {"A "confidential" memo sent from the Union Representative to all members of the Union reminding them that their mandated union fee pay period is approaching, and they should ensure their 250 thaler is deposited with them before the end of the month."} @@ -498,10 +490,6 @@ name = "proper document disposal" info = {"Some "internal use only" corporate documents detailing the proper ways to dispose of top secret and classified documents. It highlights that burning is most applicable, and shows pictures of shredded documents reconstructed with glue and sticky tape with a red "Fail" written below."} -/obj/item/paper/memo/corporate/lpainfo - name = "\improper LPA reminder" - info = {"An "internal use only" corporate document informing corporate liasions of the loss prevention associate's job aboard the ship. It cites concerns about the misuse of the LPA and general mistrust towards them from the rest of the crew, and warns how these tensions will damage EXO's image if they keep acting in this manner. It reminds them that LPAs are from an established industry and should have experience in many aspects other than carrying a firearm. It specifies good observation skills, solid restraint, an understanding of escalation rules in regards to their position, and that their experience as an industry professional can be useful as an advisor to their tasked liasion."} - /obj/item/paper/memo/corporate/exointerest name = "\improper EXO and you" info = {"Some "confidential" corporate documents directed to the corporate liasion, informing them that their job is to represent EXO and its immediate interests, which are to ensure the SEV Torch is able to maintain the currently defined direction that is within the interests of EXO as a whole."} @@ -518,10 +506,6 @@ name = "spellchecking and you" info = {"Some "internal use only" corporate memos reminding corporate liaisons to proofread reports. It details various reasons behind why correct spelling and punctuation help in making EXO and Expeditionary Command take your report seriously."} -/obj/item/paper/memo/corporate/assistants - name = "misuse of delegated personnel" - info = {"An "internal use only" corporate memo reminding corporate liaisons that just because the executive assistant is issued a firearm does not mean that they are expected to fulfill the same role as a loss prevention associate. It suggests using loss prevention associates to assist in tactical or physical affairs and executive assistants in bureaucratic or menial tasks."} - /obj/item/paper/memo/corporate/safetyfirst name = "contractor safety and you" info = {"An "internal use only" corporate document reminding corporate liaisons to report any unsafe behavior from contractors that they either see directly or is reported to them. It goes on to explain that ensuring the health and safety compliance of those under contract not only helps to keep up an image of security but ensures that the reputation of EXO as a whole is not tarnished due to a workplace accident."} diff --git a/maps/torch/job/corporate_jobs.dm b/maps/torch/job/corporate_jobs.dm index 072ac141189..a86be772adf 100644 --- a/maps/torch/job/corporate_jobs.dm +++ b/maps/torch/job/corporate_jobs.dm @@ -32,66 +32,3 @@ /datum/job/liaison/get_description_blurb() return "You are the Workplace Liaison. You are a civilian employee of EXO, the Expeditionary Corps Organisation, the government-owned corporate conglomerate that partially funds the Torch. You are on board the vessel to promote corporate interests and protect the rights of the contractors on board as their union leader. You are not internal affairs. You advise command on corporate and union matters and contractors on their rights and obligations. Maximise profit. Be the shady corporate shill you always wanted to be." - -/datum/job/liaison/post_equip_rank(var/mob/person, var/alt_title) - var/my_title = "\a ["\improper [(person.mind ? (person.mind.role_alt_title ? person.mind.role_alt_title : person.mind.assigned_role) : "Executive Assistant")]"]" - for(var/mob/M in GLOB.player_list) - if(M.client && M.mind) - if(M.mind.assigned_role == "Executive Assistant") - to_chat(M, SPAN_NOTICE("One of your employers, [my_title] named [person.real_name], is present on [GLOB.using_map.full_name].")) - ..() - -/datum/job/bodyguard - title = "Executive Assistant" - department = "Support" - department_flag = SPT - total_positions = 1 - spawn_positions = 1 - supervisors = "the Workplace Liaison" - selection_color = "#3d3d7f" - economic_power = 12 - minimal_player_age = 7 - minimum_character_age = list(SPECIES_HUMAN = 19) - outfit_type = /decl/hierarchy/outfit/job/torch/passenger/corporate_bodyguard - allowed_branches = list(/datum/mil_branch/civilian) - allowed_ranks = list(/datum/mil_rank/civ/contractor) - min_skill = list( SKILL_BUREAUCRACY = SKILL_BASIC, - SKILL_EVA = SKILL_BASIC, - SKILL_COMBAT = SKILL_BASIC, - SKILL_WEAPONS = SKILL_BASIC, - SKILL_FORENSICS = SKILL_BASIC) - max_skill = list( SKILL_COMBAT = SKILL_EXPERT, - SKILL_WEAPONS = SKILL_EXPERT, - SKILL_FORENSICS = SKILL_EXPERT) - alt_titles = list( - "Union Enforcer", - "Loss Prevention Associate", - "Asset Protection Agent" - ) - skill_points = 20 - - access = list( - access_liaison, access_bridge, access_solgov_crew, - access_nanotrasen, access_commissary, - access_sec_guard, access_torch_fax, access_radio_serv - ) - - defer_roundstart_spawn = TRUE - -/datum/job/bodyguard/is_position_available() - if(..()) - for(var/mob/M in GLOB.player_list) - if(M.client && M.mind && M.mind.assigned_role == "Workplace Liaison") - return TRUE - return FALSE - -/datum/job/bodyguard/get_description_blurb() - return "You are the Executive Assistant. You are an employee of one of the corporations that make up the Expeditionary Corps Organisation, and your job is to assist the Liason in corporate affairs. You are also expected to protect the Liason's life, though not in any way that breaks the law." - -/datum/job/bodyguard/post_equip_rank(var/mob/person, var/alt_title) - var/my_title = "\a ["\improper [(person.mind ? (person.mind.role_alt_title ? person.mind.role_alt_title : person.mind.assigned_role) : "Executive Assistant")]"]" - for(var/mob/M in GLOB.player_list) - if(M.client && M.mind) - if(M.mind.assigned_role == "Workplace Liaison") - to_chat(M, SPAN_NOTICE("Your bodyguard, [my_title] named [person.real_name], is present on [GLOB.using_map.full_name].")) - ..() diff --git a/maps/torch/job/outfits/command_outfits.dm b/maps/torch/job/outfits/command_outfits.dm index 945077ea87f..f14e24d2bfd 100644 --- a/maps/torch/job/outfits/command_outfits.dm +++ b/maps/torch/job/outfits/command_outfits.dm @@ -95,14 +95,6 @@ pda_type = /obj/item/modular_computer/pda/heads/paperpusher backpack_contents = list(/obj/item/clothing/accessory/badge/nanotrasen = 1) -/decl/hierarchy/outfit/job/torch/passenger/corporate_bodyguard - name = OUTFIT_JOB_NAME("Executive Assistant") - l_ear = /obj/item/device/radio/headset/heads/torchcorp - uniform = /obj/item/clothing/under/suit_jacket/corp - shoes = /obj/item/clothing/shoes/laceup - id_types = list( /obj/item/card/id/torch/passenger/corporate) - pda_type = /obj/item/modular_computer/pda/heads/paperpusher - /decl/hierarchy/outfit/job/torch/crew/representative name = OUTFIT_JOB_NAME("SolGov Representative") l_ear = /obj/item/device/radio/headset/headset_com diff --git a/maps/torch/job/torch_jobs.dm b/maps/torch/job/torch_jobs.dm index e9418974e0e..ad6f2a22b5c 100644 --- a/maps/torch/job/torch_jobs.dm +++ b/maps/torch/job/torch_jobs.dm @@ -15,12 +15,12 @@ /datum/species/unathi/yeosa = list(HUMAN_ONLY_JOBS, /datum/job/liaison, /datum/job/warden), /datum/species/skrell = list(HUMAN_ONLY_JOBS), /datum/species/machine = list(HUMAN_ONLY_JOBS, /datum/job/liaison, /datum/job/psychiatrist), - /datum/species/diona = list(HUMAN_ONLY_JOBS, /datum/job/officer, /datum/job/bodyguard, /datum/job/liaison, /datum/job/warden, /datum/job/doctor, /datum/job/medical_trainee), //Other jobs unavailable via branch restrictions, + /datum/species/diona = list(HUMAN_ONLY_JOBS, /datum/job/officer, /datum/job/liaison, /datum/job/warden, /datum/job/doctor, /datum/job/medical_trainee), //Other jobs unavailable via branch restrictions, ) #undef HUMAN_ONLY_JOBS allowed_jobs = list(/datum/job/captain, /datum/job/hop, /datum/job/rd, /datum/job/cmo, /datum/job/chief_engineer, /datum/job/hos, - /datum/job/liaison, /datum/job/bodyguard, /datum/job/representative, /datum/job/sea, + /datum/job/liaison, /datum/job/representative, /datum/job/sea, /datum/job/bridgeofficer, /datum/job/pathfinder, /datum/job/nt_pilot, /datum/job/explorer, /datum/job/senior_engineer, /datum/job/engineer, /datum/job/roboticist, /datum/job/engineer_trainee, /datum/job/officer, /datum/job/warden, /datum/job/detective, diff --git a/maps/torch/loadout/_defines.dm b/maps/torch/loadout/_defines.dm index 0b13f71a637..7bb617bb2ad 100644 --- a/maps/torch/loadout/_defines.dm +++ b/maps/torch/loadout/_defines.dm @@ -1,13 +1,13 @@ //The following is a list of defs to be used for the Torch loadout. //For jobs that allow for decorative or ceremonial clothing -#define FORMAL_ROLES list(/datum/job/liaison, /datum/job/bodyguard, /datum/job/rd, /datum/job/senior_scientist, /datum/job/scientist, /datum/job/scientist_assistant, /datum/job/psychiatrist, /datum/job/representative, /datum/job/assistant, /datum/job/bartender, /datum/job/merchant, /datum/job/detective, /datum/job/chaplain, /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/CTI_Undergraduate_Xenoscience_Researcher, /datum/job/submap/colonist, /datum/job/submap/pod, /datum/job/chef, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer) +#define FORMAL_ROLES list(/datum/job/liaison, /datum/job/rd, /datum/job/senior_scientist, /datum/job/scientist, /datum/job/scientist_assistant, /datum/job/psychiatrist, /datum/job/representative, /datum/job/assistant, /datum/job/bartender, /datum/job/merchant, /datum/job/detective, /datum/job/chaplain, /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/CTI_Undergraduate_Xenoscience_Researcher, /datum/job/submap/colonist, /datum/job/submap/pod, /datum/job/chef, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer) //For civilian jobs that may have a uniform, but not a strict one #define SEMIFORMAL_ROLES list(/datum/job/assistant, /datum/job/mining, /datum/job/scientist_assistant, /datum/job/psychiatrist, /datum/job/bartender, /datum/job/merchant, /datum/job/nt_pilot, /datum/job/scientist, /datum/job/senior_scientist, /datum/job/detective, /datum/job/chaplain, /datum/job/roboticist, /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman, /datum/job/submap/colonist, /datum/job/submap/pod, /datum/job/chef, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer, /datum/job/senior_doctor, /datum/job/junior_doctor, /datum/job/doctor, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/CTI_Undergraduate_Xenoscience_Researcher) //For civilian jobs that may have a strict uniform. -#define SEMIANDFORMAL_ROLES list(/datum/job/assistant, /datum/job/mining, /datum/job/scientist_assistant, /datum/job/psychiatrist, /datum/job/bartender, /datum/job/merchant, /datum/job/nt_pilot, /datum/job/liaison, /datum/job/bodyguard, /datum/job/rd, /datum/job/senior_scientist, /datum/job/scientist, /datum/job/representative, /datum/job/detective, /datum/job/chaplain, /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman, /datum/job/submap/colonist, /datum/job/submap/pod, /datum/job/chef, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer, /datum/job/senior_doctor, /datum/job/junior_doctor, /datum/job/doctor, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/CTI_Undergraduate_Xenoscience_Researcher) +#define SEMIANDFORMAL_ROLES list(/datum/job/assistant, /datum/job/mining, /datum/job/scientist_assistant, /datum/job/psychiatrist, /datum/job/bartender, /datum/job/merchant, /datum/job/nt_pilot, /datum/job/liaison, /datum/job/rd, /datum/job/senior_scientist, /datum/job/scientist, /datum/job/representative, /datum/job/detective, /datum/job/chaplain, /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman, /datum/job/submap/colonist, /datum/job/submap/pod, /datum/job/chef, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer, /datum/job/senior_doctor, /datum/job/junior_doctor, /datum/job/doctor, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/CTI_Undergraduate_Xenoscience_Researcher) //For civilian jobs with no uniform or formal clothing requirements. #define CASUAL_ROLES list(/datum/job/assistant, /datum/job/janitor, /datum/job/chef, /datum/job/bartender, /datum/job/cargo_tech, /datum/job/roboticist, /datum/job/mining, /datum/job/chaplain, /datum/job/merchant, /datum/job/submap/bearcat_captain, /datum/job/submap/bearcat_crewman, /datum/job/submap/colonist, /datum/job/submap/pod, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer, /datum/job/senior_doctor, /datum/job/junior_doctor, /datum/job/doctor, /datum/job/scientist, /datum/job/senior_scientist, /datum/job/scientist_assistant, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/CTI_Undergraduate_Xenoscience_Researcher) @@ -43,7 +43,7 @@ #define RESEARCH_ROLES list(/datum/job/rd, /datum/job/scientist, /datum/job/mining, /datum/job/scientist_assistant, /datum/job/assistant, /datum/job/nt_pilot, /datum/job/senior_scientist, /datum/job/roboticist) //For jobs that spawn with weapons in their lockers -#define ARMED_ROLES list(/datum/job/captain, /datum/job/hop, /datum/job/rd, /datum/job/cmo, /datum/job/chief_engineer, /datum/job/hos, /datum/job/sea, /datum/job/officer, /datum/job/warden, /datum/job/detective, /datum/job/merchant, /datum/job/bodyguard, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer) +#define ARMED_ROLES list(/datum/job/captain, /datum/job/hop, /datum/job/rd, /datum/job/cmo, /datum/job/chief_engineer, /datum/job/hos, /datum/job/sea, /datum/job/officer, /datum/job/warden, /datum/job/detective, /datum/job/merchant, /datum/job/submap/CTI_pilot, /datum/job/submap/CTI_engineer, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer) //For jobs that spawn with armor in their lockers #define ARMORED_ROLES list(/datum/job/captain, /datum/job/hop, /datum/job/rd, /datum/job/cmo, /datum/job/chief_engineer, /datum/job/hos, /datum/job/qm, /datum/job/sea, /datum/job/officer, /datum/job/warden, /datum/job/detective, /datum/job/merchant, /datum/job/submap/skrellscoutship_crew, /datum/job/submap/skrellscoutship_crew/leader, /datum/job/submap/scavver_pilot, /datum/job/submap/scavver_doctor, /datum/job/submap/scavver_engineer) diff --git a/maps/torch/loadout/loadout_uniform.dm b/maps/torch/loadout/loadout_uniform.dm index abf7e1a15ae..39e3a513b5a 100644 --- a/maps/torch/loadout/loadout_uniform.dm +++ b/maps/torch/loadout/loadout_uniform.dm @@ -80,4 +80,4 @@ allowed_roles = list(/datum/job/liaison) /datum/gear/uniform/corp_exec_jacket - allowed_roles = list(/datum/job/liaison, /datum/job/bodyguard) + allowed_roles = list(/datum/job/liaison) diff --git a/maps/torch/structures/closets/misc.dm b/maps/torch/structures/closets/misc.dm index f478d6cfc46..f97e264d8f1 100644 --- a/maps/torch/structures/closets/misc.dm +++ b/maps/torch/structures/closets/misc.dm @@ -47,39 +47,6 @@ /obj/item/device/radio/headset/heads/torchntcommand/alt ) -/decl/closet_appearance/secure_closet/torch/corporate/bodyguard - extra_decals = list( - "stripe_vertical_left_full" = COLOR_OFF_WHITE, - "stripe_vertical_right_full" = COLOR_OFF_WHITE, - "security" = COLOR_OFF_WHITE - ) - -/obj/structure/closet/secure_closet/bodyguard - name = "\improper corporate protection locker" - req_access = list(access_sec_guard) - closet_appearance = /decl/closet_appearance/secure_closet/torch/corporate/bodyguard - -/obj/structure/closet/secure_closet/bodyguard/WillContain() - return list( - /obj/item/device/flash, - /obj/item/clothing/accessory/storage/holster/armpit, - /obj/item/storage/secure/briefcase, - /obj/item/clothing/shoes/laceup, - /obj/item/gun/energy/gun/small/secure/corporate, - /obj/item/clothing/under/rank/internalaffairs/plain/nt, - /obj/item/clothing/suit/storage/toggle/suit/black, - /obj/item/clothing/gloves/color/black, - /obj/item/clothing/glasses/sunglasses/big, - /obj/item/clothing/accessory/badge/nanotrasen, - /obj/item/device/radio/headset/heads/torchcorp, - /obj/item/device/radio/headset/heads/torchcorp/alt, - /obj/item/clothing/accessory/storage/black_vest, - /obj/item/storage/belt/holster/general, - /obj/item/device/flashlight/maglight, - /obj/item/device/radio, - /obj/item/crowbar/prybar - ) - /obj/structure/closet/secure_closet/representative name = "\improper Sol Central Government representative's locker" req_access = list(access_representative) diff --git a/maps/torch/torch6_bridge.dmm b/maps/torch/torch6_bridge.dmm index 4468929df20..6ec8dacf569 100644 --- a/maps/torch/torch6_bridge.dmm +++ b/maps/torch/torch6_bridge.dmm @@ -5879,10 +5879,6 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 }, -/obj/machinery/recharger/wallcharger{ - dir = 1; - pixel_y = -24 - }, /turf/simulated/floor/carpet/green, /area/crew_quarters/heads/office/cl/backroom) "nE" = ( @@ -14248,10 +14244,6 @@ /obj/item/book/manual/solgov_law, /turf/simulated/floor/carpet/blue2, /area/crew_quarters/heads/office/sgr) -"QV" = ( -/obj/structure/closet/secure_closet/bodyguard, -/turf/simulated/floor/carpet/green, -/area/crew_quarters/heads/office/cl/backroom) "QW" = ( /obj/machinery/computer/ship/engines{ dir = 4; @@ -29363,7 +29355,7 @@ al bi dk Kx -QV +NC Pl Pn oE From df2b7d85f5e403ace483759d6d525eddc5b034d9 Mon Sep 17 00:00:00 2001 From: Jocelynn Date: Sun, 23 May 2021 11:56:28 -0400 Subject: [PATCH 012/246] fleet dress beret go brr Update maps/torch/datums/uniforms_fleet.dm Co-authored-by: Spookerton Update maps/torch/datums/uniforms_fleet.dm Co-authored-by: Spookerton Update maps/torch/datums/uniforms_fleet.dm Co-authored-by: Spookerton Update maps/torch/datums/uniforms_fleet.dm Co-authored-by: Spookerton Update maps/torch/datums/uniforms_fleet.dm Co-authored-by: Spookerton Update maps/torch/datums/uniforms_fleet.dm Co-authored-by: Spookerton --- maps/torch/datums/uniforms.dm | 2 -- maps/torch/datums/uniforms_fleet.dm | 36 +++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/maps/torch/datums/uniforms.dm b/maps/torch/datums/uniforms.dm index 6e3c1fd1017..a5817d0db96 100644 --- a/maps/torch/datums/uniforms.dm +++ b/maps/torch/datums/uniforms.dm @@ -81,8 +81,6 @@ dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_gloves = /obj/item/clothing/gloves/white - dress_extra = list(/obj/item/clothing/head/beret/solgov/fleet/dress) - /decl/hierarchy/mil_uniform/civilian name = "Master civilian outfit" //Basically just here for the rent-a-tux, ahem, I mean... dress uniform. hierarchy_type = /decl/hierarchy/mil_uniform/civilian diff --git a/maps/torch/datums/uniforms_fleet.dm b/maps/torch/datums/uniforms_fleet.dm index 04bf17ee15f..cf640bfa3e6 100644 --- a/maps/torch/datums/uniforms_fleet.dm +++ b/maps/torch/datums/uniforms_fleet.dm @@ -17,7 +17,10 @@ dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/officer dress_hat = /obj/item/clothing/head/solgov/dress/fleet/command - dress_extra = list(/obj/item/material/sword/replica/officersword, /obj/item/clothing/head/beret/solgov/fleet/dress/command) + dress_extra = list( + /obj/item/material/sword/replica/officersword, + /obj/item/clothing/head/beret/solgov/fleet/dress/command + ) /decl/hierarchy/mil_uniform/fleet/com/seniorofficer name = "Fleet senior command" @@ -54,7 +57,9 @@ service_hat = /obj/item/clothing/head/solgov/dress/fleet service_over = /obj/item/clothing/suit/storage/solgov/service/fleet - dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_over = list( + /obj/item/clothing/suit/storage/solgov/dress/fleet + ) /decl/hierarchy/mil_uniform/fleet/eng/snco name = "Fleet engineering SNCO" @@ -64,7 +69,10 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco - dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) + dress_extra = list( + /obj/item/material/sword/replica/officersword/pettyofficer, + /obj/item/clothing/head/beret/solgov/fleet/dress + ) /decl/hierarchy/mil_uniform/fleet/eng/officer name = "Fleet engineering CO" @@ -85,7 +93,10 @@ dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/officer dress_hat = /obj/item/clothing/head/solgov/dress/fleet/command - dress_extra = list(/obj/item/material/sword/replica/officersword, /obj/item/clothing/head/beret/solgov/fleet/dress/command) + dress_extra = list( + /obj/item/material/sword/replica/officersword, + /obj/item/clothing/head/beret/solgov/fleet/dress/command + ) /decl/hierarchy/mil_uniform/fleet/eng/officer/com //Can only be officers name = "Fleet engineering command" @@ -128,6 +139,10 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_extra = list( + /obj/item/clothing/head/beret/solgov/fleet/dress + ) + /decl/hierarchy/mil_uniform/fleet/sec/snco name = "Fleet security SNCO" @@ -137,7 +152,10 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco - dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) + dress_extra = list( + /obj/item/material/sword/replica/officersword/pettyofficer, + /obj/item/clothing/head/beret/solgov/fleet/dress + ) /decl/hierarchy/mil_uniform/fleet/sec/officer name = "Fleet security CO" @@ -200,6 +218,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_extra = /obj/item/clothing/head/beret/solgov/fleet/dress /decl/hierarchy/mil_uniform/fleet/med/snco name = "Fleet medical SNCO" @@ -272,6 +291,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_extra = /obj/item/clothing/head/beret/solgov/fleet/dress /decl/hierarchy/mil_uniform/fleet/sup/snco name = "Fleet supply SNCO" @@ -348,6 +368,8 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_extra = /obj/item/clothing/head/beret/solgov/fleet/dress + /decl/hierarchy/mil_uniform/fleet/srv/snco name = "Fleet service SNCO" @@ -402,6 +424,8 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_extra = /obj/item/clothing/head/beret/solgov/fleet/dress + /decl/hierarchy/mil_uniform/fleet/exp/snco name = "Fleet exploration SNCO" @@ -456,6 +480,8 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet + dress_extra = /obj/item/clothing/head/beret/solgov/fleet/dress + /decl/hierarchy/mil_uniform/fleet/spt/snco name = "Fleet support SNCO" From 95ccfaab2283158eb93be525c8d0e818d8274508 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 23 May 2021 23:00:28 +0530 Subject: [PATCH 013/246] Gives Engineer Trainees access to Material Goggles --- maps/torch/loadout/loadout_eyes.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maps/torch/loadout/loadout_eyes.dm b/maps/torch/loadout/loadout_eyes.dm index c81381cb4c4..afa054b8d5d 100644 --- a/maps/torch/loadout/loadout_eyes.dm +++ b/maps/torch/loadout/loadout_eyes.dm @@ -21,4 +21,4 @@ allowed_roles = TECHNICAL_ROLES /datum/gear/eyes/material - allowed_roles = list(/datum/job/chief_engineer, /datum/job/senior_engineer, /datum/job/engineer, /datum/job/mining, /datum/job/scientist_assistant) \ No newline at end of file + allowed_roles = list(/datum/job/chief_engineer, /datum/job/senior_engineer, /datum/job/engineer, /datum/job/engineer_trainee, /datum/job/mining, /datum/job/scientist_assistant) \ No newline at end of file From e4c1eb0a795e275baa9fd023b2f1572816f3e156 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 23 May 2021 20:11:41 +0200 Subject: [PATCH 014/246] Automatic changelog generation for PR #30711 [ci skip] --- html/changelogs/AutoChangeLog-pr-30711.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30711.yml diff --git a/html/changelogs/AutoChangeLog-pr-30711.yml b/html/changelogs/AutoChangeLog-pr-30711.yml new file mode 100644 index 00000000000..130f115f16a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30711.yml @@ -0,0 +1,6 @@ +author: Imienny +delete-after: true +changes: + - bugfix: Taking pills no longer stops you from eating. + - tweak: Races which can regurgitate content of their stomach no longer have to + stick finger in to their throat. From 093025ec3fd81fc9312680d486ebcfd2d4fc00b9 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 23 May 2021 20:12:02 +0200 Subject: [PATCH 015/246] Automatic changelog generation for PR #30720 [ci skip] --- html/changelogs/AutoChangeLog-pr-30720.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30720.yml diff --git a/html/changelogs/AutoChangeLog-pr-30720.yml b/html/changelogs/AutoChangeLog-pr-30720.yml new file mode 100644 index 00000000000..a580eb2e772 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30720.yml @@ -0,0 +1,4 @@ +author: SierraKomodo +delete-after: true +changes: + - rscdel: The Executive Assistant/Asset Protection Agent role has been removed. From 36f87df6d1c110f7589e33306cb2ce8fd5091d9c Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 23 May 2021 21:23:20 +0200 Subject: [PATCH 016/246] Automatic changelog generation for PR #30683 [ci skip] --- html/changelogs/AutoChangeLog-pr-30683.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30683.yml diff --git a/html/changelogs/AutoChangeLog-pr-30683.yml b/html/changelogs/AutoChangeLog-pr-30683.yml new file mode 100644 index 00000000000..d066e5fb376 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30683.yml @@ -0,0 +1,6 @@ +author: BlueNexus +delete-after: true +changes: + - maptweak: Simplified the Nacelles slightly. As a side effect, the engines should + come up to pressure faster. + - maptweak: Pipes in the Nacelles should no longer explode with burn mode on From 607373554274ebd6081e5cf4f182e4fa4c1ecf0b Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 23 May 2021 21:33:40 +0200 Subject: [PATCH 017/246] Automatic changelog generation for PR #30730 [ci skip] --- html/changelogs/AutoChangeLog-pr-30730.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30730.yml diff --git a/html/changelogs/AutoChangeLog-pr-30730.yml b/html/changelogs/AutoChangeLog-pr-30730.yml new file mode 100644 index 00000000000..9763c4cc0b8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30730.yml @@ -0,0 +1,4 @@ +author: Ryan180602 +delete-after: true +changes: + - tweak: Engineer Trainees now get access to material goggles From cc23fc48db8328dfc92d577c1ba2e00b2354db5d Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 23 May 2021 22:07:05 +0200 Subject: [PATCH 018/246] Automatic changelog generation for PR #30713 [ci skip] --- html/changelogs/AutoChangeLog-pr-30713.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30713.yml diff --git a/html/changelogs/AutoChangeLog-pr-30713.yml b/html/changelogs/AutoChangeLog-pr-30713.yml new file mode 100644 index 00000000000..01a86d13d49 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30713.yml @@ -0,0 +1,10 @@ +author: GeneralCamo +delete-after: true +changes: + - tweak: Renamed the captain's backpack and captain's satchel to the command backpack + and command satchel respectively, and changed its description to denote it is + given to senior officers. + - tweak: The executive officer now gets command packs by default + - tweak: Former corporate packs have been renamed to science packs. + - spellcheck: Various style and grammar improvements have been made to the descriptions + of various backpacks. From 39bb25abe47a461df4021c87b4542e50e0349939 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 23 May 2021 22:07:24 +0000 Subject: [PATCH 019/246] Automatic changelog generation --- html/changelog.html | 27 ++++++++++++++++++++++ html/changelogs/.all_changelog.yml | 21 +++++++++++++++++ html/changelogs/AutoChangeLog-pr-30683.yml | 6 ----- html/changelogs/AutoChangeLog-pr-30711.yml | 6 ----- html/changelogs/AutoChangeLog-pr-30713.yml | 10 -------- html/changelogs/AutoChangeLog-pr-30720.yml | 4 ---- html/changelogs/AutoChangeLog-pr-30730.yml | 4 ---- 7 files changed, 48 insertions(+), 30 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30683.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30711.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30713.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30720.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30730.yml diff --git a/html/changelog.html b/html/changelog.html index 64caa0e07f1..dcf3a0dd0c9 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,33 @@ -->
+

23 May 2021

+

BlueNexus updated:

+
    +
  • Simplified the Nacelles slightly. As a side effect, the engines should come up to pressure faster.
  • +
  • Pipes in the Nacelles should no longer explode with burn mode on
  • +
+

GeneralCamo updated:

+
    +
  • Renamed the captain's backpack and captain's satchel to the command backpack and command satchel respectively, and changed its description to denote it is given to senior officers.
  • +
  • The executive officer now gets command packs by default
  • +
  • Former corporate packs have been renamed to science packs.
  • +
  • Various style and grammar improvements have been made to the descriptions of various backpacks.
  • +
+

Imienny updated:

+
    +
  • Taking pills no longer stops you from eating.
  • +
  • Races which can regurgitate content of their stomach no longer have to stick finger in to their throat.
  • +
+

Ryan180602 updated:

+
    +
  • Engineer Trainees now get access to material goggles
  • +
+

SierraKomodo updated:

+
    +
  • The Executive Assistant/Asset Protection Agent role has been removed.
  • +
+

22 May 2021

Spookerton updated:

    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index b02474814b5..8fd3562d458 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16852,3 +16852,24 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2021-05-22: Spookerton: - admin: Added radiation build mode. +2021-05-23: + BlueNexus: + - maptweak: Simplified the Nacelles slightly. As a side effect, the engines should + come up to pressure faster. + - maptweak: Pipes in the Nacelles should no longer explode with burn mode on + GeneralCamo: + - tweak: Renamed the captain's backpack and captain's satchel to the command backpack + and command satchel respectively, and changed its description to denote it is + given to senior officers. + - tweak: The executive officer now gets command packs by default + - tweak: Former corporate packs have been renamed to science packs. + - spellcheck: Various style and grammar improvements have been made to the descriptions + of various backpacks. + Imienny: + - bugfix: Taking pills no longer stops you from eating. + - tweak: Races which can regurgitate content of their stomach no longer have to + stick finger in to their throat. + Ryan180602: + - tweak: Engineer Trainees now get access to material goggles + SierraKomodo: + - rscdel: The Executive Assistant/Asset Protection Agent role has been removed. diff --git a/html/changelogs/AutoChangeLog-pr-30683.yml b/html/changelogs/AutoChangeLog-pr-30683.yml deleted file mode 100644 index d066e5fb376..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30683.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: BlueNexus -delete-after: true -changes: - - maptweak: Simplified the Nacelles slightly. As a side effect, the engines should - come up to pressure faster. - - maptweak: Pipes in the Nacelles should no longer explode with burn mode on diff --git a/html/changelogs/AutoChangeLog-pr-30711.yml b/html/changelogs/AutoChangeLog-pr-30711.yml deleted file mode 100644 index 130f115f16a..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30711.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: Imienny -delete-after: true -changes: - - bugfix: Taking pills no longer stops you from eating. - - tweak: Races which can regurgitate content of their stomach no longer have to - stick finger in to their throat. diff --git a/html/changelogs/AutoChangeLog-pr-30713.yml b/html/changelogs/AutoChangeLog-pr-30713.yml deleted file mode 100644 index 01a86d13d49..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30713.yml +++ /dev/null @@ -1,10 +0,0 @@ -author: GeneralCamo -delete-after: true -changes: - - tweak: Renamed the captain's backpack and captain's satchel to the command backpack - and command satchel respectively, and changed its description to denote it is - given to senior officers. - - tweak: The executive officer now gets command packs by default - - tweak: Former corporate packs have been renamed to science packs. - - spellcheck: Various style and grammar improvements have been made to the descriptions - of various backpacks. diff --git a/html/changelogs/AutoChangeLog-pr-30720.yml b/html/changelogs/AutoChangeLog-pr-30720.yml deleted file mode 100644 index a580eb2e772..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30720.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: SierraKomodo -delete-after: true -changes: - - rscdel: The Executive Assistant/Asset Protection Agent role has been removed. diff --git a/html/changelogs/AutoChangeLog-pr-30730.yml b/html/changelogs/AutoChangeLog-pr-30730.yml deleted file mode 100644 index 9763c4cc0b8..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30730.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Ryan180602 -delete-after: true -changes: - - tweak: Engineer Trainees now get access to material goggles From 1584648ceab9d09aa40a67c4ee0f753d410cfd7d Mon Sep 17 00:00:00 2001 From: spookerton Date: Sun, 23 May 2021 23:11:55 +0100 Subject: [PATCH 020/246] swaps earm peacekeeper armor for sol medium --- maps/torch/torch5_deck1.dmm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maps/torch/torch5_deck1.dmm b/maps/torch/torch5_deck1.dmm index 01dcbfae6c2..8947c4c64ab 100644 --- a/maps/torch/torch5_deck1.dmm +++ b/maps/torch/torch5_deck1.dmm @@ -9435,8 +9435,8 @@ /obj/item/clothing/shoes/dutyboots, /obj/item/clothing/accessory/storage/holster/thigh, /obj/item/clothing/glasses/tacgoggles, -/obj/item/clothing/suit/armor/pcarrier/blue/sol, -/obj/item/clothing/head/helmet/solgov, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury/tactical) "aEH" = ( From 63b70cd2e044e4b479b49cfbfb20c759c7a84e76 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 24 May 2021 00:30:14 +0200 Subject: [PATCH 021/246] Automatic changelog generation for PR #30714 [ci skip] --- html/changelogs/AutoChangeLog-pr-30714.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30714.yml diff --git a/html/changelogs/AutoChangeLog-pr-30714.yml b/html/changelogs/AutoChangeLog-pr-30714.yml new file mode 100644 index 00000000000..c53ebda2082 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30714.yml @@ -0,0 +1,5 @@ +author: The-Spanish-Inquisition +delete-after: true +changes: + - rscadd: Added two new projectile firearms; Added one ammo speed loader to support + new firearm. From ac53f0f0810b1cfdb7714b16c59625bd7e2dbd6f Mon Sep 17 00:00:00 2001 From: Mucker Date: Sun, 23 May 2021 16:15:10 -0700 Subject: [PATCH 022/246] port polaris mob AI --- baystation12.dme | 33 +- code/__defines/mobs.dm | 48 +- code/__defines/subsystem-priority.dm | 1 + code/__defines/subsystems.dm | 2 + code/_helpers/global_access.dm | 10 + code/_helpers/lists.dm | 41 ++ code/_macros.dm | 4 +- code/_onclick/click.dm | 2 +- code/_onclick/item_attack.dm | 17 + code/controllers/subsystems/ai.dm | 37 ++ code/controllers/subsystems/aifast.dm | 38 ++ code/datums/mind/mind.dm | 2 +- code/datums/trading/goods.dm | 2 +- code/datums/trading/misc.dm | 38 +- code/game/gamemodes/cult/cult_structures.dm | 2 +- code/game/machinery/hologram.dm | 10 +- code/game/objects/effects/spiders.dm | 8 +- code/game/objects/items/devices/dociler.dm | 2 +- code/game/objects/items/glassjar.dm | 4 +- code/game/objects/objs.dm | 6 + code/game/objects/random/random.dm | 4 +- .../structures/crates_lockers/largecrate.dm | 10 +- code/game/objects/structures/plasticflaps.dm | 4 +- code/game/turfs/initialization/maintenance.dm | 4 +- code/game/turfs/turf.dm | 35 +- code/modules/admin/buildmode/ai.dm | 231 ++++++++ code/modules/admin/topic.dm | 8 +- code/modules/admin/view_variables/helpers.dm | 1 + code/modules/admin/view_variables/topic.dm | 7 + code/modules/ai/__readme.dm | 201 +++++++ code/modules/ai/_defines.dm | 34 ++ .../ai/aI_holder_subtypes/simple_mob_ai.dm | 189 ++++++ code/modules/ai/ai_holder.dm | 368 ++++++++++++ code/modules/ai/ai_holder_combat.dm | 323 ++++++++++ code/modules/ai/ai_holder_combat_unseen.dm | 43 ++ code/modules/ai/ai_holder_communication.dm | 136 +++++ code/modules/ai/ai_holder_cooperation.dm | 116 ++++ code/modules/ai/ai_holder_debug.dm | 97 +++ code/modules/ai/ai_holder_disabled.dm | 82 +++ code/modules/ai/ai_holder_fleeing.dm | 43 ++ code/modules/ai/ai_holder_follow.dm | 68 +++ code/modules/ai/ai_holder_movement.dm | 165 ++++++ code/modules/ai/ai_holder_pathfinding.dm | 58 ++ code/modules/ai/ai_holder_targeting.dm | 290 +++++++++ code/modules/ai/interfaces.dm | 107 ++++ code/modules/ai/say_list.dm | 125 ++++ code/modules/assembly/mousetrap.dm | 2 +- code/modules/client/client_procs.dm | 5 + code/modules/events/infestation.dm | 4 +- code/modules/events/whale_migration.dm | 2 +- .../personal_achievement_specific_object.dm | 4 +- code/modules/hydroponics/seed.dm | 8 +- code/modules/hydroponics/seed_datums.dm | 4 +- code/modules/item_worth/worths_list.dm | 4 +- code/modules/mob/animations.dm | 24 + code/modules/mob/hear_say.dm | 10 +- code/modules/mob/living/living.dm | 4 + code/modules/mob/living/living_defense.dm | 30 + code/modules/mob/living/living_defines.dm | 8 + .../living/silicon/robot/drone/drone_items.dm | 2 +- .../living/simple_animal/aquatic/_aquatic.dm | 6 +- .../simple_animal/aquatic/_aquatic_hostile.dm | 7 +- .../aquatic/_aquatic_retaliate.dm | 8 +- .../mob/living/simple_animal/borer/borer.dm | 6 +- .../mob/living/simple_animal/combat.dm | 219 +++++++ .../simple_animal/constructs/constructs.dm | 14 +- .../mob/living/simple_animal/crow/crow.dm | 10 +- .../mob/living/simple_animal/defense.dm | 133 +++++ .../simple_animal/familiars/familiars.dm | 2 +- .../simple_animal/friendly/_friendly.dm | 3 + .../mob/living/simple_animal/friendly/cat.dm | 102 ++-- .../living/simple_animal/friendly/corgi.dm | 45 +- .../mob/living/simple_animal/friendly/crab.dm | 18 +- .../simple_animal/friendly/farm_animals.dm | 114 ++-- .../friendly/juvenile_space_whale.dm | 35 +- .../living/simple_animal/friendly/lizard.dm | 2 +- .../living/simple_animal/friendly/mouse.dm | 51 +- .../living/simple_animal/friendly/mushroom.dm | 20 +- .../living/simple_animal/friendly/possum.dm | 51 +- .../living/simple_animal/friendly/slime.dm | 9 +- .../living/simple_animal/friendly/tomato.dm | 10 +- .../living/simple_animal/hostile/antlion.dm | 42 +- .../living/simple_animal/hostile/bad_drone.dm | 29 +- .../mob/living/simple_animal/hostile/bat.dm | 65 +-- .../mob/living/simple_animal/hostile/bear.dm | 132 +---- .../mob/living/simple_animal/hostile/carp.dm | 37 +- .../hostile/commanded/commanded.dm | 74 +-- .../mob/living/simple_animal/hostile/drake.dm | 28 +- .../simple_animal/hostile/faithful_hound.dm | 9 +- .../living/simple_animal/hostile/faithless.dm | 60 +- .../simple_animal/hostile/giant_spider.dm | 552 ------------------ .../hostile/giant_spider/_giant_spider.dm | 104 ++++ .../hostile/giant_spider/guard.dm | 78 +++ .../hostile/giant_spider/hunter.dm | 85 +++ .../hostile/giant_spider/nurse.dm | 269 +++++++++ .../hostile/giant_spider/spitter.dm | 38 ++ .../living/simple_animal/hostile/hivebot.dm | 25 +- .../living/simple_animal/hostile/hostile.dm | 288 +-------- .../mob/living/simple_animal/hostile/leech.dm | 29 +- .../simple_animal/hostile/meat_horrors.dm | 39 +- .../mob/living/simple_animal/hostile/mimic.dm | 61 +- .../mob/living/simple_animal/hostile/pike.dm | 4 +- .../living/simple_animal/hostile/pirate.dm | 15 +- .../simple_animal/hostile/retaliate/clown.dm | 16 +- .../simple_animal/hostile/retaliate/drone.dm | 51 +- .../hostile/retaliate/exoplanet.dm | 145 +++-- .../hostile/retaliate/giant_crab.dm | 72 +-- .../retaliate/giant_parrot/giant_parrot.dm | 38 +- .../simple_animal/hostile/retaliate/goose.dm | 23 +- .../simple_animal/hostile/retaliate/jelly.dm | 13 +- .../hostile/retaliate/king_of_goats.dm | 164 +++--- .../simple_animal/hostile/retaliate/parrot.dm | 33 +- .../hostile/retaliate/retaliate.dm | 31 +- .../hostile/retaliate/space_whale.dm | 31 +- .../living/simple_animal/hostile/russian.dm | 3 +- .../living/simple_animal/hostile/syndicate.dm | 2 - .../mob/living/simple_animal/hostile/tree.dm | 13 +- .../living/simple_animal/hostile/vagrant.dm | 49 +- .../living/simple_animal/hostile/voxslug.dm | 25 +- code/modules/mob/living/simple_animal/life.dm | 105 ++++ .../modules/mob/living/simple_animal/shade.dm | 12 +- .../mob/living/simple_animal/simple_animal.dm | 419 +++++-------- code/modules/mob/mob_movement.dm | 5 + code/modules/mob/observer/ghost/ghost.dm | 4 +- code/modules/mob/transform_procs.dm | 12 +- .../overmap/exoplanets/planet_types/grass.dm | 12 +- code/modules/projectiles/projectile.dm | 8 +- code/modules/random_map/dungeon/predefined.dm | 4 +- code/modules/random_map/noise/desert.dm | 2 +- code/modules/random_map/noise/tundra.dm | 2 +- code/modules/reagents/Chemistry-Recipes.dm | 14 +- .../reagents/reagent_containers/glass.dm | 2 +- .../spells/aoe_turf/conjure/druidic_spells.dm | 46 +- .../spells/artifacts/spellbound_servants.dm | 16 +- code/modules/spells/targeted/shapeshift.dm | 2 +- code/modules/tension/tension.dm | 245 ++++++++ .../xenoarcheaology/artifacts/autocloner.dm | 12 +- icons/misc/buildmode.dmi | Bin 2349 -> 2786 bytes icons/mob/hivebot.dmi | Bin 0 -> 15677 bytes maps/away/ascent_caulship/ascent_props.dm | 10 +- maps/away/blueriver/blueriver.dm | 50 +- maps/away/errant_pisces/errant_pisces.dm | 38 +- maps/away/lar_maria/lar_maria.dm | 16 +- maps/away/meatstation/meatstation.dm | 45 +- maps/away/scavver/scavver_gantry-2.dmm | 2 +- maps/away/slavers/slavers_base.dm | 4 +- .../crashed_pod/crashed_pod.dmm | 2 +- .../exoplanet_ruins/hydrobase/hydrobase.dm | 10 +- .../exoplanet_ruins/playablecolony/colony.dmm | 8 +- .../space_ruins/multi_zas_test.dmm | 2 +- maps/torch/torch6_bridge.dmm | 2 +- maps/torch/torch_npcs.dm | 4 +- test/check-paths.sh | 4 +- test/run-test.sh | 2 +- 154 files changed, 5675 insertions(+), 2250 deletions(-) create mode 100644 code/controllers/subsystems/ai.dm create mode 100644 code/controllers/subsystems/aifast.dm create mode 100644 code/modules/admin/buildmode/ai.dm create mode 100644 code/modules/ai/__readme.dm create mode 100644 code/modules/ai/_defines.dm create mode 100644 code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm create mode 100644 code/modules/ai/ai_holder.dm create mode 100644 code/modules/ai/ai_holder_combat.dm create mode 100644 code/modules/ai/ai_holder_combat_unseen.dm create mode 100644 code/modules/ai/ai_holder_communication.dm create mode 100644 code/modules/ai/ai_holder_cooperation.dm create mode 100644 code/modules/ai/ai_holder_debug.dm create mode 100644 code/modules/ai/ai_holder_disabled.dm create mode 100644 code/modules/ai/ai_holder_fleeing.dm create mode 100644 code/modules/ai/ai_holder_follow.dm create mode 100644 code/modules/ai/ai_holder_movement.dm create mode 100644 code/modules/ai/ai_holder_pathfinding.dm create mode 100644 code/modules/ai/ai_holder_targeting.dm create mode 100644 code/modules/ai/interfaces.dm create mode 100644 code/modules/ai/say_list.dm create mode 100644 code/modules/mob/living/simple_animal/combat.dm create mode 100644 code/modules/mob/living/simple_animal/defense.dm create mode 100644 code/modules/mob/living/simple_animal/friendly/_friendly.dm delete mode 100644 code/modules/mob/living/simple_animal/hostile/giant_spider.dm create mode 100644 code/modules/mob/living/simple_animal/hostile/giant_spider/_giant_spider.dm create mode 100644 code/modules/mob/living/simple_animal/hostile/giant_spider/guard.dm create mode 100644 code/modules/mob/living/simple_animal/hostile/giant_spider/hunter.dm create mode 100644 code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm create mode 100644 code/modules/mob/living/simple_animal/hostile/giant_spider/spitter.dm create mode 100644 code/modules/mob/living/simple_animal/life.dm create mode 100644 code/modules/tension/tension.dm create mode 100644 icons/mob/hivebot.dmi diff --git a/baystation12.dme b/baystation12.dme index 1ad954194da..de8a0e38608 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -173,6 +173,8 @@ #include "code\controllers\evacuation\evacuation_predicate.dm" #include "code\controllers\evacuation\evacuation_shuttle.dm" #include "code\controllers\evacuation\~evac.dm" +#include "code\controllers\subsystems\ai.dm" +#include "code\controllers\subsystems\aifast.dm" #include "code\controllers\subsystems\air.dm" #include "code\controllers\subsystems\alarm.dm" #include "code\controllers\subsystems\antags.dm" @@ -1226,6 +1228,7 @@ #include "code\modules\admin\buildmode\_click_handler.dm" #include "code\modules\admin\buildmode\_color_pool.dm" #include "code\modules\admin\buildmode\_overlay.dm" +#include "code\modules\admin\buildmode\ai.dm" #include "code\modules\admin\buildmode\areas.dm" #include "code\modules\admin\buildmode\atmosphere.dm" #include "code\modules\admin\buildmode\build.dm" @@ -1306,6 +1309,22 @@ #include "code\modules\admin\view_variables\view_variables.dm" #include "code\modules\admin\view_variables\view_variables_global.dm" #include "code\modules\admin\view_variables\vv_set_handlers.dm" +#include "code\modules\ai\_defines.dm" +#include "code\modules\ai\ai_holder.dm" +#include "code\modules\ai\ai_holder_combat.dm" +#include "code\modules\ai\ai_holder_combat_unseen.dm" +#include "code\modules\ai\ai_holder_communication.dm" +#include "code\modules\ai\ai_holder_cooperation.dm" +#include "code\modules\ai\ai_holder_debug.dm" +#include "code\modules\ai\ai_holder_disabled.dm" +#include "code\modules\ai\ai_holder_fleeing.dm" +#include "code\modules\ai\ai_holder_follow.dm" +#include "code\modules\ai\ai_holder_movement.dm" +#include "code\modules\ai\ai_holder_pathfinding.dm" +#include "code\modules\ai\ai_holder_targeting.dm" +#include "code\modules\ai\interfaces.dm" +#include "code\modules\ai\say_list.dm" +#include "code\modules\ai\aI_holder_subtypes\simple_mob_ai.dm" #include "code\modules\alarm\alarm.dm" #include "code\modules\alarm\alarm_handler.dm" #include "code\modules\alarm\atmosphere_alarm.dm" @@ -2169,6 +2188,9 @@ #include "code\modules\mob\living\silicon\robot\modules\module_security.dm" #include "code\modules\mob\living\silicon\robot\modules\module_standard.dm" #include "code\modules\mob\living\silicon\robot\modules\module_uncertified.dm" +#include "code\modules\mob\living\simple_animal\combat.dm" +#include "code\modules\mob\living\simple_animal\defense.dm" +#include "code\modules\mob\living\simple_animal\life.dm" #include "code\modules\mob\living\simple_animal\natural_weapons.dm" #include "code\modules\mob\living\simple_animal\shade.dm" #include "code\modules\mob\living\simple_animal\simple_animal.dm" @@ -2188,6 +2210,7 @@ #include "code\modules\mob\living\simple_animal\constructs\soulstone.dm" #include "code\modules\mob\living\simple_animal\crow\crow.dm" #include "code\modules\mob\living\simple_animal\familiars\familiars.dm" +#include "code\modules\mob\living\simple_animal\friendly\_friendly.dm" #include "code\modules\mob\living\simple_animal\friendly\cat.dm" #include "code\modules\mob\living\simple_animal\friendly\corgi.dm" #include "code\modules\mob\living\simple_animal\friendly\crab.dm" @@ -2208,7 +2231,6 @@ #include "code\modules\mob\living\simple_animal\hostile\drake.dm" #include "code\modules\mob\living\simple_animal\hostile\faithful_hound.dm" #include "code\modules\mob\living\simple_animal\hostile\faithless.dm" -#include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm" #include "code\modules\mob\living\simple_animal\hostile\hivebot.dm" #include "code\modules\mob\living\simple_animal\hostile\hostile.dm" #include "code\modules\mob\living\simple_animal\hostile\leech.dm" @@ -2222,9 +2244,11 @@ #include "code\modules\mob\living\simple_animal\hostile\vagrant.dm" #include "code\modules\mob\living\simple_animal\hostile\voxslug.dm" #include "code\modules\mob\living\simple_animal\hostile\commanded\_command_defines.dm" -#include "code\modules\mob\living\simple_animal\hostile\commanded\bear_companion.dm" -#include "code\modules\mob\living\simple_animal\hostile\commanded\commanded.dm" -#include "code\modules\mob\living\simple_animal\hostile\commanded\nanomachines.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spider\_giant_spider.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spider\guard.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spider\hunter.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spider\nurse.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spider\spitter.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\exoplanet.dm" @@ -2991,6 +3015,7 @@ #include "code\modules\tables\rack.dm" #include "code\modules\tables\tables.dm" #include "code\modules\tables\update_triggers.dm" +#include "code\modules\tension\tension.dm" #include "code\modules\turbolift\_turbolift.dm" #include "code\modules\turbolift\turbolift.dm" #include "code\modules\turbolift\turbolift_areas.dm" diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 235b4a56258..0e02099ad74 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -9,6 +9,7 @@ #define CANPARALYSE 0x4 #define CANPUSH 0x8 #define PASSEMOTES 0x10 // Mob has a cortical borer or holders inside of it that need to see emotes. +#define LEAPING 0x16 #define GODMODE 0x1000 #define FAKEDEATH 0x2000 // Replaces stuff like changeling.changeling_fakedeath. #define NO_ANTAG 0x4000 // Players are restricted from gaining antag roles when occupying this mob @@ -35,12 +36,23 @@ #define BORGXRAY 0x4 #define BORGMATERIAL 8 -#define HOSTILE_STANCE_IDLE 1 -#define HOSTILE_STANCE_ALERT 2 -#define HOSTILE_STANCE_ATTACK 3 -#define HOSTILE_STANCE_ATTACKING 4 -#define HOSTILE_STANCE_TIRED 5 -#define HOSTILE_STANCE_INSIDE 6 + +#define STANCE_SLEEP 0 // Doing (almost) nothing, to save on CPU because nobody is around to notice or the mob died. +#define STANCE_IDLE 1 // The more or less default state. Wanders around, looks for baddies, and spouts one-liners. +#define STANCE_ALERT 2 // A baddie is visible but not too close, and essentially we tell them to go away or die. +#define STANCE_APPROACH 3 // Attempting to get into range to attack them. +#define STANCE_FIGHT 4 // Actually fighting, with melee or ranged. +#define STANCE_BLINDFIGHT 5 // Fighting something that cannot be seen by the mob, from invisibility or out of sight. +#define STANCE_REPOSITION 6 // Relocating to a better position while in combat. Also used when moving away from a danger like grenades. +#define STANCE_MOVE 7 // Similar to above but for out of combat. If a baddie is seen, they'll cancel and fight them. +#define STANCE_FOLLOW 8 // Following somone, without trying to murder them. +#define STANCE_FLEE 9 // Run away from the target because they're too spooky/we're dying/some other reason. +#define STANCE_DISABLED 10 // Used when the holder is afflicted with certain status effects, such as stuns or confusion. + +#define STANCE_ATTACK 11 // Backwards compatability +#define STANCE_ATTACKING 12 // Ditto + +#define STANCES_COMBAT list(STANCE_ALERT, STANCE_APPROACH, STANCE_FIGHT, STANCE_BLINDFIGHT, STANCE_REPOSITION) #define LEFT 0x1 #define RIGHT 0x2 @@ -359,6 +371,28 @@ #define MOB_FLAG_HOLY_BAD 0x001 // If this mob is allergic to holiness +// More refined version of SA_* ""intelligence"" seperators. +// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID' +#define MOB_CLASS_NONE 0 // Default value, and used to invert for _ALL. + +#define MOB_CLASS_PLANT 1 // Unused at the moment. +#define MOB_CLASS_ANIMAL 2 // Animals and beasts like spiders, saviks, and bears. +#define MOB_CLASS_HUMANOID 4 // Non-robotic humanoids, including /simple_mob and /carbon/humans and their alien variants. +#define MOB_CLASS_SYNTHETIC 8 // Silicons, mechanical simple mobs, FBPs, and anything else that would pass is_synthetic() +#define MOB_CLASS_SLIME 16 // Everyone's favorite xenobiology specimen (and maybe prometheans?). +#define MOB_CLASS_ABERRATION 32 // Weird shit. +#define MOB_CLASS_DEMONIC 64 // Cult stuff. +#define MOB_CLASS_BOSS 128 // Future megafauna hopefully someday. +#define MOB_CLASS_ILLUSION 256 // Fake mobs, e.g. Technomancer illusions. +#define MOB_CLASS_PHOTONIC 512 // Holographic mobs like holocarp, similar to _ILLUSION, but that make no attempt to hide their true nature. + +#define MOB_CLASS_ALL (~MOB_CLASS_NONE) + +// For slime commanding. Higher numbers allow for more actions. +#define SLIME_COMMAND_OBEY 1 // When disciplined. +#define SLIME_COMMAND_FACTION 2 // When in the same 'faction'. +#define SLIME_COMMAND_FRIEND 3 // When befriended with a slime friendship agent. + #define MARKING_TARGET_SKIN 0 // Draw a datum/sprite_accessory/marking to the mob's body, eg. tattoos #define MARKING_TARGET_HAIR 1 // Draw a datum/sprite_accessory/marking to the mob's hair, eg. color fades #define MARKING_TARGET_HEAD 2 // Draw a datum/sprite_accessory/marking to the mob's head after their hair, eg. ears, horns @@ -391,3 +425,5 @@ #define DO_MISSING_USER (-1) #define DO_MISSING_TARGET (-2) #define DO_INCAPACITATED (-3) + +#define FAKE_INVIS_ALPHA_THRESHOLD 127 // If something's alpha var is at or below this number, certain things will pretend it is invisible. \ No newline at end of file diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm index 66c93ec0e54..995b9713e76 100644 --- a/code/__defines/subsystem-priority.dm +++ b/code/__defines/subsystem-priority.dm @@ -24,6 +24,7 @@ #define SS_PRIORITY_RADIATION 20 // Radiation processing and cache updates. #define SS_PRIORITY_OPEN_SPACE 20 // Open turf updates. #define SS_PRIORITY_AIRFLOW 15 // Object movement from ZAS airflow. +#define SS_PRIORITY_AI 15 // Mob AI #define SS_PRIORITY_VOTE 10 // Vote management. #define SS_PRIORITY_INACTIVITY 10 // Idle kicking. #define SS_PRIORITY_SUPPLY 10 // Supply point accumulation. diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 964949c89d1..dd35eb2e691 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -61,6 +61,8 @@ return;\ #define SS_INIT_XENOARCH -10 #define SS_INIT_BAY_LEGACY -12 #define SS_INIT_TICKER -20 +#define SS_INIT_AI -21 +#define SS_INIT_AIFAST -22 #define SS_INIT_CHAT -90 // Should be lower to ensure chat remains smooth during init. #define SS_INIT_UNIT_TESTS -100 diff --git a/code/_helpers/global_access.dm b/code/_helpers/global_access.dm index 4a477b5e867..83bfd8465f9 100644 --- a/code/_helpers/global_access.dm +++ b/code/_helpers/global_access.dm @@ -31,6 +31,10 @@ return global.Master; if("OOClog") return global.OOClog; + if("SSai") + return global.SSai; + if("SSaifast") + return global.SSaifast; if("SSair") return global.SSair; if("SSairflow") @@ -906,6 +910,10 @@ global.Master=newval; if("OOClog") global.OOClog=newval; + if("SSai") + global.SSai=newval; + if("SSaifast") + global.SSaifast=newval; if("SSair") global.SSair=newval; if("SSairflow") @@ -1765,6 +1773,8 @@ "LIGHTING_CORNER_DIAGONAL", "Master", "OOClog", + "SSai", + "SSaifast", "SSair", "SSairflow", "SSalarm", diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm index 9ab46f91a2a..60f54033c30 100644 --- a/code/_helpers/lists.dm +++ b/code/_helpers/lists.dm @@ -699,3 +699,44 @@ proc/dd_sortedTextList(list/incoming) var/atom/A = key if(A.type == T) return A + +/** + * 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 + +/** + * 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 diff --git a/code/_macros.dm b/code/_macros.dm index 1d9f86eb6ae..d0d5a6e706c 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -44,7 +44,7 @@ var/const/NEGATIVE_INFINITY = -1#INF // win: -1.#INF, lin: -inf #define isclient(A) istype(A, /client) -#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi) +#define iscorgi(A) istype(A, /mob/living/simple_animal/friendly/corgi) #define is_drone(A) istype(A, /mob/living/silicon/robot/drone) @@ -58,7 +58,7 @@ var/const/NEGATIVE_INFINITY = -1#INF // win: -1.#INF, lin: -inf #define isliving(A) istype(A, /mob/living) -#define ismouse(A) istype(A, /mob/living/simple_animal/mouse) +#define ismouse(A) istype(A, /mob/living/simple_animal/friendly/mouse) #define isnewplayer(A) istype(A, /mob/new_player) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index cf23f274410..c06c1193156 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -34,7 +34,7 @@ /turf/allow_click_through(var/atom/A, var/params, var/mob/user) return TRUE - + /* Standard mob ClickOn() Handles exceptions: middle click, modified clicks, exosuit actions diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 5b723e77352..72f3b5a7f22 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -104,3 +104,20 @@ avoid code duplication. This includes items that may sometimes act as a standard if(MUTATION_HULK in user.mutations) power *= 2 return target.hit_with_weapon(src, user, power, hit_zone) + +/** + * Used to get how fast a mob should attack, and influences click delay. + * This is just for inheritance. + */ +/mob/proc/get_attack_speed() + return DEFAULT_ATTACK_COOLDOWN + +/** + * 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 = base_attack_cooldown + if(istype(W)) + speed = W.attack_cooldown + + return speed diff --git a/code/controllers/subsystems/ai.dm b/code/controllers/subsystems/ai.dm new file mode 100644 index 00000000000..b993813abb5 --- /dev/null +++ b/code/controllers/subsystems/ai.dm @@ -0,0 +1,37 @@ +SUBSYSTEM_DEF(ai) + name = "AI" + init_order = SS_INIT_AI + priority = SS_PRIORITY_AI + wait = 2 SECONDS + //mobs can mess up unrelated tests, so we don't turn their AI on during them + #ifdef UNIT_TEST + flags = SS_NO_FIRE|SS_NO_INIT + #else + flags = SS_NO_INIT + #endif + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + + var/list/processing = list() + var/list/currentrun = list() + +/datum/controller/subsystem/ai/stat_entry(msg_prefix) + var/list/msg = list(msg_prefix) + msg += "P:[processing.len]" + ..(msg.Join()) + +/datum/controller/subsystem/ai/fire(resumed = 0) + if (!resumed) + src.currentrun = processing.Copy() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while(currentrun.len) + var/datum/ai_holder/A = currentrun[currentrun.len] + --currentrun.len + if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick + continue + A.handle_strategicals() + + if(MC_TICK_CHECK) + return \ No newline at end of file diff --git a/code/controllers/subsystems/aifast.dm b/code/controllers/subsystems/aifast.dm new file mode 100644 index 00000000000..540dacdc7a0 --- /dev/null +++ b/code/controllers/subsystems/aifast.dm @@ -0,0 +1,38 @@ +SUBSYSTEM_DEF(aifast) + name = "AI (Fast)" + init_order = SS_INIT_AIFAST + priority = SS_PRIORITY_AI + wait = 0.25 SECONDS // Every quarter second + + #ifdef UNIT_TEST + flags = SS_NO_FIRE|SS_NO_INIT + #else + flags = SS_NO_INIT + #endif + + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + + var/list/processing = list() + var/list/currentrun = list() + +/datum/controller/subsystem/aifast/stat_entry(msg_prefix) + var/list/msg = list(msg_prefix) + msg += "P:[processing.len]" + ..(msg.Join()) + +/datum/controller/subsystem/aifast/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/ai_holder/A = currentrun[currentrun.len] + --currentrun.len + if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick + continue + A.handle_tactics() + + if(MC_TICK_CHECK) + return \ No newline at end of file diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 9e152962183..1681369db4e 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -575,7 +575,7 @@ ..() mind.assigned_role = "Animal" -/mob/living/simple_animal/corgi/mind_initialize() +/mob/living/simple_animal/friendly/corgi/mind_initialize() ..() mind.assigned_role = "Corgi" diff --git a/code/datums/trading/goods.dm b/code/datums/trading/goods.dm index bacfdc44ab6..9bff6e728f0 100644 --- a/code/datums/trading/goods.dm +++ b/code/datums/trading/goods.dm @@ -301,7 +301,7 @@ Sells devices, odds and ends, and medical stuff ) possible_wanted_items = list(/mob/living/simple_animal/tindalos = TRADER_THIS_TYPE, - /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/tomato = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/beast/diyaab = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/beast/shantak= TRADER_THIS_TYPE, diff --git a/code/datums/trading/misc.dm b/code/datums/trading/misc.dm index 590befb5f8e..3884ce5b780 100644 --- a/code/datums/trading/misc.dm +++ b/code/datums/trading/misc.dm @@ -23,37 +23,37 @@ TRADER_BRIBE_SUCCESS = "Hm. It'll be good for the animals, so sure.", ) - possible_wanted_items = list(/mob/living/simple_animal/corgi = TRADER_THIS_TYPE, - /mob/living/simple_animal/cat = TRADER_THIS_TYPE, + possible_wanted_items = list(/mob/living/simple_animal/friendly/corgi = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/cat = TRADER_THIS_TYPE, /mob/living/simple_animal/crab = TRADER_THIS_TYPE, - /mob/living/simple_animal/lizard = TRADER_THIS_TYPE, - /mob/living/simple_animal/mouse = TRADER_THIS_TYPE, - /mob/living/simple_animal/mushroom = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/lizard = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/mouse = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/mushroom = TRADER_THIS_TYPE, /mob/living/simple_animal/tindalos = TRADER_THIS_TYPE, - /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, - /mob/living/simple_animal/cow = TRADER_THIS_TYPE, - /mob/living/simple_animal/chick = TRADER_THIS_TYPE, - /mob/living/simple_animal/chicken = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/tomato = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/cow = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/chick = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/chicken = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/beast/diyaab = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/bear= TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/beast/shantak= TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/parrot = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/beast/samak= TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/goat = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/carp = TRADER_THIS_TYPE) - possible_trading_items = list(/mob/living/simple_animal/corgi = TRADER_THIS_TYPE, - /mob/living/simple_animal/cat = TRADER_THIS_TYPE, + possible_trading_items = list(/mob/living/simple_animal/friendly/corgi = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/cat = TRADER_THIS_TYPE, /mob/living/simple_animal/crab = TRADER_THIS_TYPE, - /mob/living/simple_animal/lizard = TRADER_THIS_TYPE, - /mob/living/simple_animal/mouse = TRADER_THIS_TYPE, - /mob/living/simple_animal/mushroom = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/lizard = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/mouse = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/mushroom = TRADER_THIS_TYPE, /mob/living/simple_animal/tindalos = TRADER_THIS_TYPE, - /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, - /mob/living/simple_animal/cow = TRADER_THIS_TYPE, - /mob/living/simple_animal/chick = TRADER_THIS_TYPE, - /mob/living/simple_animal/chicken = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/tomato = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/cow = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/chick = TRADER_THIS_TYPE, + /mob/living/simple_animal/friendly/chicken = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/beast/diyaab = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/bear= TRADER_THIS_TYPE, diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 6bb3db28180..72421d6f2b1 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -147,7 +147,7 @@ if(istype(W, /obj/item/implant)) qdel(W) - var/mob/living/new_mob = new /mob/living/simple_animal/corgi(A.loc) + var/mob/living/new_mob = new /mob/living/simple_animal/friendly/corgi(A.loc) new_mob.a_intent = I_HURT if(M.mind) M.mind.transfer_to(new_mob) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 932db45ce7c..6f222ae0881 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -239,8 +239,9 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ else ai_text = stars(text) if(isanimal(M) && !M.universal_speak) - var/mob/living/simple_animal/SA = M - ai_text = pick(SA.speak) + var/datum/say_list/SA = M.say_list + if (SA) + ai_text = pick(SA.speak) var/name_used = M.GetVoice() //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. var/short_links = master.get_preference_value(/datum/client_preference/ghost_follow_link_length) == GLOB.PREF_SHORT @@ -250,8 +251,9 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ var/name_used = M.GetVoice() var/message if(isanimal(M) && !M.universal_speak) - var/mob/living/simple_animal/SA = M - message = get_hear_message(name_used, pick(SA.speak), verb, speaking) + var/datum/say_list/SA = M.say_list + if (SA) + message = get_hear_message(name_used, pick(SA.speak), verb, speaking) else message = get_hear_message(name_used, text, verb, speaking) if(targetpad && !targetpad.incoming_connection) //If this is the pad you're making the call from and the call is accepted diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index ee0e6711d63..d3c654bc477 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -129,10 +129,10 @@ var/shift_range = 6 var/castes = list(/mob/living/simple_animal/hostile/giant_spider = 2, - /mob/living/simple_animal/hostile/giant_spider/guard = 2, - /mob/living/simple_animal/hostile/giant_spider/nurse = 2, - /mob/living/simple_animal/hostile/giant_spider/spitter = 2, - /mob/living/simple_animal/hostile/giant_spider/hunter = 1) + /mob/living/simple_animal/hostile/giant_spider/guard = 2, + /mob/living/simple_animal/hostile/giant_spider/nurse = 2, + /mob/living/simple_animal/hostile/giant_spider/spitter = 2, + /mob/living/simple_animal/hostile/giant_spider/hunter = 1) /obj/effect/spider/spiderling/Initialize(var/mapload, var/atom/parent) greater_form = pickweight(castes) diff --git a/code/game/objects/items/devices/dociler.dm b/code/game/objects/items/devices/dociler.dm index c553e462abf..e01a82366de 100644 --- a/code/game/objects/items/devices/dociler.dm +++ b/code/game/objects/items/devices/dociler.dm @@ -42,7 +42,7 @@ L.faction = null if(istype(L,/mob/living/simple_animal/hostile)) var/mob/living/simple_animal/hostile/H = L - H.LoseTarget() + H.ai_holder.lose_target() H.attack_same = 0 H.friends += weakref(user) L.desc += "
    It looks especially docile." diff --git a/code/game/objects/items/glassjar.dm b/code/game/objects/items/glassjar.dm index 32314e0ffb8..71fb4e5e991 100644 --- a/code/game/objects/items/glassjar.dm +++ b/code/game/objects/items/glassjar.dm @@ -7,8 +7,8 @@ matter = list(MATERIAL_GLASS = 200) item_flags = ITEM_FLAG_NO_BLUDGEON var/list/accept_mobs = list( - /mob/living/simple_animal/lizard, - /mob/living/simple_animal/mouse, + /mob/living/simple_animal/friendly/lizard, + /mob/living/simple_animal/friendly/mouse, /mob/living/simple_animal/borer ) var/contains = 0 // 0 = nothing, 1 = money, 2 = animal, 3 = spiderling diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 6d5f6ccd87d..6604dc09bd6 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -179,3 +179,9 @@ //For things to apply special effects after damaging an organ, called by organ's take_damage /obj/proc/after_wounding(obj/item/organ/external/organ, datum/wound) + +/** + * Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. + */ +/obj/proc/is_safe_to_step(mob/living/L) + return TRUE diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index f042bab12a8..4ea3acb021a 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -433,8 +433,8 @@ obj/random/closet //A couple of random closets to spice up maint icon_state = "base" var/vermin_chance = 5 var/list/locker_vermin = list( - /mob/living/simple_animal/mouse, - /mob/living/simple_animal/opossum, + /mob/living/simple_animal/friendly/mouse, + /mob/living/simple_animal/friendly/opossum, /mob/living/carbon/alien/diona ) diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index e44f4ca55e1..fe6c29c3e15 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -50,11 +50,11 @@ /obj/structure/largecrate/animal/corgi name = "corgi carrier" - held_type = /mob/living/simple_animal/corgi + held_type = /mob/living/simple_animal/friendly/corgi /obj/structure/largecrate/animal/cow name = "cow crate" - held_type = /mob/living/simple_animal/cow + held_type = /mob/living/simple_animal/friendly/cow /obj/structure/largecrate/animal/goat name = "goat crate" @@ -66,12 +66,12 @@ /obj/structure/largecrate/animal/cat name = "cat carrier" - held_type = /mob/living/simple_animal/cat + held_type = /mob/living/simple_animal/friendly/cat /obj/structure/largecrate/animal/cat/bones - held_type = /mob/living/simple_animal/cat/fluff/bones + held_type = /mob/living/simple_animal/friendly/cat/fluff/bones /obj/structure/largecrate/animal/chick name = "chicken crate" held_count = 5 - held_type = /mob/living/simple_animal/chick + held_type = /mob/living/simple_animal/friendly/chick diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index d0670cb874d..9de807b8f27 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -13,7 +13,7 @@ var/list/mobs_can_pass = list( /mob/living/bot, /mob/living/carbon/slime, - /mob/living/simple_animal/mouse, + /mob/living/simple_animal/friendly/mouse, /mob/living/silicon/robot/drone ) var/airtight = 0 @@ -79,5 +79,5 @@ T.blocks_air = 0 -/obj/structure/plasticflaps/airtight // airtight defaults to on +/obj/structure/plasticflaps/airtight // airtight defaults to on airtight = 1 diff --git a/code/game/turfs/initialization/maintenance.dm b/code/game/turfs/initialization/maintenance.dm index 57e690da9c7..8393455d27d 100644 --- a/code/game/turfs/initialization/maintenance.dm +++ b/code/game/turfs/initialization/maintenance.dm @@ -39,9 +39,9 @@ if(prob(vermin_probability)) if(prob(80)) - new /mob/living/simple_animal/mouse(T) + new /mob/living/simple_animal/friendly/mouse(T) else - new /mob/living/simple_animal/lizard(T) + new /mob/living/simple_animal/friendly/lizard(T) if(prob(web_probability)) // Keep in mind that only "corners" get any sort of web attempt_web(T, cardinal_turfs) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index e6171399d3f..7b0d2f7436f 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -36,6 +36,9 @@ var/tmp/changing_turf + /// List of 'dangerous' objs that the turf holds that can cause something bad to happen when stepped on, used for AI mobs. + var/list/dangerous_objects + /turf/Initialize(mapload, ...) . = ..() if(dynamic_lighting) @@ -310,7 +313,7 @@ var/const/enterloopsanity = 100 var/intial_dir = TT.init_dir spawn(2) step(AM, turn(intial_dir, 180)) - + /turf/proc/can_engrave() return FALSE @@ -370,4 +373,32 @@ var/const/enterloopsanity = 100 for (var/thing in src) var/atom/movable/AM = thing if (AM.simulated && AM.blocks_airlock()) - LAZYADD(., AM) \ No newline at end of file + LAZYADD(., AM) + +/** + * Returns false if stepping into a tile would cause harm (e.g. open space while unable to fly, water tile while a slime, lava, etc). + */ +/turf/proc/is_safe_to_enter(mob/living/L) + if(LAZYLEN(dangerous_objects)) + for(var/obj/O in dangerous_objects) + if(!O.is_safe_to_step(L)) + return FALSE + return TRUE + +/** + * Tells the turf that it currently contains something that automated movement should consider if planning to enter the tile. + * This uses lazy list macros to reduce memory footprint since for 99% of turfs the list would've been empty anyway. + */ +/turf/proc/register_dangerous_object(obj/O) + if(!istype(O)) + return FALSE + LAZYADD(dangerous_objects, O) + +/** + * Similar to `register_dangerous_object()`, for when the dangerous object stops being dangerous/gets deleted/moved/etc. + */ +/turf/proc/unregister_dangerous_object(obj/O) + if(!istype(O)) + return FALSE + LAZYREMOVE(dangerous_objects, O) + UNSETEMPTY(dangerous_objects) // This nulls the list var if it's empty. diff --git a/code/modules/admin/buildmode/ai.dm b/code/modules/admin/buildmode/ai.dm new file mode 100644 index 00000000000..6644a49369c --- /dev/null +++ b/code/modules/admin/buildmode/ai.dm @@ -0,0 +1,231 @@ +/datum/build_mode/ai + name = "AI Editor" + icon_state = "buildmode14" + var/list/selected_mobs = list() + var/list/overlayed_mobs = list() + var/copied_faction = null + var/icon/buildmode_hud = icon('icons/misc/buildmode.dmi') + var/help_text = {"\ + ***********************************************************
    \ + Left Mouse Button drag box + ctrl = Select only mobs in box
    \ + Left Mouse Button drag box + ctrl + shift = Select additional mobs in area
    \ + Left Mouse Button on non-mob = Deselect all mobs
    \ + Left Mouse Button on AI mob = Select/Deselect mob
    \ + Left Mouse Button + alt on AI mob = Toggle hostility on mob
    \ + Left Mouse Button + shift on AI mob = Toggle AI (also resets)
    \ + Left Mouse Button + ctrl on AI mob = Copy mob faction
    \ + Right Mouse Button + ctrl on any mob = Paste mob faction copied with Left Mouse Button + shift
    \ + Right Mouse Button on enemy mob = Command selected mobs to attack mob
    \ + Right Mouse Button on allied mob = Command selected mobs to follow mob
    \ + Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction
    \ + Note: The following also reset the mob's home position:
    \ + Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)
    \ + Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be inturrupted by enemies)
    \ + Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf
    \ + ***********************************************************
    +"} + +/datum/build_mode/ai/Help() + to_chat(user, SPAN_NOTICE(help_text)) + +/datum/build_mode/ai/Unselected() + . = ..() + + for (var/mob/M in selected_mobs) + deselect_AI_mob(M) + + for (var/mob/living/M in GLOB.living_mob_list_) + user.remove_client_image(M.ai_status_image) + +/datum/build_mode/ai/TimerEvent() + . = ..() + + for (var/mob/living/M in GLOB.living_mob_list_) + if (M.ai_status_image) + user.add_client_image(M.ai_status_image) + +/datum/build_mode/ai/OnClick(atom/A, list/pa) + if(pa["left"]) + if(isliving(A)) + var/mob/living/L = A + + // Pause/unpause AI + if(pa["shift"]) + var/stance = L.get_AI_stance() + if(!isnull(stance)) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. + var/datum/ai_holder/AI = L.ai_holder + if(stance == STANCE_SLEEP) + AI.go_wake() + to_chat(user, SPAN_NOTICE("\The [L]'s AI has been enabled.")) + else + AI.go_sleep() + to_chat(user, SPAN_NOTICE("\The [L]'s AI has been disabled.")) + + toggle_ai_status(L) + return + else + to_chat(user, SPAN_WARNING("\The [L] is not AI controlled.")) + return + + // Toggle hostility + if(pa["alt"]) + if(!isnull(L.get_AI_stance())) + var/datum/ai_holder/AI = L.ai_holder + AI.hostile = !AI.hostile + to_chat(user, SPAN_NOTICE("\The [L] is now [AI.hostile ? "hostile" : "passive"].")) + else + to_chat(user, SPAN_WARNING("\The [L] is not AI controlled.")) + return + + // Copy faction + if(pa["ctrl"]) + copied_faction = L.faction + to_chat(user, SPAN_NOTICE("Copied faction '[copied_faction]'.")) + return + + // Select/Deselect + if(!isnull(L.get_AI_stance())) + if(L in selected_mobs) + deselect_AI_mob(L) + to_chat(user, SPAN_NOTICE("Deselected \the [L].")) + else + select_AI_mob(L) + to_chat(user, SPAN_NOTICE("Selected \the [L].")) + return + else + to_chat(user, SPAN_WARNING("\The [L] is not AI controlled.")) + return + else //Not living + for(var/mob/living/unit in selected_mobs) + deselect_AI_mob(unit) + + + if(pa["right"]) + // Paste faction + if(pa["ctrl"] && isliving(A)) + if(!copied_faction) + to_chat(user, SPAN_WARNING("LMB+Shift a mob to copy their faction before pasting.")) + return + else + var/mob/living/L = A + L.faction = copied_faction + to_chat(user, SPAN_NOTICE("Pasted faction '[copied_faction]'.")) + return + + if(istype(A, /atom)) // Force attack. + if(pa["alt"]) + var/i = 0 + for(var/mob/living/unit in selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.give_target(A) + i++ + to_chat(user, SPAN_NOTICE("Commanded [i] mob\s to attack \the [A].")) + var/image/orderimage = image(buildmode_hud,A,"ai_targetorder") + // orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + + if(isliving(A)) // Follow or attack. + var/mob/living/L = A + var/i = 0 // Attacking mobs. + var/j = 0 // Following mobs. + for(var/mob/living/unit in selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + if(L.IIsAlly(unit) || !AI.hostile || pa["shift"]) + AI.set_follow(L) + j++ + else + AI.give_target(L) + i++ + var/message = "Commanded " + if(i) + message += "[i] mob\s to attack \the [L]" + if(j) + message += ", and " + else + message += "." + if(j) + message += "[j] mob\s to follow \the [L]." + to_chat(user, SPAN_NOTICE(message)) + var/image/orderimage = image(buildmode_hud,L,"ai_targetorder") + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + + var/turf/T = get_turf(A) + if(isturf(T)) // Move or reposition. + var/forced = 0 + var/told = 0 + for(var/mob/living/unit in selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.home_turf = T + if(unit.get_AI_stance() == STANCE_SLEEP) + unit.forceMove(T) + forced++ + else + AI.give_destination(T, 0, pa["shift"]) // If shift is held, the mobs will not stop moving to attack a visible enemy. + told++ + to_chat(user, SPAN_NOTICE("Commanded [told] mob\s to move to \the [T], and manually placed [forced] of them.")) + var/image/orderimage = image(buildmode_hud,T,"ai_turforder") + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + +/datum/build_mode/ai/proc/select_AI_mob(mob/living/unit) + selected_mobs += unit + user.client.images += unit.selected_image + +/datum/build_mode/ai/proc/deselect_AI_mob(mob/living/unit) + selected_mobs -= unit + user.client.images -= unit.selected_image + +/datum/build_mode/ai/proc/toggle_ai_status(mob/living/unit) + + if (unit.ai_status_image) + user.remove_client_image(unit.ai_status_image) + QDEL_NULL(unit.ai_status_image) + + if (unit.has_AI()) + unit.ai_status_image = image('icons/misc/buildmode.dmi', unit, "ai_0") + user.add_client_image(unit.ai_status_image) + else + unit.ai_status_image = image('icons/misc/buildmode.dmi', unit, "ai_1") + user.add_client_image(unit.ai_status_image) + +/proc/build_drag(var/client/user, buildmode, var/atom/fromatom, var/atom/toatom, var/atom/fromloc, var/atom/toloc, var/fromcontrol, var/tocontrol, params) + if (!istype(buildmode, /datum/build_mode/ai)) + return + + var/datum/build_mode/ai/holder = buildmode + for(var/datum/build_mode/ai/H) + if(H.user == user) + holder = H + break + if(!holder) return + var/list/pa = params2list(params) + if (pa["ctrl"]) + //Holding shift prevents the deselection of existing + if(!pa["shift"]) + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(unit) + + var/turf/c1 = get_turf(fromatom) + var/turf/c2 = get_turf(toatom) + if(!c1 || !c2) + return //Dragged outside window or something + + var/low_x = min(c1.x,c2.x) + var/low_y = min(c1.y,c2.y) + var/hi_x = max(c1.x,c2.x) + var/hi_y = max(c1.y,c2.y) + var/z = c1.z //Eh + + var/i = 0 + for(var/mob/living/L in GLOB.living_mob_list_) + if(L.z != z || L.client) + continue + if(L.x >= low_x && L.x <= hi_x && L.y >= low_y && L.y <= hi_y) + if (L.ai_holder) + holder.select_AI_mob(L) + i++ + + to_chat(user, SPAN_NOTICE("Band-selected [i] mobs.")) + return diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index cd85ce79962..24f5a673897 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -237,10 +237,10 @@ if("slime") M.change_mob_type( /mob/living/carbon/slime , null, null, delmob ) if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob ) if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) - if("cat") M.change_mob_type( /mob/living/simple_animal/cat , null, null, delmob ) - if("runtime") M.change_mob_type( /mob/living/simple_animal/cat/fluff/Runtime , null, null, delmob ) - if("corgi") M.change_mob_type( /mob/living/simple_animal/corgi , null, null, delmob ) - if("ian") M.change_mob_type( /mob/living/simple_animal/corgi/Ian , null, null, delmob ) + if("cat") M.change_mob_type( /mob/living/simple_animal/friendly/cat , null, null, delmob ) + if("runtime") M.change_mob_type( /mob/living/simple_animal/friendly/cat/fluff/Runtime , null, null, delmob ) + if("corgi") M.change_mob_type( /mob/living/simple_animal/friendly/corgi , null, null, delmob ) + if("ian") M.change_mob_type( /mob/living/simple_animal/friendly/corgi/Ian , null, null, delmob ) if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob ) if("coffee") M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob ) if("parrot") M.change_mob_type( /mob/living/simple_animal/hostile/retaliate/parrot , null, null, delmob ) diff --git a/code/modules/admin/view_variables/helpers.dm b/code/modules/admin/view_variables/helpers.dm index 3b57e01049b..f808e2d4f99 100644 --- a/code/modules/admin/view_variables/helpers.dm +++ b/code/modules/admin/view_variables/helpers.dm @@ -67,6 +67,7 @@ return ..() + {" + "} /mob/living/carbon/human/get_view_variables_options() diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 8a15e44638a..4c609844220 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -557,6 +557,13 @@ return log_and_message_admins("removed \the [choice] to \the [L]") qdel(choice) + + else if (href_list["debug_mob_ai"]) + if (!check_rights(R_DEBUG)) + return + var/mob/living/L = locate(href_list["debug_mob_ai"]) + log_debug("AI Debugging toggled [L.ai_holder.debug() ? "ON" : "OFF"] for \the [L]") + if(href_list["datumrefresh"]) var/datum/DAT = locate(href_list["datumrefresh"]) if(istype(DAT, /datum) || istype(DAT, /client)) diff --git a/code/modules/ai/__readme.dm b/code/modules/ai/__readme.dm new file mode 100644 index 00000000000..e2a0574baad --- /dev/null +++ b/code/modules/ai/__readme.dm @@ -0,0 +1,201 @@ +/* +[Summary] + +This module contains an AI implementation designed to be (at the base level) mobtype-agnostic, +by being held inside a datum instead of being written into the mob directly. More specialized +subtypes of the base AI may be designed with a specific mob type in mind, but the base system +should be compatible with most types of mobs which have the needed Interfaces in place to +support them. + +When designing a new mob, all that is needed to give a mob an AI is to set +its 'ai_holder_type' variable to the path of the AI that is desired. + + +[Seperation] + +In previous iterations of AI systems, the AI is generally written into the mob's code directly, +which has some advantages, but often makes the code rigid, and also tied the speed of the AI +to the mob's own ticker, meaning it could only decide every two seconds. + +Instead, this version has the code for the AI held inside an /datum/ai_holder object, +which is carried by the mob it controls. This gives some advantages; + All /mob/living mobs can potentially have an AI applied to them, and utilize the + same base code while adding specialized code on top. + + Interfaces allow the base AI code to not need to know what particular mode it's controlling. + + The processing of the AI is independant of the mob's Life() cycle, which allows for a + different clock rate. + + Seperating the AI from the mob simplies the mob's code greatly. + + It is more logical to think that a mob is the 'body', where as its ai_holder is + the 'mind'. + + AIs can be applied or disabled on the fly by instantiating or deleting the + ai_holder, if needed. + + +The current implementation also has some disadvantages, but they can perhaps be resolved +in the future. + AI-driven mob movement and attack speed is tied to half-second delays due to the + AI subsystem ticking at that rate. Porting the timer subsystem and integrating + callbacks into basic AI actions (moving, attacking) can potentially resolve that. + + It can be difficult to modify AI variables at mob instantiation without an ugly + delay, as the ai_holder might not exist yet. + + +[Flow of Processing] + +Terrible visual representation here; +AI Subsystem -> Every 0.5s -> /datum/ai_holder/handle_tactics() -> switch(stance)... + -> Every 2.0s -> /datum/ai_holder/handle_strategicals() -> switch(stance)... + +The AI datum is not processed by the mob itself, but instead it is directly processed +by a new AI subsystem. The AI subsystem contains a list of all active ai_holder +objects, which is iterated every tick to process each individual ai_holder +object attached to a mob. + +Each ai_holder actually has two 'tracks' for processing, a 'fast' track +and a 'slow' track. + +The fast track is named handle_tactics(), and is called every 0.5 seconds. + +The slow track is named handle_strategicals(), and is called every 2 seconds. + +When an ai_holder is iterated on inside the AI subsystem's list, it first +calls that ai_holder's handle_tactics(). It will then call that ai_holder's +handle_strategicals() every fourth tick, effectively doing so every two seconds. + +Both functions do different things depending on which 'stance' the +ai_holder is in. See the Stances section for more information. + +The fast track is for 'cheap' processing that needs to happen fast, such as +walking along a path, initiating an attack, or firing a gun. The rate that +it is called allows for the ai_holder to interact with the world through +its mob very often, giving a more convincing appearance of intelligence, +allowing for faster reaction times to certain events, and allowing for +variable attack speeds that would not be possible when bound to a +two second Life() cycle. + +The slow track, on the other hand, is for 'expensive' processing that might +be too demanding on the CPU to do every half a second, such as +re/calculating an A* path (if the mob uses A*), or running a complicated +tension assessment to determine how brave the mob is feeling. This is the +same delay used for certain tasks in the old implementation, but it is less +noticable due to the mob appearing to do things inbetween those two seconds. + +The purpose of having two tracks is to allow for 'fast' and 'slow' actions +to be more easily encapsulated, and ensures that all ai_holders are syncronized +with each other, as opposed to having individual tick counters inside all of +the ai_holder instances. It should be noted that handle_tactics() is always +called first, before handle_strategicals() every two seconds. + +[Process Skipping] + +An ai_holder object can choose to enter a 'busy' state, or a 'sleep' state, +in order to avoid processing. + +When busy, the AI subsystem will skip over the ai_holder until it is no +longer busy. The busy state is intended to be short-term, and is usually +toggled by the mob when doing something with a delay, so that the ai_holder +does not accidentally do something to inturrupt something important, like +a special attack. + +The longer term alternative to the busy state is the sleep state. Unlike +being busy, an ai_holder set to sleep will remove itself from the +AI subsystem's list, meaning it will no longer process until something +else 'wakes' it. This is usually done when the mob dies or a client +logs into an AI-controlled mob (and the AI is not set to ignore that, +with the autopilot variable). If the mob is revived, the AI will be +awakened automatically. + +The ai_holder functions, and mob functions that are called by the +ai_holder, should not be sleep()ed, as it will block the AI Subsystem +from processing the other ai_holders until the sleep() finishes. +Delays on the mob typically have set waitfor = FALSE, or spawn() is used. + + +[Stances] + +The AI has a large number of states that it can be in, called stances. +The AI will act in a specific way depending on which stance it is in, +and only one stance can be active at a time. This effectively creates +a state pattern. + +To change the stance, set_stance() is used, with the new stance as +the first argument. It should be noted that the change is not immediate, +and it will react to the change next tick instead of immediately switching +to the new stance and acting on that in the same tick. This is done to help +avoid infinite loops (I.E. Stance A switches to Stance B, which then +switches to Stance A, and so on...), and the delay is very short so +it should not be an issue. + +See code/__defines/mob.dm for a list of stance defines, and descriptions +about their purpose. Generally, each stance has its own file in the AI +module folder and are mostly self contained, however some files instead +deal with general things that other stances may require, such as targeting +or movement. + +[Interfaces] + +Interfaces are a concept that is used to help bridge the gap between +the ai_holder, and its mob. Because the (base) ai_holder is explicitly +designed to not be specific to any type of mob, all that it knows is +that it is controlling a /mob/living mob. Some mobs work very differently, +between mob types such as /mob/living/simple_animal, /mob/living/silicon/robot, +/mob/living/carbon/human, and more. + +The solution to the vast differences between mob types is to have the +mob itself deal with how to handle a specific task, such as attacking +something, talking, moving, etc. Interfaces exist to do this. + +Interfaces are applied on the mob-side, and are generally specific to +that mob type. This lets the ai_holder not have to worry about specific +implementations and instead just tell the Interface that it wants to attack +something, or move into a tile. The AI does not need to know if the mob its +controlling has hands, instead that is the mob's responsibility. + +Interface functions have an uppercase I at the start of the function name, +and then the function they are bridging between the AI and the mob +(if it exists), e.g. IMove(), IAttack(), ISay(). + +Interfaces are also used for the AI to ask its mob if it can do certain +things, without having to actually know what type of mob it is attached to. +For example, ICheckRangedAttack() tells the AI if it is possible to do a +ranged attack. For simple_mobs, they can if a ranged projectile type was set, +where as for a human mob, it could check if a gun is in a hand. For a borg, +it could check if a gun is inside their current module. + +[Say List] + +A /datum/say_list is a very light datum that holds a list of strings for the +AI to have their mob say based on certain conditions, such as when threatening +to kill another mob. Despite the name, a say_list also can contain emotes +and some sounds. + +The reason that it is in a seperate datum is to allow for multiple mob types +to have the same text, even when inheritence cannot do that, such as +mercenaries and fake piloted mecha mobs. + +The say_list datum is applied to the mob itself and not held inside the AI datum. + +[Subtypes] + +Some subtypes of ai_holder are more specialized, but remain compatible with +most mob types. There are many different subtypes that make the AI act different +by overriding a function, such as kiting their target, moving up close while +using ranged attacks, or running away if not cloaked. + +Other subtypes are very specific about what kind of mob it controls, and trying +to apply them to a different type of mob will likely result in a lot of bugs +or ASSERT() failures. The xenobio slime AI is an example of the latter. + +To use a specific subtype on a mob, all that is needed is setting the mob's +ai_holder_type to the subtype desired, and it will create that subtype when +the mob is initialize()d. Switching to a subtype 'live' will require additional +effort on the coder. + + +*/ \ No newline at end of file diff --git a/code/modules/ai/_defines.dm b/code/modules/ai/_defines.dm new file mode 100644 index 00000000000..7b49f18ddb2 --- /dev/null +++ b/code/modules/ai/_defines.dm @@ -0,0 +1,34 @@ +// Defines for the ai_intelligence var. +// Controls if the mob will do 'advanced tactics' like running from grenades. +#define AI_DUMB 1 // Be dumber than usual. +#define AI_NORMAL 2 // Default level. +#define AI_SMART 3 // Will do more processing to be a little smarter, like not walking while confused if it could risk stepping randomly onto a bad tile. + +#define ai_log(M,V) if(debug_ai) ai_log_output(M,V) + +// Logging level defines. +#define AI_LOG_OFF 0 // Don't show anything. +#define AI_LOG_ERROR 1 // Show logs of things likely causing the mob to not be functioning correctly. +#define AI_LOG_WARNING 2 // Show less serious but still helpful to know issues that might be causing things to work incorrectly. +#define AI_LOG_INFO 3 // Important regular events, like selecting a target or switching stances. +#define AI_LOG_DEBUG 4 // More detailed information about the flow of execution. +#define AI_LOG_TRACE 5 // Even more detailed than the last. Will absolutely flood your chatlog. + +// Results of pre-movement checks. +// Todo: Move outside AI code? +#define MOVEMENT_ON_COOLDOWN -1 // Recently moved and needs to try again soon. +#define MOVEMENT_FAILED 0 // Move() returned false for whatever reason and the mob didn't move. +#define MOVEMENT_SUCCESSFUL 1 // Move() returned true and the mob hopefully moved. + +// Results of pre-attack checks +#define ATTACK_ON_COOLDOWN -1 // Recently attacked and needs to try again soon. +#define ATTACK_FAILED 0 // Something else went wrong! Maybe they moved away! +#define ATTACK_SUCCESSFUL 1 // We attacked (or tried to, misses count too) + +// Reasons for targets to not be valid. Based on why, the AI responds differently. +#define AI_TARGET_VALID 0 // We can fight them. +#define AI_TARGET_INVIS 1 // They were in field of view but became invisible. Switch to STANCE_BLINDFIGHT if no other viable targets exist. +#define AI_TARGET_NOSIGHT 2 // No longer in field of view. Go STANCE_REPOSITION to their last known location if no other targets are seen. +#define AI_TARGET_ALLY 3 // They are an ally. Find a new target. +#define AI_TARGET_DEAD 4 // They're dead. Find a new target. +#define AI_TARGET_INVINCIBLE 5 // Target is currently unable to receive damage for whatever reason. Find a new target or wait. diff --git a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm new file mode 100644 index 00000000000..a4340e28949 --- /dev/null +++ b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm @@ -0,0 +1,189 @@ +// Base AIs for simple mobs. +// Mob-specific AIs are in their mob's file. + +/datum/ai_holder/simple_animal + hostile = TRUE // The majority of simplemobs are hostile. + retaliate = TRUE // The majority of simplemobs will fight back. + cooperative = TRUE + returns_home = FALSE + can_flee = FALSE + speak_chance = 1 // If the mob's saylist is empty, nothing will happen. + wander = TRUE + base_wander_delay = 4 + +// For non-hostile animals, and pets like Ian and Runtime. +/datum/ai_holder/simple_animal/passive + hostile = FALSE + retaliate = FALSE + can_flee = TRUE + violent_breakthrough = FALSE + +// Won't wander away as quickly, ideal for event-spawned mobs like carp or drones. +/datum/ai_holder/simple_animal/event + base_wander_delay = 8 + +// Will keep the mob within a limited radius of its home, useful for guarding an area +/datum/ai_holder/simple_animal/guard + returns_home = TRUE + +// Won't return home while it's busy doing something else, like chasing a player +/datum/ai_holder/simple_animal/guard/give_chase + home_low_priority = TRUE + +// Doesn't really act until told to by something on the outside. +/datum/ai_holder/simple_animal/inert + hostile = FALSE + retaliate = FALSE + can_flee = FALSE + wander = FALSE + speak_chance = 0 + cooperative = FALSE + violent_breakthrough = FALSE // So it can open doors but not attack windows and shatter the literal illusion. + +/datum/ai_holder/simple_animal/stationary + wander = FALSE +// Ranged mobs. + +/datum/ai_holder/simple_animal/ranged + +// Tries to not waste ammo. +/datum/ai_holder/simple_animal/ranged/careful + conserve_ammo = TRUE + +/datum/ai_holder/simple_animal/ranged/pointblank + pointblank = TRUE + +// Runs away from its target if within a certain distance. +/datum/ai_holder/simple_animal/ranged/kiting + pointblank = TRUE // So we don't need to copypaste post_melee_attack(). + /// If anything gets within this range, it'll try to move away. + var/run_if_this_close = 4 + /// If true, mob turns to face the target while kiting, otherwise they turn in the direction they moved towards. + var/moonwalk = TRUE + +/datum/ai_holder/simple_animal/ranged/kiting/threatening + threaten = TRUE + threaten_delay = 1 SECOND // Less of a threat and more of pre-attack notice. + threaten_timeout = 30 SECONDS + conserve_ammo = TRUE + +// For event-spawned malf drones. +/datum/ai_holder/simple_animal/ranged/kiting/threatening/event + base_wander_delay = 8 + +/datum/ai_holder/simple_animal/ranged/kiting/no_moonwalk + moonwalk = FALSE + +/datum/ai_holder/simple_animal/ranged/kiting/on_engagement(atom/A) + if(get_dist(holder, A) < run_if_this_close) + holder.IMove(get_step_away(holder, A, run_if_this_close)) + if(moonwalk) + holder.face_atom(A) + +// Closes distance from the target even while in range. +/datum/ai_holder/simple_animal/ranged/aggressive + pointblank = TRUE + /// How close to get to the target. By default they will get into melee range (and then pointblank them). + var/closest_distance = 1 + +/datum/ai_holder/simple_animal/ranged/aggressive/on_engagement(atom/A) + if(get_dist(holder, A) > closest_distance) + holder.IMove(get_step_towards(holder, A)) + holder.face_atom(A) + +// Yakkity saxes while firing at you. +/datum/ai_holder/hostile/ranged/robust/on_engagement(atom/movable/AM) + step_rand(holder) + holder.face_atom(AM) + +// Switches intents based on specific criteria. +// Used for special mobs who do different things based on intents (and aren't slimes). +// Intent switching is generally done in pre_[ranged/special]_attack(), so that the mob can use the right attack for the right time. +/datum/ai_holder/simple_animal/intentional + + +// These try to avoid collateral damage. +/datum/ai_holder/simple_animal/restrained + violent_breakthrough = FALSE + conserve_ammo = TRUE + destructive = FALSE + +// This does the opposite of the above subtype. +/datum/ai_holder/simple_animal/destructive + destructive = TRUE + +// Melee mobs. + +/datum/ai_holder/simple_animal/melee + +// Dances around the enemy its fighting, making it harder to fight back. +/datum/ai_holder/simple_animal/melee/evasive + +/datum/ai_holder/simple_animal/melee/evasive/post_melee_attack(atom/A) + if(holder.Adjacent(A)) + holder.IMove(get_step(holder, pick(GLOB.alldirs))) + holder.face_atom(A) + + + +// This AI hits something, then runs away for awhile. +// It will (almost) always flee if they are uncloaked, AND their target is not stunned. +/datum/ai_holder/simple_animal/melee/hit_and_run + can_flee = TRUE + +// Used for the 'running' part of hit and run. +/datum/ai_holder/simple_animal/melee/hit_and_run/special_flee_check() + if(!holder.is_cloaked()) + if(isliving(target)) + var/mob/living/L = target + return !L.incapacitated(INCAPACITATION_DISABLED) // Don't flee if our target is stunned in some form, even if uncloaked. This is so the mob keeps attacking a stunned opponent. + return TRUE // We're out in the open, uncloaked, and our target isn't stunned, so lets flee. + return FALSE + + +// Simple mobs that aren't hostile, but will fight back. +/datum/ai_holder/simple_animal/retaliate + hostile = FALSE + retaliate = TRUE + +// Simple mobs that retaliate and support others in their faction who get attacked. +/datum/ai_holder/simple_animal/retaliate/cooperative + cooperative = TRUE + +// With all the bells and whistles +/datum/ai_holder/simple_animal/humanoid + intelligence_level = AI_SMART //Purportedly + retaliate = TRUE //If attacked, attack back + threaten = TRUE //Verbal threats + firing_lanes = TRUE //Avoid shooting allies + conserve_ammo = TRUE //Don't shoot when it can't hit target + can_breakthrough = TRUE //Can break through doors + violent_breakthrough = FALSE //Won't try to break through walls (humans can, but usually don't) + speak_chance = 2 //Babble chance + cooperative = TRUE //Assist each other + wander = TRUE //Wander around + returns_home = TRUE //But not too far + use_astar = TRUE //Path smartly + home_low_priority = TRUE //Following/helping is more important + +// The hostile subtype is implied to be trained combatants who use ""tactics"" +/datum/ai_holder/simple_animal/humanoid/hostile + /// If anything gets within this range, it'll try to move away. + var/run_if_this_close = 4 + hostile = TRUE //Attack! + +// Juke +/datum/ai_holder/simple_animal/humanoid/hostile/post_melee_attack(atom/A) + holder.IMove(get_step(holder, pick(GLOB.alldirs))) + holder.face_atom(A) + +/datum/ai_holder/simple_animal/humanoid/hostile/post_ranged_attack(atom/A) + //Pick a random turf to step into + var/turf/T = get_step(holder, pick(GLOB.alldirs)) + if(check_trajectory(A, T)) // Can we even hit them from there? + holder.IMove(T) + holder.face_atom(A) + + if(get_dist(holder, A) < run_if_this_close) + holder.IMove(get_step_away(holder, A)) + holder.face_atom(A) diff --git a/code/modules/ai/ai_holder.dm b/code/modules/ai/ai_holder.dm new file mode 100644 index 00000000000..6dc7fe22c73 --- /dev/null +++ b/code/modules/ai/ai_holder.dm @@ -0,0 +1,368 @@ +// This is a datum-based artificial intelligence for simple mobs (and possibly others) to use. +// The neat thing with having this here instead of on the mob is that it is independant of Life(), and that different mobs +// can use a more or less complex AI by giving it a different datum. +#define AI_NO_PROCESS 0 +#define AI_PROCESSING (1<<0) +#define AI_FASTPROCESSING (1<<1) + +#define START_AIPROCESSING(Datum) if (!(Datum.process_flags & AI_PROCESSING)) {Datum.process_flags |= AI_PROCESSING;SSai.processing += Datum} +#define STOP_AIPROCESSING(Datum) Datum.process_flags &= ~AI_PROCESSING;SSai.processing -= Datum +#define START_AIFASTPROCESSING(Datum) if (!(Datum.process_flags & AI_FASTPROCESSING)) {Datum.process_flags |= AI_FASTPROCESSING;SSaifast.processing += Datum} +#define STOP_AIFASTPROCESSING(Datum) Datum.process_flags &= ~AI_FASTPROCESSING;SSaifast.processing -= Datum + +/mob/living + var/datum/ai_holder/ai_holder = null + /// Which `ai_holder` datum to give to the mob when initialized. If `null`, nothing happens. + var/ai_holder_type = null + var/image/ai_status_image + +/mob/living/Initialize() + if (ai_holder_type) + ai_holder = new ai_holder_type(src) + if (istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + H.InitializeHud() + ai_status_image = image('icons/misc/buildmode.dmi', src, "ai_0") + return ..() + +/mob/living/Destroy() + if (ai_holder) + GLOB.stat_set_event.unregister(src, ai_holder, /datum/ai_holder/proc/holder_stat_change) + QDEL_NULL(ai_holder) + + return ..() + +/mob/living/Login() + if (!stat && ai_holder) + ai_holder.manage_processing(AI_NO_PROCESS) + return ..() + +/mob/living/Logout() + if (!stat && !key && ai_holder) + ai_holder.manage_processing(AI_PROCESSING) + return ..() + +/datum/ai_holder + /// The mob this datum is going to control. + var/mob/living/simple_animal/holder = null + /// Determines if the mob should be doing a specific thing, e.g. attacking, following, standing around, etc. + var/stance = STANCE_IDLE + /// Adjust to make the AI be intentionally dumber, or make it more robust (e.g. dodging grenades). + var/intelligence_level = AI_NORMAL + /// If true, the AI won't be deactivated if a client gets attached to the AI's mob. + var/autopilot = FALSE + /** + * If true, the ticker will skip processing this mob until this is false. Good for if you need the + * mob to stay still (e.g. delayed attacking). If you need the mob to be inactive for an extended period of time, + * consider sleeping the AI instead. + */ + var/busy = FALSE + /// Where we're processing, see flag defines. + var/process_flags = 0 + /// A list used in mass-editing of AI datums, holding a snapshot of the 'before' state + var/list/snapshot = null + var/list/static/fastprocess_stances = list( + STANCE_ALERT, + STANCE_APPROACH, + STANCE_FIGHT, + STANCE_BLINDFIGHT, + STANCE_REPOSITION, + STANCE_MOVE, + STANCE_FOLLOW, + STANCE_FLEE, + STANCE_DISABLED + ) + var/list/static/noprocess_stances = list( + STANCE_SLEEP + ) + + +/datum/ai_holder/hostile + hostile = TRUE + +/datum/ai_holder/retaliate + hostile = TRUE + retaliate = TRUE + +/datum/ai_holder/New(new_holder) + ASSERT(new_holder) + holder = new_holder + home_turf = get_turf(holder) + manage_processing(AI_PROCESSING) + GLOB.stat_set_event.register(holder, src, .proc/holder_stat_change) + ..() + +/datum/ai_holder/Destroy() + holder = null + manage_processing(AI_NO_PROCESS) + home_turf = null + return ..() + +/datum/ai_holder/proc/manage_processing(desired) + if (desired & AI_PROCESSING) + START_AIPROCESSING(src) + else + STOP_AIPROCESSING(src) + + if (desired & AI_FASTPROCESSING) + START_AIFASTPROCESSING(src) + else + STOP_AIFASTPROCESSING(src) + +/datum/ai_holder/proc/holder_stat_change(mob, old_stat, new_stat) + if (old_stat >= DEAD && new_stat <= DEAD) //Revived + manage_processing(AI_PROCESSING) + else if (old_stat <= DEAD && new_stat >= DEAD) //Killed + manage_processing(AI_NO_PROCESS) + +// Now for the actual AI stuff. +/datum/ai_holder/proc/set_busy(value = FALSE) + busy = value + +/** + * Makes this ai holder not get processed. + * Called automatically when the host mob is killed. + * Potential future optimization would be to sleep AIs which mobs that are far away from in-round players. + */ +/datum/ai_holder/proc/go_sleep() + if (stance == STANCE_SLEEP) + return + forget_everything() // If we ever wake up, its really unlikely that our current memory will be of use. + set_stance(STANCE_SLEEP) + +/** + * Reverses the `go_sleep()` proc. + * Revived mobs will wake their AI if they have one. + */ +/datum/ai_holder/proc/go_wake() + if (stance != STANCE_SLEEP) + return + if (!should_wake()) + return + set_stance(STANCE_IDLE) + +/datum/ai_holder/proc/should_wake() + if (holder.client && !autopilot) + return FALSE + if (holder.stat >= DEAD) + return FALSE + return TRUE + +/// Resets a lot of 'memory' vars. +/datum/ai_holder/proc/forget_everything() + // Some of these might be redundant, but hopefully this prevents future bugs if that changes. + lose_follow() + remove_target() + +/// 'Tactical' processes such as moving a step, meleeing an enemy, firing a projectile, and other fairly cheap actions that need to happen quickly. +/datum/ai_holder/proc/handle_tactics() + if (holder.key && !autopilot) + return + handle_special_tactic() + handle_stance_tactical() + +/// 'Strategical' processes that are more expensive on the CPU and so don't get run as often as the above proc, such as A* pathfinding or robust targeting. +/datum/ai_holder/proc/handle_strategicals() + if (holder.key && !autopilot) + return + handle_special_strategical() + handle_stance_strategical() + +/// Override this for special things without polluting the main `handle_tactics()` loop. +/datum/ai_holder/proc/handle_special_tactic() + +/// Override this for special things without polluting the main `handle_strategicals()` loop. +/datum/ai_holder/proc/handle_special_strategical() + +/// For setting the stance WITHOUT processing it +/datum/ai_holder/proc/set_stance(new_stance) + if (holder.key && !autopilot) + return + if (stance == new_stance) + ai_log("set_stance() : Ignoring change stance to same stance request.", AI_LOG_INFO) + return + + ai_log("set_stance() : Setting stance from [stance] to [new_stance].", AI_LOG_INFO) + stance = new_stance + if (stance_coloring) // For debugging or really weird mobs. + stance_color() + // update_stance_hud() + + if (new_stance in fastprocess_stances) //Becoming fast + manage_processing(AI_PROCESSING|AI_FASTPROCESSING) + else if (new_stance in noprocess_stances) + manage_processing(AI_NO_PROCESS) //Becoming off + else + manage_processing(AI_PROCESSING) //Becoming slow + +/// This is called every half a second. +/datum/ai_holder/proc/handle_stance_tactical() + ai_log("========= Fast Process Beginning ==========", AI_LOG_TRACE) // This is to make it easier visually to disinguish between 'blocks' of what a tick did. + ai_log("handle_stance_tactical() : Called.", AI_LOG_TRACE) + + if (stance == STANCE_SLEEP) + ai_log("handle_stance_tactical() : Going to sleep.", AI_LOG_TRACE) + go_sleep() + return + + if (target && can_see_target(target)) + track_target_position() + + if (stance != STANCE_DISABLED && is_disabled()) // Stunned/confused/etc + ai_log("handle_stance_tactical() : Disabled.", AI_LOG_TRACE) + set_stance(STANCE_DISABLED) + return + + if (stance in STANCES_COMBAT) + // Should resist? We check this before fleeing so that we can actually flee and not be trapped in a chair. + if (holder.incapacitated(INCAPACITATION_BUCKLED_PARTIALLY)) + ai_log("handle_stance_tactical() : Going to handle_resist().", AI_LOG_TRACE) + handle_resist() + + else if (istype(holder.loc, /obj/structure/closet)) + var/obj/structure/closet/C = holder.loc + ai_log("handle_stance_tactical() : Inside a closet. Going to attempt escape.", AI_LOG_TRACE) + if (C.welded) + holder.resist() + else + C.open() + + // Should we flee? + if (should_flee()) + ai_log("handle_stance_tactical() : Going to flee.", AI_LOG_TRACE) + set_stance(STANCE_FLEE) + return + + switch(stance) + if (STANCE_ALERT) + ai_log("handle_stance_tactical() : STANCE_ALERT, going to threaten_target().", AI_LOG_TRACE) + threaten_target() + + if (STANCE_APPROACH) + ai_log("handle_stance_tactical() : STANCE_APPROACH, going to walk_to_target().", AI_LOG_TRACE) + walk_to_target() + + if (STANCE_FIGHT) + ai_log("handle_stance_tactical() : STANCE_FIGHT, going to engage_target().", AI_LOG_TRACE) + engage_target() + + if (STANCE_MOVE) + ai_log("handle_stance_tactical() : STANCE_MOVE, going to walk_to_destination().", AI_LOG_TRACE) + walk_to_destination() + + if (STANCE_REPOSITION) // This is the same as above but doesn't stop if an enemy is visible since its an 'in-combat' move order. + ai_log("handle_stance_tactical() : STANCE_REPOSITION, going to walk_to_destination().", AI_LOG_TRACE) + if (!target && !find_target()) + walk_to_destination() + + if (STANCE_FOLLOW) + ai_log("handle_stance_tactical() : STANCE_FOLLOW, going to walk_to_leader().", AI_LOG_TRACE) + walk_to_leader() + + if (STANCE_FLEE) + ai_log("handle_stance_tactical() : STANCE_FLEE, going to flee_from_target().", AI_LOG_TRACE) + flee_from_target() + + if (STANCE_DISABLED) + ai_log("handle_stance_tactical() : STANCE_DISABLED.", AI_LOG_TRACE) + if (!is_disabled()) + ai_log("handle_stance_tactical() : No longer disabled.", AI_LOG_TRACE) + set_stance(STANCE_IDLE) + else + handle_disabled() + + ai_log("handle_stance_tactical() : Exiting.", AI_LOG_TRACE) + ai_log("========= Fast Process Ending ==========", AI_LOG_TRACE) + +/// This is called every two seconds. +/datum/ai_holder/proc/handle_stance_strategical() + ai_log("++++++++++ Slow Process Beginning ++++++++++", AI_LOG_TRACE) + ai_log("handle_stance_strategical() : Called.", AI_LOG_TRACE) + + //We got left around for some reason. Goodbye cruel world. + if (!holder) + qdel(src) + + ai_log("handle_stance_strategical() : LTT=[lose_target_time]", AI_LOG_TRACE) + if (lose_target_time && (lose_target_time + lose_target_timeout < world.time)) // We were tracking an enemy but they are gone. + ai_log("handle_stance_strategical() : Giving up a chase.", AI_LOG_DEBUG) + remove_target() + + if (stance in STANCES_COMBAT) + request_help() // Call our allies. + + switch(stance) + if (STANCE_IDLE) + if (speak_chance) // In the long loop since otherwise it wont shut up. + handle_idle_speaking() + + if (hostile) + ai_log("handle_stance_strategical() : STANCE_IDLE, going to find_target().", AI_LOG_TRACE) + find_target() + + if (should_go_home()) + ai_log("handle_stance_tactical() : STANCE_IDLE, going to go home.", AI_LOG_TRACE) + go_home() + + else if (should_follow_leader()) + ai_log("handle_stance_tactical() : STANCE_IDLE, going to follow leader.", AI_LOG_TRACE) + set_stance(STANCE_FOLLOW) + + else if (should_wander()) + ai_log("handle_stance_tactical() : STANCE_IDLE, going to wander randomly.", AI_LOG_TRACE) + handle_wander_movement() + + if (STANCE_APPROACH) + if (target) + ai_log("handle_stance_strategical() : STANCE_APPROACH, going to calculate_path([target]).", AI_LOG_TRACE) + calculate_path(target) + walk_to_target() + if (STANCE_MOVE) + if (hostile && find_target()) // This will switch its stance. + ai_log("handle_stance_strategical() : STANCE_MOVE, found target and was inturrupted.", AI_LOG_TRACE) + if (STANCE_FOLLOW) + if (hostile && find_target()) // This will switch its stance. + ai_log("handle_stance_strategical() : STANCE_FOLLOW, found target and was inturrupted.", AI_LOG_TRACE) + else if (leader) + ai_log("handle_stance_strategical() : STANCE_FOLLOW, going to calculate_path([leader]).", AI_LOG_TRACE) + calculate_path(leader) + walk_to_leader() + + ai_log("handle_stance_strategical() : Exiting.", AI_LOG_TRACE) + ai_log("++++++++++ Slow Process Ending ++++++++++", AI_LOG_TRACE) + + +/// Helper proc to turn AI 'busy' mode on or off without having to check if there is an AI, to simplify writing code. +/mob/living/proc/set_AI_busy(value) + if (ai_holder) + ai_holder.set_busy(value) + +///Set an AI's busy status. Stops the mob from performing any actions while true. +/mob/living/proc/is_AI_busy() + if (!ai_holder) + return FALSE + return ai_holder.busy + +/** + * Helper proc to check for the AI's stance. + * Returns null if there's no AI holder, or the mob has a player and autopilot is not on. + * Otherwise returns the stance. + */ +/mob/living/proc/get_AI_stance() + if (!ai_holder) + return null + if (client && !ai_holder.autopilot) + return null + return ai_holder.stance + +/// Similar to `get_ai_stance()` but only returns 1 or 0. +/mob/living/proc/has_AI() + return get_AI_stance() ? TRUE : FALSE + +/// 'Taunts' the AI into attacking the taunter. +/mob/living/proc/taunt(atom/movable/taunter, force_target_switch = FALSE) + if (ai_holder) + ai_holder.receive_taunt(taunter, force_target_switch) + +#undef AI_PROCESSING +#undef AI_FASTPROCESSING diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm new file mode 100644 index 00000000000..b6aa6b824c8 --- /dev/null +++ b/code/modules/ai/ai_holder_combat.dm @@ -0,0 +1,323 @@ +// This file is for actual fighting. Targeting is in a seperate file. + +/datum/ai_holder + var/firing_lanes = TRUE // If true, tries to refrain from shooting allies or the wall. + var/conserve_ammo = FALSE // If true, the mob will avoid shooting anything that does not have a chance to hit a mob. Requires firing_lanes to be true. + var/pointblank = FALSE // If ranged is true, and this is true, people adjacent to the mob will suffer the ranged instead of using a melee attack. + + var/can_breakthrough = TRUE // If false, the AI will not try to open a path to its goal, like opening doors. + var/violent_breakthrough = TRUE // If false, the AI is not allowed to destroy things like windows or other structures in the way. Requires above var to be true. + + var/stand_ground = FALSE // If true, the AI won't try to get closer to an enemy if out of range. + + var/prying = FALSE // True when the mob is trying to force open a door. + + var/list/valid_obstacles_by_priority = list(/obj/structure/window, + /obj/structure/closet, + /obj/machinery/door/window, + /obj/structure/table, + /obj/structure/grille, + /obj/structure/barricade, + /obj/structure/wall_frame, + /obj/structure/railing) + +// This does the actual attacking. +/datum/ai_holder/proc/engage_target() + ai_log("engage_target() : Entering.", AI_LOG_DEBUG) + + // Can we still see them? + if (!target || !can_attack(target)) + ai_log("engage_target() : Lost sight of target.", AI_LOG_TRACE) + if (lose_target()) // We lost them (returns TRUE if we found something else to do) + ai_log("engage_target() : Pursuing other options (last seen, or a new target).", AI_LOG_TRACE) + return + + var/distance = get_dist(holder, target) + ai_log("engage_target() : Distance to target ([target]) is [distance].", AI_LOG_TRACE) + holder.face_atom(target) + last_conflict_time = world.time + + // Do a 'special' attack, if one is allowed. + if (holder.ICheckSpecialAttack(target)) + ai_log("engage_target() : Attempting a special attack.", AI_LOG_TRACE) + on_engagement(target) + if (special_attack(target)) // If this fails, then we try a regular melee/ranged attack. + ai_log("engage_target() : Successful special attack. Exiting.", AI_LOG_DEBUG) + return + + // Stab them. + else if (distance <= 1 && !pointblank) + ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE) + on_engagement(target) + melee_attack(target) + + else if (distance <= 1 && !holder.ICheckRangedAttack(target)) // Doesn't have projectile, but is pointblank + ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE) + on_engagement(target) + melee_attack(target) + + // Shoot them. + else if (holder.ICheckRangedAttack(target) && (distance <= max_range(target)) ) + on_engagement(target) + if (firing_lanes && !test_projectile_safety(target)) + // Nudge them a bit, maybe they can shoot next time. + var/turf/T = get_step(holder, pick(GLOB.cardinal)) + if (T) + holder.IMove(get_dir(holder, T)) // IMove() will respect movement cooldown. + holder.face_atom(target) + ai_log("engage_target() : Could not safely fire at target. Exiting.", AI_LOG_DEBUG) + return + + ai_log("engage_target() : Attempting a ranged attack.", AI_LOG_TRACE) + ranged_attack(target) + + // Run after them. + else if (!stand_ground) + ai_log("engage_target() : Target ([target]) too far away. Exiting.", AI_LOG_DEBUG) + set_stance(STANCE_APPROACH) + +// We're not entirely sure how holder will do melee attacks since any /mob/living could be holder, but we don't have to care because Interfaces. +/datum/ai_holder/proc/melee_attack(atom/A) + pre_melee_attack(A) + . = holder.IAttack(A) + if (. == ATTACK_SUCCESSFUL) + post_melee_attack(A) + +// Ditto. +/datum/ai_holder/proc/ranged_attack(atom/A) + pre_ranged_attack(A) + . = holder.IRangedAttack(A) + if (. == ATTACK_SUCCESSFUL) + post_ranged_attack(A) + +// Most mobs probably won't have this defined but we don't care. +/datum/ai_holder/proc/special_attack(atom/movable/AM) + pre_special_attack(AM) + . = holder.ISpecialAttack(AM) + if (. == ATTACK_SUCCESSFUL) + post_special_attack(AM) + +// Called when within striking/shooting distance, however cooldown is not considered. +// Override to do things like move in a random step for evasiveness. +// Note that this is called BEFORE the attack. +/datum/ai_holder/proc/on_engagement(atom/A) + +// Called before a ranged attack is attempted. +/datum/ai_holder/proc/pre_ranged_attack(atom/A) + +// Called before a melee attack is attempted. +/datum/ai_holder/proc/pre_melee_attack(atom/A) + +// Called before a 'special' attack is attempted. +/datum/ai_holder/proc/pre_special_attack(atom/A) + +// Called after a successful (IE not on cooldown) ranged attack. +// Note that this is not whether the projectile actually hit, just that one was launched. +/datum/ai_holder/proc/post_ranged_attack(atom/A) + +// Ditto but for melee. +/datum/ai_holder/proc/post_melee_attack(atom/A) + +// And one more for special snowflake attacks. +/datum/ai_holder/proc/post_special_attack(atom/A) + +// Used to make sure projectiles will probably hit the target and not the wall or a friend. +/datum/ai_holder/proc/test_projectile_safety(atom/movable/AM) + ai_log("test_projectile_safety([AM]) : Entering.", AI_LOG_TRACE) + + // If they're right next to us then lets just say yes. check_trajectory() tends to spaz out otherwise. + if (holder.Adjacent(AM)) + ai_log("test_projectile_safety() : Adjacent to target. Exiting with TRUE.", AI_LOG_TRACE) + return TRUE + + // This will hold a list of all mobs in a line, even those behind the target, and possibly the wall. + // By default the test projectile goes through things like glass and grilles, which is desirable as otherwise the AI won't try to shoot through windows. + var/hit_thing = check_trajectory(AM, holder) // This isn't always reliable but its better than the previous method. + + // Test to see if the primary target actually has a chance to get hit. + // We'll fire anyways if not, if we have conserve_ammo turned off. + var/would_hit_primary_target = FALSE + if (hit_thing == AM) + would_hit_primary_target = TRUE + ai_log("test_projectile_safety() : Test projectile did[!would_hit_primary_target ? " NOT " : " "]hit \the [AM]", AI_LOG_DEBUG) + + // Make sure we don't have a chance to shoot our friends. + var/atom/A = hit_thing + ai_log("test_projectile_safety() : Evaluating \the [A] ([A.type]).", AI_LOG_TRACE) + if (isliving(A)) // Don't shoot at our friends, even if they're behind the target, as RNG can make them get hit. + var/mob/living/L = A + if (holder.IIsAlly(L)) + ai_log("test_projectile_safety() : Would threaten ally, exiting with FALSE.", AI_LOG_DEBUG) + return FALSE + + // Don't fire if we cannot hit the primary target, and we wish to be conservative with our projectiles. + // We make an exception for turf targets because manual commanded AIs targeting the floor are generally intending to fire blindly. + if (!would_hit_primary_target && !isturf(AM) && conserve_ammo) + ai_log("test_projectile_safety() : conserve_ammo is set, and test projectile failed to hit primary target. Exiting with FALSE.", AI_LOG_DEBUG) + return FALSE + + ai_log("test_projectile_safety() : Passed other tests, exiting with TRUE.", AI_LOG_TRACE) + return TRUE + +// Test if we are within range to attempt an attack, melee or ranged. +/datum/ai_holder/proc/within_range(atom/movable/AM) + var/distance = get_dist(holder, AM) + if (distance <= 1) + return TRUE // Can melee. + else if (holder.ICheckRangedAttack(AM) && distance <= max_range(AM)) + return TRUE // Can shoot. + return FALSE + +// Determines how close the AI will move to its target. +/datum/ai_holder/proc/closest_distance(atom/movable/AM) + return max(max_range(AM) - 1, 1) // Max range -1 just because we don't want to constantly get kited + +// Can be used to conditionally do a ranged or melee attack. +/datum/ai_holder/proc/max_range(atom/movable/AM) + return holder.ICheckRangedAttack(AM) ? 7 : 1 + +// Goes to the target, to attack them. +// Called when in STANCE_APPROACH. +/datum/ai_holder/proc/walk_to_target() + ai_log("walk_to_target() : Entering.", AI_LOG_DEBUG) + // Make sure we can still chase/attack them. + if (!target || !can_attack(target)) + ai_log("walk_to_target() : Lost target.", AI_LOG_INFO) + lose_target() + return + + // Find out where we're going. + var/get_to = closest_distance(target) + var/distance = get_dist(holder, target) + ai_log("walk_to_target() : get_to is [get_to].", AI_LOG_TRACE) + + // We're here! + // Special case: Our holder has a special attack that is ranged, but normally the holder uses melee. + // If that happens, we'll switch to STANCE_FIGHT so they can use it. If the special attack is limited, they'll likely switch back next tick. + if (distance <= get_to || holder.ICheckSpecialAttack(target)) + ai_log("walk_to_target() : Within range.", AI_LOG_INFO) + forget_path() + set_stance(STANCE_FIGHT) + ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) + return + + // Otherwise keep walking. + if (!stand_ground) + walk_path(target, get_to) + + ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) + +// Resists out of things. +// Sometimes there are times you want your mob to be buckled to something, so override this for when that is needed. +/datum/ai_holder/proc/handle_resist() + holder.resist() + +// Used to break through windows and barriers to a target on the other side. +// This does two passes, so that if its just a public access door, the windows nearby don't need to be smashed. +/datum/ai_holder/proc/breakthrough(atom/target_atom) + ai_log("breakthrough() : Entering", AI_LOG_TRACE) + + if (!can_breakthrough) + ai_log("breakthrough() : Not allowed to breakthrough. Exiting.", AI_LOG_TRACE) + return FALSE + + if (!isturf(holder.loc)) + ai_log("breakthrough() : Trapped inside \the [holder.loc]. Exiting.", AI_LOG_TRACE) + return FALSE + + var/dir_to_target = get_dir(holder, target_atom) + holder.face_atom(target_atom) + + // Sometimes the mob will try to hit something diagonally, and generally this fails. + // So instead we will try two more times with some adjustments if the attack fails. + var/list/directions_to_try = list( + dir_to_target, + turn(dir_to_target, 45), + turn(dir_to_target, -45) + ) + + ai_log("breakthrough() : Starting peaceful pass.", AI_LOG_DEBUG) + + var/result = FALSE + + // First, we will try to peacefully make a path, I.E opening a door we have access to. + for(var/direction in directions_to_try) + result = destroy_surroundings(direction, violent = FALSE) + if (result) + break + + // Alright, lets smash some shit instead, if it didn't work and we're allowed to be violent. + if (!result && can_violently_breakthrough()) + ai_log("breakthrough() : Starting violent pass.", AI_LOG_DEBUG) + for(var/direction in directions_to_try) + result = destroy_surroundings(direction, violent = TRUE) + if (result) + break + + ai_log("breakthrough() : Exiting with [result].", AI_LOG_TRACE) + return result + +// Despite the name, this can also be used to help clear a path without any destruction. +/datum/ai_holder/proc/destroy_surroundings(direction, violent = TRUE) + ai_log("destroy_surroundings() : Entering.", AI_LOG_TRACE) + if (!direction) + direction = pick(GLOB.cardinal) // FLAIL WILDLY + ai_log("destroy_surroundings() : No direction given, picked [direction] randomly.", AI_LOG_DEBUG) + + var/turf/problem_turf = get_step(holder, direction) + + // First, give peace a chance. + if (!violent) + ai_log("destroy_surroundings() : Going to try to peacefully clear [problem_turf].", AI_LOG_DEBUG) + for(var/obj/machinery/door/D in problem_turf) + if (D.density && holder.Adjacent(D)) + + //Try to open the door if we're allowed too. + if (D.allowed(holder) && D.operable()) + // First, try to open the door if possible without smashing it. We might have access. + ai_log("destroy_surroundings() : Opening closed door.", AI_LOG_INFO) + return D.open() + + //Try to force the door if its broken/has no power + if (!prying && holder.can_pry && !D.operable()) + prying = TRUE + var/pry_time_holder = (D.pry_mod * holder.pry_time) + holder.pry_door(holder, pry_time_holder, D) + + // Peace has failed us, can we just smash the things in the way? + else + ai_log("destroy_surroundings() : Going to try to violently clear [problem_turf].", AI_LOG_DEBUG) + // First, kill windows in the way. + for(var/obj/structure/window/W in problem_turf) + if (W.dir == GLOB.reverse_dir[holder.dir]) // So that windows get smashed in the right order + ai_log("destroy_surroundings() : Attacking side window.", AI_LOG_INFO) + return melee_attack(W) + + else if (W.is_fulltile()) + ai_log("destroy_surroundings() : Attacking full tile window.", AI_LOG_INFO) + return melee_attack(W) + + // Kill hull shields in the way. + for(var/obj/effect/energy_field/shield in problem_turf) + if (shield.density) // Don't attack shields that are already down. + ai_log("destroy_surroundings() : Attacking hull shield.", AI_LOG_INFO) + return melee_attack(shield) + + // Kill common obstacle in the way like tables. + for(var/obstacle in valid_obstacles_by_priority) + obstacle = locate(obstacle) in problem_turf + if (obstacle) + ai_log("destroy_surroundings() : Attacking generic structure.", AI_LOG_INFO) + return melee_attack(obstacle) + + for(var/obj/machinery/door/D in problem_turf) // Required since firelocks take up the same turf. + if (D.density) + ai_log("destroy_surroundings() : Attacking closed door.", AI_LOG_INFO) + return melee_attack(D) + + ai_log("destroy_surroundings() : Exiting due to nothing to attack.", AI_LOG_INFO) + return ATTACK_FAILED // Nothing to attack. + +// Override for special behaviour. +/datum/ai_holder/proc/can_violently_breakthrough() + return violent_breakthrough diff --git a/code/modules/ai/ai_holder_combat_unseen.dm b/code/modules/ai/ai_holder_combat_unseen.dm new file mode 100644 index 00000000000..4455b26c988 --- /dev/null +++ b/code/modules/ai/ai_holder_combat_unseen.dm @@ -0,0 +1,43 @@ +// Used for fighting invisible things. + +// Used when a target is out of sight or invisible. +/datum/ai_holder/proc/engage_unseen_enemy() + ai_log("engage_unseen_enemy() : Entering.", AI_LOG_TRACE) + // Lets do some last things before giving up. + if (conserve_ammo || !holder.ICheckRangedAttack(target_last_seen_turf)) + if (get_dist(holder, target_last_seen_turf) > 1) // We last saw them over there. + // Go to where you last saw the enemy. + give_destination(target_last_seen_turf, 1, TRUE) // Sets stance as well + else if (lose_target_time == world.time) // We last saw them next to us, so do a blind attack on that tile. + melee_on_tile(target_last_seen_turf) + else + find_target() + else + shoot_near_turf(target_last_seen_turf) + +// This shoots semi-randomly near a specific turf. +/datum/ai_holder/proc/shoot_near_turf(turf/targeted_turf) + if (get_dist(holder, targeted_turf) > max_range(targeted_turf)) + return // Too far to shoot. + + var/turf/T = pick(RANGE_TURFS(targeted_turf, 2)) // The turf we're actually gonna shoot at. + on_engagement(T) + if (firing_lanes && !test_projectile_safety(T)) + step_rand(holder) + holder.face_atom(T) + return + + ranged_attack(T) + +// Attempts to attack something on a specific tile. +/datum/ai_holder/proc/melee_on_tile(turf/T) + ai_log("melee_on_tile() : Entering.", AI_LOG_TRACE) + var/mob/living/L = locate() in T + if (!L) + T.visible_message("\The [holder] attacks nothing around \the [T].") + return + + if (holder.IIsAlly(L)) // Don't hurt our ally. + return + + melee_attack(L) \ No newline at end of file diff --git a/code/modules/ai/ai_holder_communication.dm b/code/modules/ai/ai_holder_communication.dm new file mode 100644 index 00000000000..c5984b10cf1 --- /dev/null +++ b/code/modules/ai/ai_holder_communication.dm @@ -0,0 +1,136 @@ +// Contains code for speaking and emoting. + +/datum/ai_holder + var/threaten = FALSE // If hostile and sees a valid target, gives a 'warning' to the target before beginning the attack. + var/threatening = FALSE // If the mob actually gave the warning, checked so it doesn't constantly yell every tick. + var/threaten_delay = 3 SECONDS // How long a 'threat' lasts, until actual fighting starts. If null, the mob never starts the fight but still does the threat. + var/threaten_timeout = 1 MINUTE // If the mob threatens someone, they leave, and then come back before this timeout period, the mob escalates to fighting immediately. + var/last_conflict_time = null // Last occurance of fighting being used, in world.time. + var/last_threaten_time = null // Ditto but only for threats. + var/last_target_time = null // Ditto for when we last switched targets, used to stop retaliate from gimping mobs + + var/speak_chance = 0 // Probability that the mob talks (this is 'X in 200' chance since even 1/100 is pretty noisy) + + +/datum/ai_holder/proc/should_threaten() + if (!threaten) + return FALSE // We don't negotiate. + if (check_attacker(target)) + return FALSE // They (or someone like them) attacked us before, escalate immediately. + if (!will_threaten(target)) + return FALSE // Pointless to threaten an animal, a mindless drone, or an object. + if (stance in STANCES_COMBAT) + return FALSE // We're probably already fighting or recently fought if not in these stances. + if (last_threaten_time && threaten_delay && last_conflict_time + threaten_timeout > world.time) + return FALSE // We threatened someone recently, so lets show them we mean business. + return TRUE // Lets give them a chance to choose wisely and walk away. + +/datum/ai_holder/proc/threaten_target() + holder.face_atom(target) // Constantly face the target. + + if (!threatening) // First tick. + threatening = TRUE + last_threaten_time = world.time + + if (holder.say_list) + holder.ISay(pick(holder.say_list.say_threaten)) + playsound(holder, holder.say_list.threaten_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. + playsound(target, holder.say_list.threaten_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. + else // Otherwise we are waiting for them to go away or to wait long enough for escalate. + if (target in list_targets()) // Are they still visible? + var/should_escalate = FALSE + + if (threaten_delay && last_threaten_time + threaten_delay < world.time) // Waited too long. + should_escalate = TRUE + + if (should_escalate) + threatening = FALSE + set_stance(STANCE_APPROACH) + if (holder.say_list) + holder.ISay(pick(holder.say_list.say_escalate)) + else + return // Wait a bit. + + else // They left, or so we think. + if (last_threaten_time + threaten_timeout < world.time) // They've been gone long enough, probably safe to stand down + threatening = FALSE + set_stance(STANCE_IDLE) + if (holder.say_list) + holder.ISay(pick(holder.say_list.say_stand_down)) + playsound(holder, holder.say_list.stand_down_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. + playsound(target, holder.say_list.stand_down_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. + +// Determines what is deserving of a warning when STANCE_ALERT is active. +/datum/ai_holder/proc/will_threaten(mob/living/the_target) + if (!isliving(the_target)) + return FALSE // Turrets don't give a fuck so neither will we. + /* + // Find a nice way of doing this later. + if (istype(the_target, /mob/living/simple_animal) && istype(holder, /mob/living/simple_animal)) + var/mob/living/simple_animal/us = holder + var/mob/living/simple_animal/them = target + + if (them.intelligence_level < us.intelligence_level) // Todo: Bitflag these. + return FALSE // Humanoids don't care about drones/animals/etc. Drones don't care about animals, and so on. + */ + return TRUE + +// Temp defines to make the below code a bit more readable. +#define COMM_SAY "say" +#define COMM_AUDIBLE_EMOTE "audible emote" +#define COMM_VISUAL_EMOTE "visual emote" + +/datum/ai_holder/proc/handle_idle_speaking() + if (rand(0,200) < speak_chance) + // Check if anyone is around to 'appreciate' what we say. + var/alone = TRUE + for(var/m in viewers(holder)) + var/mob/M = m + if (M.client) + alone = FALSE + break + if (alone) // Forever alone. No point doing anything else. + return + + var/list/comm_types = list() // What kinds of things can we do? + if (!holder.say_list) + return + + if (holder.say_list.speak.len) + comm_types += COMM_SAY + if (holder.say_list.emote_hear.len) + comm_types += COMM_AUDIBLE_EMOTE + if (holder.say_list.emote_see.len) + comm_types += COMM_VISUAL_EMOTE + + if (!comm_types.len) + return // All the relevant lists are empty, so do nothing. + + switch(pick(comm_types)) + if (COMM_SAY) + holder.ISay(pick(holder.say_list.speak)) + if (COMM_AUDIBLE_EMOTE) + holder.audible_emote(pick(holder.say_list.emote_hear)) + if (COMM_VISUAL_EMOTE) + holder.visible_emote(pick(holder.say_list.emote_see)) + +#undef COMM_SAY +#undef COMM_AUDIBLE_EMOTE +#undef COMM_VISUAL_EMOTE + +// Handles the holder hearing a mob's say() +// Does nothing by default, override this proc for special behavior. +/datum/ai_holder/proc/on_hear_say(mob/living/speaker, message) + return + +// This is to make responses feel a bit more natural and not instant. +/datum/ai_holder/proc/delayed_say(message, mob/speak_to) + addtimer(CALLBACK(src, .proc/do_delayed_say, message, speak_to), rand(1 SECOND, 2 SECONDS)) + +/datum/ai_holder/proc/do_delayed_say(message, mob/speak_to) + if (!src || !holder || !can_act()) // We might've died/got deleted/etc in the meantime. + return + + if (speak_to) + holder.face_atom(speak_to) + holder.ISay(message) diff --git a/code/modules/ai/ai_holder_cooperation.dm b/code/modules/ai/ai_holder_cooperation.dm new file mode 100644 index 00000000000..6f14ae379e8 --- /dev/null +++ b/code/modules/ai/ai_holder_cooperation.dm @@ -0,0 +1,116 @@ +// Involves cooperating with other ai_holders. +/datum/ai_holder + var/cooperative = FALSE // If true, asks allies to help when fighting something. + var/call_distance = 14 // How far away calls for help will go for. + var/last_helpask_time = 0 // world.time when a mob asked for help. + var/list/faction_friends = list() // List of all mobs inside the faction with ai_holders that have cooperate on, to call for help without using range(). + // Note that this is only used for sending calls out. Receiving calls doesn't care about this list, only if the mob is in the faction. + // This means the AI could respond to a player's call for help, if a way to do so was implemented. + + // These vars don't do anything currently. They did before but an optimization made them nonfunctional. + // It was probably worth it. + var/call_players = FALSE // (Currently nonfunctional) If true, players get notified of an allied mob calling for help. + var/called_player_message = "needs help!" // (Currently nonfunctional) Part of a message used when above var is true. Full message is "\The [holder] [called_player_message]" + +/datum/ai_holder/New(new_holder) + ..() + if (cooperative) + build_faction_friends() + +/datum/ai_holder/Destroy() + if (faction_friends.len) //This list is shared amongst the faction + faction_friends -= src + return ..() + +// Handles everything about that list. +// Call on initialization or if something weird happened like the mob switched factions. +/datum/ai_holder/proc/build_faction_friends() + if (faction_friends.len) // Already have a list. + // Assume we're moving to a new faction. + faction_friends -= src // Get us out of the current list shared by everyone else. + faction_friends = list() // Then make our list empty and unshared in case we become a loner. + + // Find another AI-controlled mob in the same faction if possible. + var/mob/living/first_friend + for (var/mob/living/L in GLOB.living_mob_list_) + if (L.faction == holder.faction && L.ai_holder) + first_friend = L + break + + if (first_friend) // Joining an already established faction. + faction_friends = first_friend.ai_holder.faction_friends + faction_friends |= holder + else // We're the 'founder' (first and/or only member) of this faction. + faction_friends |= holder + +// Requests help in combat from other mobs possessing ai_holders. +/datum/ai_holder/proc/request_help() + ai_log("request_help() : Entering.", AI_LOG_DEBUG) + if (!cooperative || ((world.time - last_helpask_time) < 10 SECONDS)) + return + + ai_log("request_help() : Asking for help.", AI_LOG_INFO) + last_helpask_time = world.time + + for (var/mob/living/L in faction_friends) + if (L == holder) // Lets not call ourselves. + continue + if (holder.z != L.z) // On seperate z-level. + continue + if (get_dist(L, holder) > call_distance) // Too far to 'hear' the call for help. + continue + + if (holder.IIsAlly(L)) + // This will currently never run sadly, until faction_friends is made to accept players too. + // That might be for the best since I can imagine it getting spammy in a big fight. + if (L.client && call_players) // Dealing with a player. + ai_log("request_help() : Asking [L] (Player) for help.", AI_LOG_INFO) + to_chat(L, "\The [holder] [called_player_message]") + + else if (L.ai_holder) // Dealing with an AI. + ai_log("request_help() : Asking [L] (AI) for help.", AI_LOG_INFO) + L.ai_holder.help_requested(holder) + + ai_log("request_help() : Exiting.", AI_LOG_DEBUG) + +// What allies receive when someone else is calling for help.1 +/datum/ai_holder/proc/help_requested(mob/living/friend) + ai_log("help_requested() : Entering.", AI_LOG_DEBUG) + if (stance == STANCE_SLEEP) + ai_log("help_requested() : Help requested by [friend] but we are asleep.", AI_LOG_INFO) + return + if (!cooperative) + ai_log("help_requested() : Help requested by [friend] but we're not cooperative.", AI_LOG_INFO) + return + if (stance in STANCES_COMBAT) + ai_log("help_requested() : Help requested by [friend] but we are busy fighting something else.", AI_LOG_INFO) + return + if (!can_act()) + ai_log("help_requested() : Help requested by [friend] but cannot act (stunned or dead).", AI_LOG_INFO) + return + if (!holder.IIsAlly(friend)) // Extra sanity. + ai_log("help_requested() : Help requested by [friend] but we hate them.", AI_LOG_INFO) + return + var/their_target = friend?.ai_holder?.target + if (their_target) // They have a target and aren't just shouting for no reason + if (!can_attack(their_target, vision_required = FALSE)) + ai_log("help_requested() : Help requested by [friend] but we don't want to fight their target.", AI_LOG_INFO) + return + if (get_dist(holder, friend) <= follow_distance) + ai_log("help_requested() : Help requested by [friend] but we're already here.", AI_LOG_INFO) + return + if (get_dist(holder, friend) <= vision_range) // Within our sight. + ai_log("help_requested() : Help requested by [friend], and within target sharing range.", AI_LOG_INFO) + last_conflict_time = world.time // So we attack immediately and not threaten. + give_target(their_target, urgent = TRUE) // This will set us to the appropiate stance. + ai_log("help_requested() : Given target [target] by [friend]. Exiting", AI_LOG_DEBUG) + return + + // Otherwise they're outside our sight, lack a target, or aren't AI controlled, but within call range. + // So assuming we're AI controlled, we'll go to them and see whats wrong. + ai_log("help_requested() : Help requested by [friend], going to go to friend.", AI_LOG_INFO) + if (their_target) + add_attacker(their_target) // We won't wait and 'warn' them while they're stabbing our ally + set_follow(friend, 10 SECONDS) + ai_log("help_requested() : Exiting.", AI_LOG_DEBUG) + diff --git a/code/modules/ai/ai_holder_debug.dm b/code/modules/ai/ai_holder_debug.dm new file mode 100644 index 00000000000..c701a115191 --- /dev/null +++ b/code/modules/ai/ai_holder_debug.dm @@ -0,0 +1,97 @@ +// Contains settings to make it easier to debug things. + +/datum/ai_holder + var/path_display = FALSE // Displays a visual path when A* is being used. + var/path_icon = 'icons/misc/debug_group.dmi' // What icon to use for the overlay + var/path_icon_state = "red" // What state to use for the overlay + var/image/path_overlay // A reference to the overlay + + var/last_turf_display = FALSE // Similar to above, but shows the target's last known turf visually. + var/last_turf_icon_state = "green" // A seperate icon_state from the previous. + var/image/last_turf_overlay // Another reference for an overlay. + + var/stance_coloring = FALSE // Colors the mob depending on its stance. + + var/debug_ai = AI_LOG_OFF // The level of debugging information to display to people who can see log_debug(). + +/datum/ai_holder/New() + ..() + path_overlay = new(path_icon,path_icon_state) + last_turf_overlay = new(path_icon, last_turf_icon_state) + +/datum/ai_holder/Destroy() + path_overlay = null + last_turf_overlay = null + return ..() + +//For debug purposes! +/datum/ai_holder/proc/ai_log_output(msg = "missing message", ver = AI_LOG_INFO) + var/span_type + switch(ver) + if (AI_LOG_OFF) + return + if (AI_LOG_ERROR) + span_type = "debug_error" + if (AI_LOG_WARNING) + span_type = "debug_warning" + if (AI_LOG_INFO) + span_type = "debug_info" + if (AI_LOG_DEBUG) + span_type = "debug_debug" // RAS syndrome at work. + if (AI_LOG_TRACE) + span_type = "debug_trace" + if (ver <= debug_ai) + log_debug("AI: ([holder]:\ref[holder] | [holder.x],[holder.y],[holder.z])(@[world.time]): [msg] ") + +// Colors the mob based on stance, to visually tell what stance it is for debugging. +// Probably not something you want for regular use. +/datum/ai_holder/proc/stance_color() + var/new_color = null + switch(stance) + if (STANCE_SLEEP) + new_color = "#ffffff" // White + if (STANCE_IDLE) + new_color = "#00ff00" // Green + if (STANCE_ALERT) + new_color = "#ffff00" // Yellow + if (STANCE_APPROACH) + new_color = "#ff9933" // Orange + if (STANCE_FIGHT) + new_color = "#ff0000" // Red + if (STANCE_MOVE) + new_color = "#0000ff" // Blue + if (STANCE_REPOSITION) + new_color = "#ff00ff" // Purple + if (STANCE_FOLLOW) + new_color = "#00ffff" // Cyan + if (STANCE_FLEE) + new_color = "#666666" // Grey + if (STANCE_DISABLED) + new_color = "#000000" // Black + holder.color = new_color + +// Turns on all the debugging stuff. +/datum/ai_holder/proc/debug() + if (debug_ai == AI_LOG_OFF) + stance_coloring = TRUE + path_display = TRUE + last_turf_display = TRUE + debug_ai = AI_LOG_INFO + return TRUE + + stance_coloring = FALSE + path_display = FALSE + last_turf_display = FALSE + debug_ai = AI_LOG_OFF + return FALSE + +/datum/ai_holder/hostile/debug + wander = FALSE + conserve_ammo = FALSE + intelligence_level = AI_SMART + + stance_coloring = TRUE + path_display = TRUE + last_turf_display = TRUE + debug_ai = AI_LOG_INFO + diff --git a/code/modules/ai/ai_holder_disabled.dm b/code/modules/ai/ai_holder_disabled.dm new file mode 100644 index 00000000000..294f9037341 --- /dev/null +++ b/code/modules/ai/ai_holder_disabled.dm @@ -0,0 +1,82 @@ +// Handles AI while stunned or otherwise disabled. + +/datum/ai_holder + var/respect_confusion = TRUE // If false, the mob won't wander around recklessly. + +// If our holder is able to do anything. +/datum/ai_holder/proc/can_act() + if (!holder) // Holder missing. + manage_processing(AI_NO_PROCESS) + return FALSE + if (holder.stat) // Dead or unconscious. + ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE) + return FALSE + if (holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned in some form. + ai_log("can_act() : Incapacited.", AI_LOG_TRACE) + return FALSE + if (holder.instasis()) // In a stasis field. + ai_log("can_act() : In a stasis field.", AI_LOG_TRACE) + return FALSE + return TRUE + +// Test if we should switch to STANCE_DISABLE. +// Currently tests for death, stuns, and confusion. +/datum/ai_holder/proc/is_disabled() + if (!can_act()) + return TRUE + if (is_confused()) + return TRUE + return FALSE + +/datum/ai_holder/proc/is_confused() + return holder.confused > 0 && respect_confusion + +// Called by the main loop. +/datum/ai_holder/proc/handle_disabled() + if (!can_act()) + return // Just sit there and take it. + else if (is_confused()) + dangerous_wander() // Let's bump into allies and hit them. + +// Similar to normal wander, but will walk into tiles that are harmful, and attack anything they bump into, including allies. +// Occurs when confused. +/datum/ai_holder/proc/dangerous_wander() + ai_log("dangerous_wander() : Entered.", AI_LOG_DEBUG) + if (isturf(holder.loc) && can_act()) + // Test if we should refrain from falling/attacking allies, if we're smart enough to realize that. + if (intelligence_level > AI_NORMAL) + var/unsafe = FALSE + + tile_test: + for(var/dir_tested in GLOB.cardinal) + var/turf/turf_tested = get_step(holder, dir_tested) + // Look for unsafe tiles. + if (!turf_tested.is_safe_to_enter(holder)) + unsafe = TRUE + break + + // Look for allies. + for(var/mob/living/L in turf_tested) + if (holder.IIsAlly(L)) + unsafe = TRUE + break tile_test + + + if (unsafe) + ai_log("dangerous_wander() : Staying still due to risk of harm to self or allies.", AI_LOG_TRACE) + return // Just stay still. + + var/moving_to = 0 + moving_to = pick(GLOB.cardinal) + var/turf/T = get_step(holder, moving_to) + + var/mob/living/L = locate() in T + if (L) + // Attack whoever's on the tile. Even if it's an ally. + ai_log("dangerous_wander() : Going to confuse-attack [L].", AI_LOG_TRACE) + melee_attack(L) + else + // Move to the tile. Even if it's unsafe. + ai_log("dangerous_wander() : Going to confuse-walk to [T] ([T.x],[T.y],[T.z]).", AI_LOG_TRACE) + holder.IMove(T, safety = FALSE) + ai_log("dangerous_wander() : Exited.", AI_LOG_DEBUG) diff --git a/code/modules/ai/ai_holder_fleeing.dm b/code/modules/ai/ai_holder_fleeing.dm new file mode 100644 index 00000000000..c1e186b18a5 --- /dev/null +++ b/code/modules/ai/ai_holder_fleeing.dm @@ -0,0 +1,43 @@ +// This code handles what to do inside STANCE_FLEE. + +/datum/ai_holder + var/can_flee = TRUE // If they're even allowed to flee. + var/flee_when_dying = TRUE // If they should flee when low on health. + var/dying_threshold = 0.3 // How low on health the holder needs to be before fleeing. Defaults to 30% or lower health. + var/flee_when_outmatched = FALSE // If they should flee upon reaching a specific tension threshold. + var/outmatched_threshold = 200 // The tension threshold needed for a mob to decide it should run away. + + + +/datum/ai_holder/proc/should_flee(force = FALSE) + if (force) + return TRUE + + if (can_flee) + if (special_flee_check()) + return TRUE + if (!hostile && !retaliate) + return TRUE // We're not hostile and someone attacked us first. + if (flee_when_dying && (holder.health / holder.getMaxHealth()) <= dying_threshold) + return TRUE // We're gonna die! + else if (flee_when_outmatched && holder.get_tension() >= outmatched_threshold) + return TRUE // We're fighting something way way stronger then us. + return FALSE + +// Override for special fleeing conditionally. +/datum/ai_holder/proc/special_flee_check() + return FALSE + +/datum/ai_holder/proc/flee_from_target() + ai_log("flee_from_target() : Entering.", AI_LOG_DEBUG) + + if (!target || !should_flee() || !can_attack(target)) // can_attack() is used since it checks the same things we would need to anyways. + ai_log("flee_from_target() : Lost target to flee from.", AI_LOG_INFO) + lose_target() + set_stance(STANCE_IDLE) + ai_log("flee_from_target() : Exiting.", AI_LOG_DEBUG) + return + + ai_log("flee_from_target() : Stepping away.", AI_LOG_TRACE) + step_away(holder, target, vision_range) + ai_log("flee_from_target() : Exiting.", AI_LOG_DEBUG) \ No newline at end of file diff --git a/code/modules/ai/ai_holder_follow.dm b/code/modules/ai/ai_holder_follow.dm new file mode 100644 index 00000000000..a7953498e7b --- /dev/null +++ b/code/modules/ai/ai_holder_follow.dm @@ -0,0 +1,68 @@ +// This handles following a specific atom/movable, without violently murdering it. + +/datum/ai_holder + // Following. + var/atom/movable/leader = null // The movable atom that the mob wants to follow. + var/follow_distance = 2 // How far leader must be to start moving towards them. + var/follow_until_time = 0 // world.time when the mob will stop following leader. 0 means it won't time out. + +/datum/ai_holder/proc/walk_to_leader() + ai_log("walk_to_leader() : Entering.",AI_LOG_TRACE) + if (!leader) + ai_log("walk_to_leader() : No leader.", AI_LOG_WARNING) + forget_path() + set_stance(STANCE_IDLE) + ai_log("walk_to_leader() : Exiting.", AI_LOG_TRACE) + return + + // Did we time out? + if (follow_until_time && world.time > follow_until_time) + ai_log("walk_to_leader() : Follow timed out, losing leader.", AI_LOG_INFO) + lose_follow() + set_stance(STANCE_IDLE) + ai_log("walk_to_leader() : Exiting.", AI_LOG_TRACE) + return + + var/get_to = follow_distance + var/distance = get_dist(holder, leader) + ai_log("walk_to_leader() : get_to is [get_to].", AI_LOG_TRACE) + + // We're here! + if (distance <= get_to) + give_up_movement() + set_stance(STANCE_IDLE) + ai_log("walk_to_leader() : Within range, exiting.", AI_LOG_INFO) + return + + ai_log("walk_to_leader() : Walking.", AI_LOG_TRACE) + walk_path(leader, get_to) + ai_log("walk_to_leader() : Exiting.",AI_LOG_DEBUG) + +/datum/ai_holder/proc/set_follow(mob/living/L, follow_for = 0) + ai_log("set_follow() : Entered.", AI_LOG_DEBUG) + if (!L) + ai_log("set_follow() : Was told to follow a nonexistant mob.", AI_LOG_ERROR) + return FALSE + + leader = L + follow_until_time = !follow_for ? 0 : world.time + follow_for + ai_log("set_follow() : Exited.", AI_LOG_DEBUG) + return TRUE + +/datum/ai_holder/proc/lose_follow() + ai_log("lose_follow() : Entered.", AI_LOG_DEBUG) + ai_log("lose_follow() : Going to lose leader [leader].", AI_LOG_INFO) + leader = null + give_up_movement() + ai_log("lose_follow() : Exited.", AI_LOG_DEBUG) + +/datum/ai_holder/proc/should_follow_leader() + if (!leader || target) + return FALSE + if (follow_until_time && world.time > follow_until_time) + lose_follow() + set_stance(STANCE_IDLE) + return FALSE + if (get_dist(holder, leader) > follow_distance) + return TRUE + return FALSE \ No newline at end of file diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm new file mode 100644 index 00000000000..b6494d30501 --- /dev/null +++ b/code/modules/ai/ai_holder_movement.dm @@ -0,0 +1,165 @@ +/datum/ai_holder + // General. + var/turf/destination = null // The targeted tile the mob wants to walk to. + var/min_distance_to_destination = 1 // Holds how close the mob should go to destination until they're done. + + // Home. + var/turf/home_turf = null // The mob's 'home' turf. It will try to stay near it if told to do so. This is the turf the AI was initialized on by default. + var/returns_home = FALSE // If true, makes the mob go to its 'home' if it strays too far. + var/home_low_priority = FALSE // If true, the mob will not go home unless it has nothing better to do, e.g. its following someone. + var/max_home_distance = 3 // How far the mob can go away from its home before being told to go_home(). + // Note that there is a 'BYOND cap' of 14 due to limitations of get_/step_to(). + // Wandering. + var/wander = FALSE // If true, the mob will randomly move in the four cardinal directions when idle. + var/wander_delay = 0 // How many ticks until the mob can move a tile in handle_wander_movement(). + var/base_wander_delay = 2 // What the above var gets set to when it wanders. Note that a tick happens every half a second. + var/wander_when_pulled = FALSE // If the mob will refrain from wandering if someone is pulling it. + + // Breakthrough + var/failed_breakthroughs = 0 // How many times we've failed to breakthrough something lately + +/datum/ai_holder/proc/walk_to_destination() + ai_log("walk_to_destination() : Entering.",AI_LOG_TRACE) + if (!destination) + ai_log("walk_to_destination() : No destination.", AI_LOG_WARNING) + forget_path() + set_stance(stance == STANCE_REPOSITION ? STANCE_APPROACH : STANCE_IDLE) + ai_log("walk_to_destination() : Exiting.", AI_LOG_TRACE) + return + + var/get_to = min_distance_to_destination + var/distance = get_dist(holder, destination) + ai_log("walk_to_destination() : get_to is [get_to].", AI_LOG_TRACE) + + // We're here! + if (distance <= get_to) + give_up_movement() + set_stance(stance == STANCE_REPOSITION ? STANCE_APPROACH : STANCE_IDLE) + ai_log("walk_to_destination() : Destination reached. Exiting.", AI_LOG_INFO) + return + + ai_log("walk_to_destination() : Walking.", AI_LOG_TRACE) + walk_path(destination, get_to) + ai_log("walk_to_destination() : Exiting.",AI_LOG_TRACE) + +/datum/ai_holder/proc/should_go_home() + if (stance != STANCE_IDLE) + return FALSE + if (!returns_home || !home_turf) + return FALSE + if (get_dist(holder, home_turf) > max_home_distance) + if (!home_low_priority) + return TRUE + else if (!leader && !target) + return TRUE + return FALSE + +/datum/ai_holder/proc/go_home() + if (home_turf) + ai_log("go_home() : Telling holder to go home.", AI_LOG_INFO) + lose_follow() // So they don't try to path back and forth. + give_destination(home_turf, max_home_distance) + else + ai_log("go_home() : Told to go home without home_turf.", AI_LOG_ERROR) + +/datum/ai_holder/proc/give_destination(turf/new_destination, min_distance = 1, combat = FALSE) + ai_log("give_destination() : Entering.", AI_LOG_DEBUG) + + destination = new_destination + min_distance_to_destination = min_distance + + if (new_destination != null) + ai_log("give_destination() : Going to new destination.", AI_LOG_INFO) + set_stance(combat ? STANCE_REPOSITION : STANCE_MOVE) + return TRUE + else + ai_log("give_destination() : Given null destination.", AI_LOG_ERROR) + + ai_log("give_destination() : Exiting.", AI_LOG_DEBUG) + + +/// Walk towards whatever. +/datum/ai_holder/proc/walk_path(atom/A, get_to = 1) + ai_log("walk_path() : Entered.", AI_LOG_TRACE) + + if (use_astar) + if (!path.len) // If we're missing a path, make a new one. + ai_log("walk_path() : No path. Attempting to calculate path.", AI_LOG_DEBUG) + calculate_path(A, get_to) + + if (!path.len) // If we still don't have one, then the target's probably somewhere inaccessible to us. Get as close as we can. + ai_log("walk_path() : Failed to obtain path to target. Using get_step_to() instead.", AI_LOG_INFO) + if (holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED) + ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) + if (!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 + return + + if (move_once() == FALSE) // Start walking the path. + ai_log("walk_path() : Failed to step.", AI_LOG_TRACE) + ++failed_steps + if (failed_steps > 3) // We're probably stuck. + ai_log("walk_path() : Too many failed_steps.", AI_LOG_DEBUG) + forget_path() // So lets try again with a new path. + failed_steps = 0 + + else + ai_log("walk_path() : Going to IMove().", AI_LOG_TRACE) + if (holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED ) + ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) + if (!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 + + ai_log("walk_path() : Exited.", AI_LOG_TRACE) + + +///Take one step along a path +/datum/ai_holder/proc/move_once() + ai_log("move_once() : Entered.", AI_LOG_TRACE) + if (!path.len) + return + + if (path_display) + var/turf/T = src.path[1] + T.overlays -= path_overlay + + if (holder.IMove(get_dir(holder, src.path[1])) != MOVEMENT_ON_COOLDOWN) + if (holder.loc != src.path[1]) + ai_log("move_once() : Failed step. Exiting.", AI_LOG_TRACE) + return MOVEMENT_FAILED + else + path -= src.path[1] + ai_log("move_once() : Successful step. Exiting.", AI_LOG_TRACE) + return MOVEMENT_SUCCESSFUL + ai_log("move_once() : Mob movement on cooldown. Exiting.", AI_LOG_TRACE) + return MOVEMENT_ON_COOLDOWN + +/datum/ai_holder/proc/should_wander() + return (stance == STANCE_IDLE) && wander && !leader + +/// Wanders randomly in cardinal directions. +/datum/ai_holder/proc/handle_wander_movement() + ai_log("handle_wander_movement() : Entered.", AI_LOG_TRACE) + if (isturf(holder.loc) && can_act()) + wander_delay-- + if (wander_delay <= 0) + if (!wander_when_pulled && (holder.pulledby || holder.grabbed_by.len)) + ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) + return + + var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. + moving_to = pick(GLOB.cardinal) + holder.set_dir(moving_to) + holder.IMove(moving_to) + wander_delay = base_wander_delay + ai_log("handle_wander_movement() : Exited.", AI_LOG_TRACE) + +/mob/living/proc/set_wander_when_pulled(value) + if (ai_holder) + ai_holder.wander_when_pulled = value + +/mob/living/proc/set_wander(value) + if(ai_holder) + ai_holder.wander = value \ No newline at end of file diff --git a/code/modules/ai/ai_holder_pathfinding.dm b/code/modules/ai/ai_holder_pathfinding.dm new file mode 100644 index 00000000000..2ad17082b8d --- /dev/null +++ b/code/modules/ai/ai_holder_pathfinding.dm @@ -0,0 +1,58 @@ +// This handles obtaining a (usually A*) path towards something, such as a target, destination, or leader. +// This interacts heavily with code inside ai_holder_movement.dm + +/datum/ai_holder + // Pathfinding. + var/use_astar = FALSE // Do we use the more expensive A* implementation or stick with BYOND's default step_to()? + var/list/path = list() // A list of tiles that A* gave us as a solution to reach the target. + var/list/obstacles = list() // Things A* will try to avoid. + var/astar_adjacent_proc = /turf/proc/CardinalTurfsWithAccess // Proc to use when A* pathfinding. Default makes them bound to cardinals. + var/failed_steps = 0 // If move_once() fails to move the mob onto the correct tile, this increases. When it reaches 3, the path is recalc'd since they're probably stuck. + +// This clears the stored A* path. +/datum/ai_holder/proc/forget_path() + ai_log("forget_path() : Entering.", AI_LOG_DEBUG) + if (path_display) + for (var/turf/T in path) + T.overlays -= path_overlay + path.Cut() + ai_log("forget_path() : Exiting.", AI_LOG_DEBUG) + +/datum/ai_holder/proc/give_up_movement() + ai_log("give_up_movement() : Entering.", AI_LOG_DEBUG) + forget_path() + destination = null + ai_log("give_up_movement() : Exiting.", AI_LOG_DEBUG) + +/datum/ai_holder/proc/calculate_path(atom/A, get_to = 1) + ai_log("calculate_path([A],[get_to]) : Entering.", AI_LOG_DEBUG) + if (!A) + ai_log("calculate_path() : Called without an atom. Exiting.",AI_LOG_WARNING) + return + + if (!use_astar) // If we don't use A* then this is pointless. + ai_log("calculate_path() : Not using A*, Exiting.", AI_LOG_DEBUG) + return + + get_path(get_turf(A), get_to) + + ai_log("calculate_path() : Exiting.", AI_LOG_DEBUG) + +///A* now, try to a path to a target +/datum/ai_holder/proc/get_path(turf/target,var/get_to = 1, max_distance = world.view*6) + ai_log("get_path() : Entering.",AI_LOG_DEBUG) + forget_path() + var/list/new_path = AStar(get_turf(holder.loc), target, astar_adjacent_proc, /turf/proc/Distance, min_target_dist = get_to, max_node_depth = max_distance, id = holder.IGetID(), exclude = obstacles) + + if (new_path && new_path.len) + path = new_path + ai_log("get_path() : Made new path.", AI_LOG_DEBUG) + if (path_display) + for(var/turf/T in path) + T.overlays |= path_overlay + else + ai_log("get_path() : Failed to make new path. Exiting.", AI_LOG_DEBUG) + return 0 + + ai_log("get_path() : Exiting.", AI_LOG_DEBUG) + return path.len \ No newline at end of file diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm new file mode 100644 index 00000000000..7c6a4854e2b --- /dev/null +++ b/code/modules/ai/ai_holder_targeting.dm @@ -0,0 +1,290 @@ +// Used for assigning a target for attacking. + +/datum/ai_holder + var/hostile = FALSE // Do we try to hurt others? + var/retaliate = FALSE // Attacks whatever struck it first. Mobs will still attack back if this is false but hostile is true. + var/mauling = FALSE // Attacks unconscious mobs + var/handle_corpse = FALSE // Allows AI to acknowledge corpses (e.g. nurse spiders) + + var/atom/movable/target = null // The thing (mob or object) we're trying to kill. + var/atom/movable/preferred_target = null// If set, and if given the chance, we will always prefer to target this over other options. + var/turf/target_last_seen_turf = null // Where the mob last observed the target being, used if the target disappears but the mob wants to keep fighting. + + var/vision_range = 7 // How far the targeting system will look for things to kill. Note that values higher than 7 are 'offscreen' and might be unsporting. + var/respect_alpha = TRUE // If true, mobs with a sufficently low alpha will be treated as invisible. + var/alpha_vision_threshold = FAKE_INVIS_ALPHA_THRESHOLD // Targets with an alpha less or equal to this will be considered invisible. Requires above var to be true. + + var/lose_target_time = 0 // world.time when a target was lost. + var/lose_target_timeout = 5 SECONDS // How long until a mob 'times out' and stops trying to find the mob that disappeared. + + var/list/attackers = list() // List of strings of names of people who attacked us before in our life. + // This uses strings and not refs to allow for disguises, and to avoid needing to use weakrefs. + var/destructive = FALSE // Will target 'neutral' structures/objects and not just 'hostile' ones. + +// A lot of this is based off of /TG/'s AI code. + +/// Step 1, find out what we can see. +/datum/ai_holder/proc/list_targets() + . = ohearers(vision_range, holder) + . -= GLOB.dview_mob // Not the dview mob! + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /mob/living/exosuit, /obj/effect/blob)) + + for (var/HM in typecache_filter_list(range(vision_range, holder), hostile_machines)) + if (can_see(holder, HM, vision_range)) + . += HM + +/// Step 2, filter down possible targets to things we actually care about. +/datum/ai_holder/proc/find_target(list/possible_targets, has_targets_list = FALSE) + ai_log("find_target() : Entered.", AI_LOG_TRACE) + if (!hostile) // So retaliating mobs only attack the thing that hit it. + return null + . = list() + if (!has_targets_list) + possible_targets = list_targets() + for (var/possible_target in possible_targets) + if (can_attack(possible_target)) // Can we attack it? + . += possible_target + + var/new_target = pick_target(.) + give_target(new_target) + return new_target + +/// Step 3, pick among the possible, attackable targets. +/datum/ai_holder/proc/pick_target(list/targets) + if (target) // If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets. + targets = target_filter_distance(targets) + else + targets = target_filter_closest(targets) + if (!targets.len) // We found nothing. + return + + var/chosen_target + if (preferred_target && (preferred_target in targets)) + chosen_target = preferred_target + else + chosen_target = pick(targets) + return chosen_target + +/// Step 4, give us our selected target. +/datum/ai_holder/proc/give_target(new_target, urgent = FALSE) + ai_log("give_target() : Given '[new_target]', urgent=[urgent].", AI_LOG_TRACE) + target = new_target + + if (target != null) + lose_target_time = 0 + track_target_position() + if (should_threaten() && !urgent) + set_stance(STANCE_ALERT) + else + set_stance(STANCE_FIGHT) + last_target_time = world.time + return TRUE + +// Filters return one or more 'preferred' targets. + +/// Targets closer to us than the current one. +/datum/ai_holder/proc/target_filter_distance(list/targets) + var/target_dist = get_dist(holder, target) + var/list/better_targets = list() + for (var/possible_target in targets) + var/atom/A = possible_target + var/possible_target_distance = get_dist(holder, A) + if (possible_target_distance < target_dist) + better_targets += A + return better_targets + +/// Returns the closest target and anything tied with it for distance +/datum/ai_holder/proc/target_filter_closest(list/targets) + var/lowest_distance = 1e6 //fakely far + var/list/closest_targets = list() + for (var/possible_target in targets) + var/atom/A = possible_target + var/current_distance = get_dist(holder, A) + if (current_distance < lowest_distance) + closest_targets.Cut() + lowest_distance = current_distance + closest_targets += A + else if (current_distance == lowest_distance) + closest_targets += A + return closest_targets + +/datum/ai_holder/proc/can_attack(atom/movable/the_target, vision_required = TRUE) + if (!can_see_target(the_target) && vision_required) + return FALSE + + if (isliving(the_target)) + var/mob/living/L = the_target + if (ishuman(L) || issilicon(L)) + if (L.key && !L.client) // SSD players get a pass + return FALSE + + if (L.status_flags & NOTARGET) + return FALSE + if (L.stat) + if (L.stat == DEAD && !handle_corpse) // Leave dead things alone + return FALSE + if (L.stat == UNCONSCIOUS) // Do we have mauling? Yes? Then maul people who are sleeping but not SSD + if (mauling) + return TRUE + else + return FALSE + if (holder.IIsAlly(L)) + return FALSE + return TRUE + + if (istype(the_target, /mob/living/exosuit)) + var/mob/living/exosuit/M = the_target + for (var/mob/pilot in M.pilots) + return can_attack(pilot) + return destructive // Empty mechs are 'neutral'. + + if (istype(the_target, /obj/machinery/porta_turret)) + var/obj/machinery/porta_turret/P = the_target + if (P.stat & BROKEN) + return FALSE // Already dead. + if (!(P.assess_living(holder))) + return FALSE // Don't shoot allied turrets. + if (!P.raised && !P.raising) + return FALSE // Turrets won't get hurt if they're still in their cover. + return TRUE + + return TRUE + +/// 'Soft' loss of target. They may still exist, we still have some info about them maybe. +/datum/ai_holder/proc/lose_target() + ai_log("lose_target() : Entering.", AI_LOG_TRACE) + if (target) + ai_log("lose_target() : Had a target, setting to null and LTT.", AI_LOG_DEBUG) + target = null + lose_target_time = world.time + + give_up_movement() + + if (target_last_seen_turf && intelligence_level >= AI_SMART) + ai_log("lose_target() : Going into 'engage unseen enemy' mode.", AI_LOG_INFO) + engage_unseen_enemy() + return TRUE //We're still working on it + else + ai_log("lose_target() : Can't chase target, so giving up.", AI_LOG_INFO) + remove_target() + return find_target() //Returns if we found anything else to do + +/// 'Hard' loss of target. Clean things up and return to idle. +/datum/ai_holder/proc/remove_target() + ai_log("remove_target() : Entering.", AI_LOG_TRACE) + if (target) + target = null + + lose_target_time = 0 + give_up_movement() + lose_target_position() + set_stance(STANCE_IDLE) + +/// Check if target is visible to us. +/datum/ai_holder/proc/can_see_target(atom/movable/the_target, view_range = vision_range) + ai_log("can_see_target() : Entering.", AI_LOG_TRACE) + + if (!the_target) // Nothing to target. + ai_log("can_see_target() : There is no target. Exiting.", AI_LOG_WARNING) + return FALSE + + if (holder.see_invisible < the_target.invisibility) // Real invis. + ai_log("can_see_target() : Target ([the_target]) was invisible to holder. Exiting.", AI_LOG_TRACE) + return FALSE + + if (respect_alpha && the_target.alpha <= alpha_vision_threshold) // Fake invis. + ai_log("can_see_target() : Target ([the_target]) was sufficently transparent to holder and is hidden. Exiting.", AI_LOG_TRACE) + return FALSE + + if (get_dist(holder, the_target) > view_range) // Too far away. + ai_log("can_see_target() : Target ([the_target]) was too far from holder. Exiting.", AI_LOG_TRACE) + return FALSE + + if (!(the_target in oview(view_range, holder))) + ai_log("can_see_target() : Target ([the_target]) failed can_see(). Exiting.", AI_LOG_TRACE) + return FALSE + + ai_log("can_see_target() : Target ([the_target]) can be seen. Exiting.", AI_LOG_TRACE) + return TRUE + +/// Updates the last known position of the target. +/datum/ai_holder/proc/track_target_position() + if (!target) + lose_target_position() + + if (last_turf_display && target_last_seen_turf) + target_last_seen_turf.overlays -= last_turf_overlay + + target_last_seen_turf = get_turf(target) + + if (last_turf_display) + target_last_seen_turf.overlays += last_turf_overlay + +/// Resets the last known position to null. +/datum/ai_holder/proc/lose_target_position() + if (last_turf_display && target_last_seen_turf) + target_last_seen_turf.overlays -= last_turf_overlay + ai_log("lose_target_position() : Last position is being reset.", AI_LOG_INFO) + target_last_seen_turf = null + +/// Responds to a hostile action against its mob. +/datum/ai_holder/proc/react_to_attack(atom/movable/attacker) + if (holder.stat) // We're dead. + ai_log("react_to_attack() : Was attacked by [attacker], but we are dead/unconscious.", AI_LOG_TRACE) + return FALSE + if (!hostile && !retaliate) // Not allowed to defend ourselves. + ai_log("react_to_attack() : Was attacked by [attacker], but we are not allowed to attack back.", AI_LOG_TRACE) + return FALSE + if (holder.IIsAlly(attacker)) // I'll overlook it THIS time... + ai_log("react_to_attack() : Was attacked by [attacker], but they were an ally.", AI_LOG_TRACE) + return FALSE + if (target) // Already fighting someone. Switching every time we get hit would impact our combat performance. + if (!retaliate) // If we don't get to fight back, we don't fight back... + ai_log("react_to_attack() : Was attacked by [attacker], but we already have a target.", AI_LOG_TRACE) + on_attacked(attacker) // So we attack immediately and not threaten. + return FALSE + else if (check_attacker(attacker) && world.time > last_target_time + 3 SECONDS) // Otherwise, let 'er rip + ai_log("react_to_attack() : Was attacked by [attacker]. Can retaliate, waited 3 seconds.", AI_LOG_INFO) + on_attacked(attacker) // So we attack immediately and not threaten. + return give_target(attacker) // Also handles setting the appropiate stance. + + if (stance == STANCE_SLEEP) // If we're asleep, try waking up if someone's wailing on us. + ai_log("react_to_attack() : AI is asleep. Waking up.", AI_LOG_TRACE) + go_wake() + + ai_log("react_to_attack() : Was attacked by [attacker].", AI_LOG_INFO) + on_attacked(attacker) // So we attack immediately and not threaten. + return give_target(attacker, urgent = TRUE) // Also handles setting the appropiate stance. + +/// Sets a few vars so mobs that threaten will react faster to an attacker or someone who attacked them before. +/datum/ai_holder/proc/on_attacked(atom/movable/AM) + last_conflict_time = world.time + add_attacker(AM) + +/// Checks to see if an atom attacked us lately +/datum/ai_holder/proc/check_attacker(atom/movable/A) + return (A in attackers) + +/// We were attacked by this thing recently +/datum/ai_holder/proc/add_attacker(atom/movable/A) + attackers |= A.name + +/// Forgive this attacker +/datum/ai_holder/proc/remove_attacker(atom/movable/A) + attackers -= A.name + +/** +* Causes targeting to prefer targeting the taunter if possible. +* This generally occurs if more than one option is within striking distance, including the taunter. +* Otherwise the default filter will prefer the closest target. +*/ +/datum/ai_holder/proc/receive_taunt(atom/movable/taunter, force_target_switch = FALSE) + ai_log("receive_taunt() : Was taunted by [taunter].", AI_LOG_INFO) + preferred_target = taunter + if (force_target_switch) + give_target(taunter) + +/datum/ai_holder/proc/lose_taunt() + ai_log("lose_taunt() : Resetting preferred_target.", AI_LOG_INFO) + preferred_target = null diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm new file mode 100644 index 00000000000..7f662e28e5d --- /dev/null +++ b/code/modules/ai/interfaces.dm @@ -0,0 +1,107 @@ +// 'Interfaces' are procs that the ai_holder datum uses to communicate its will to the mob its attached. +// The reason for using this proc in the middle is to ensure the AI has some form of compatibility with most mob types, +// since some actions work very differently between mob types (e.g. executing an attack as a simple animal compared to a human). +// The AI can just call holder.IAttack(target) and the mob is responsible for determining how to actually attack the target. + +/mob/living/proc/IAttack(atom/A) + return FALSE + +/mob/living/simple_animal/IAttack(atom/A) + if (!canClick()) // Still on cooldown from a "click". + return ATTACK_ON_COOLDOWN + return attack_target(A) // This will set click cooldown. + +/mob/living/carbon/human/IAttack(atom/A) + if (!canClick()) // Still on cooldown from a "click". + return FALSE + return ClickOn(A) // Except this is an actual fake "click". + +/mob/living/proc/IRangedAttack(atom/A) + return FALSE + +/mob/living/simple_animal/IRangedAttack(atom/A) + if (!canClick()) // Still on cooldown from a "click". + return ATTACK_ON_COOLDOWN + return shoot_target(A) + +// Test if the AI is allowed to attempt a ranged attack. +/mob/living/proc/ICheckRangedAttack(atom/A) + return FALSE + +/mob/living/simple_animal/ICheckRangedAttack(atom/A) + if (needs_reload) + if (reload_count >= reload_max) + try_reload() + return FALSE + return projectiletype ? TRUE : FALSE + +/mob/living/proc/ISpecialAttack(atom/A) + return FALSE + +/mob/living/simple_animal/ISpecialAttack(atom/A) + return special_attack_target(A) + +// Is the AI allowed to attempt to do it? +/mob/living/proc/ICheckSpecialAttack(atom/A) + return FALSE + +/mob/living/simple_animal/ICheckSpecialAttack(atom/A) + return can_special_attack(A) && should_special_attack(A) // Just because we can doesn't mean we should. + +/mob/living/proc/ISay(message) + return say(message) + +/mob/living/proc/IIsAlly(mob/living/L) + return istype(L) && src.faction == L.faction + +/mob/living/simple_animal/IIsAlly(mob/living/L) + . = ..() + if (!.) // Outside the faction, try to see if they're friends. + return L in friends + +/mob/living/proc/IGetID() + +/mob/living/simple_animal/IGetID() + return myid + +/mob/living/proc/instasis() + +/mob/living/simple_animal/instasis() + if (in_stasis) + return TRUE + +// Respects move cooldowns as if it had a client. +// Also tries to avoid being superdumb with moving into certain tiles (unless that's desired). +/mob/living/proc/IMove(dir, safety = TRUE) + + var/turf/newloc + if (istype(dir, /turf)) + newloc = dir + dir = get_dir(src, dir) + else + newloc = get_step(src, dir) + + if (!checkMoveCooldown()) + return MOVEMENT_ON_COOLDOWN + + // Check to make sure moving to newloc won't actually kill us. e.g. we're a slime and trying to walk onto water. + if (istype(newloc)) + if (safety && !newloc.is_safe_to_enter(src)) + return MOVEMENT_FAILED + + // Move()ing to another tile successfully returns 32 because BYOND. Would rather deal with TRUE/FALSE-esque terms. + // Note that moving to the same tile will be 'successful'. + var/turf/old_T = get_turf(src) + + // An adjacency check to avoid mobs phasing diagonally past windows. + // This might be better in general movement code but I'm too scared to add it, and most things don't move diagonally anyways. + if (!old_T.Adjacent(newloc)) + return MOVEMENT_FAILED + + . = Move(newloc, dir) ? MOVEMENT_SUCCESSFUL : MOVEMENT_FAILED + if (. == MOVEMENT_SUCCESSFUL) + set_dir(get_dir(old_T, newloc)) + // Apply movement delay. + // Player movement has more factors but its all in the client and fixing that would be its own project. + SetMoveCooldown(movement_delay()) + return \ No newline at end of file diff --git a/code/modules/ai/say_list.dm b/code/modules/ai/say_list.dm new file mode 100644 index 00000000000..773c0e78172 --- /dev/null +++ b/code/modules/ai/say_list.dm @@ -0,0 +1,125 @@ +// A simple datum that just holds many lists of lines for mobs to pick from. +// This is its own datum in order to be able to have different types of mobs be able to use the same lines if desired, +// even when inheritence wouldn't be able to do so. + +// Also note this also contains emotes, despite its name. +// and now sounds because its probably better that way. + +/mob/living + var/datum/say_list/say_list = null + var/say_list_type = /datum/say_list // Type to give us on initialization. Default has empty lists, so the mob will be silent. + +/mob/living/Initialize() + if (say_list_type) + say_list = new say_list_type(src) + return ..() + +/mob/living/Destroy() + QDEL_NULL(say_list) + return ..() + + +/datum/say_list + var/list/speak = list() // Things the mob might say if it talks while idle. + var/list/emote_hear = list() // Hearable emotes it might perform + var/list/emote_see = list() // Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/list/say_understood = list() // When accepting an order. + var/list/say_cannot = list() // When they cannot comply. + var/list/say_maybe_target = list() // When they briefly see something. + var/list/say_got_target = list() // When a target is first assigned. + var/list/say_threaten = list() // When threatening someone. + var/list/say_stand_down = list() // When the threatened thing goes away. + var/list/say_escalate = list() // When the threatened thing doesn't go away. + + var/threaten_sound = null // Sound file played when the mob's AI calls threaten_target() for the first time. + var/stand_down_sound = null // Sound file played when the mob's AI loses sight of the threatened target. + + + + + + + +// Subtypes. + +// This one's pretty dumb, but pirates are dumb anyways and it makes for a good test. +/datum/say_list/pirate + speak = list("Yarr!") + + say_understood = list("Alright, matey.") + say_cannot = list("No, matey.") + say_maybe_target = list("Eh?") + say_got_target = list("Yarrrr!") + say_threaten = list("You best leave, this booty is mine.", "No plank to walk on, just walk away.") + say_stand_down = list("Good.") + say_escalate = list("Yarr! The booty is mine!") + +// Mercs! +/datum/say_list/merc + speak = list("When are we gonna get out of this chicken-shit outfit?", + "Wish I had better equipment...", + "I knew I should have been a line chef...", + "Fuckin' helmet keeps fogging up.", + "Anyone else smell that?") + emote_see = list("sniffs", "coughs", "taps his foot", "looks around", "checks his equipment") + + say_understood = list("Understood!", "Affirmative!") + say_cannot = list("Negative!") + say_maybe_target = list("Who's there?") + say_got_target = list("Engaging!") + say_threaten = list("Get out of here!", "Hey! Private Property!") + say_stand_down = list("Good.") + say_escalate = list("Your funeral!", "Bring it!") + + threaten_sound = 'sound/weapons/TargetOn.ogg' + stand_down_sound = 'sound/weapons/TargetOff.ogg' + +/datum/say_list/malf_drone + speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.") + emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity") + + say_understood = list("Affirmative.", "Positive.") + say_cannot = list("Denied.", "Negative.") + say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.") + say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.") + say_threaten = list("Motion detected, judging target...") + say_stand_down = list("Visual lost.", "Error: Target not found.") + say_escalate = list("Viable target found. Removing.", "Engaging target.", "Target judgement complete. Removal required.") + + threaten_sound = 'sound/effects/turret/move1.wav' + stand_down_sound = 'sound/effects/turret/move2.wav' + +/datum/say_list/crab + emote_hear = list("clicks") + emote_see = list("clacks") + +/datum/say_list/spider + emote_hear = list("chitters") + +/datum/say_list/hivebot + speak = list( + "Resuming task: Protect area.", + "No threats found.", + "Error: No targets found." + ) + emote_hear = list("hums ominously", "whirrs softly", "grinds a gear") + emote_see = list("looks around the area", "turns from side to side") + say_understood = list("Affirmative.", "Positive.") + say_cannot = list("Denied.", "Negative.") + say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.") + say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.") + +/datum/say_list/lizard + emote_hear = list("hisses") + +/datum/say_list/crab + emote_hear = list("clicks") + emote_see = list("clacks") + +/datum/say_list/parrot + + + speak = list("Hi","Hello!","Cracker?") + emote_hear = list("squawks","bawks") + emote_see = list("flutters its wings") \ No newline at end of file diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 01772afb850..df0b7f08c70 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -38,7 +38,7 @@ affecting.take_external_damage(1, 0) H.updatehealth() else if(ismouse(target)) - var/mob/living/simple_animal/mouse/M = target + var/mob/living/simple_animal/friendly/mouse/M = target visible_message("SPLAT!") M.splat() playsound(target.loc, 'sound/effects/snap.ogg', 50, 1) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 89ad565661d..07d5ca44b9f 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -409,6 +409,11 @@ client/verb/character_setup() if(istype(M)) M.OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) + var/datum/click_handler/build_mode/B = M.GetClickHandler() + if (istype(B)) + if(B.current_build_mode && src_control == "mapwindow.map" && src_control == over_control) + build_drag(src,B.current_build_mode,src_object,over_object,src_location,over_location,src_control,over_control,params) + /client/verb/toggle_fullscreen() set name = "Toggle Fullscreen" set category = "OOC" diff --git a/code/modules/events/infestation.dm b/code/modules/events/infestation.dm index 6a8fe212dd2..7f0783663c1 100644 --- a/code/modules/events/infestation.dm +++ b/code/modules/events/infestation.dm @@ -38,11 +38,11 @@ vermin = rand(0,2) switch(vermin) if(VERM_MICE) - spawn_types = list(/mob/living/simple_animal/mouse) // The base mouse type selects a random color for us + spawn_types = list(/mob/living/simple_animal/friendly/mouse) // The base mouse type selects a random color for us max_number = 12 vermstring = "mice" if(VERM_LIZARDS) - spawn_types = list(/mob/living/simple_animal/lizard) + spawn_types = list(/mob/living/simple_animal/friendly/lizard) max_number = 6 vermstring = "lizards" if(VERM_SPIDERS) diff --git a/code/modules/events/whale_migration.dm b/code/modules/events/whale_migration.dm index e5c9888c69f..0bf89121a34 100644 --- a/code/modules/events/whale_migration.dm +++ b/code/modules/events/whale_migration.dm @@ -24,7 +24,7 @@ T = get_random_edge_turf(dir, TRANSITIONEDGE + 5, station_level) oppT = get_random_edge_turf(GLOB.reverse_dir[dir], TRANSITIONEDGE + 5, station_level) var/mob/living/simple_animal/hostile/retaliate/space_whale/adult - var/mob/living/simple_animal/juvenile_space_whale/child + var/mob/living/simple_animal/friendly/juvenile_space_whale/child if(prob(75)) adult = new(T) child = new(get_step(T, dir)) diff --git a/code/modules/goals/definitions/personal_achievement_specific_object.dm b/code/modules/goals/definitions/personal_achievement_specific_object.dm index 1e0d0817a1d..1d253bcf00f 100644 --- a/code/modules/goals/definitions/personal_achievement_specific_object.dm +++ b/code/modules/goals/definitions/personal_achievement_specific_object.dm @@ -58,8 +58,8 @@ /datum/goal/achievement/specific_object/pet possible_objects = list( - /mob/living/simple_animal/corgi, - /mob/living/simple_animal/cat + /mob/living/simple_animal/friendly/corgi, + /mob/living/simple_animal/friendly/cat ) /datum/goal/achievement/specific_object/pet/update_strings() diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index e4ff5fc4ea5..1655187fa3d 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -111,10 +111,10 @@ return if(!istype(target)) - if(istype(target, /mob/living/simple_animal/mouse)) + if(istype(target, /mob/living/simple_animal/friendly/mouse)) new /obj/item/remains/mouse(get_turf(target)) qdel(target) - else if(istype(target, /mob/living/simple_animal/lizard)) + else if(istype(target, /mob/living/simple_animal/friendly/lizard)) new /obj/item/remains/lizard(get_turf(target)) qdel(target) return @@ -784,8 +784,8 @@ if(istype(product,/mob/living)) product.visible_message("The pod disgorges [product]!") handle_living_product(product) - if(istype(product,/mob/living/simple_animal/mushroom)) // Gross. - var/mob/living/simple_animal/mushroom/mush = product + if(istype(product,/mob/living/simple_animal/friendly/mushroom)) // Gross. + var/mob/living/simple_animal/friendly/mushroom/mush = product mush.seed = src // When the seed in this machine mutates/is modified, the tray seed value diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index dee324e4f44..3a0a61c5207 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -203,7 +203,7 @@ display_name = "killer tomato plant" mutants = null can_self_harvest = TRUE - has_mob_product = /mob/living/simple_animal/tomato + has_mob_product = /mob/living/simple_animal/friendly/tomato /datum/seed/tomato/killer/New() ..() @@ -403,7 +403,7 @@ display_name = "walking mushrooms" mutants = null can_self_harvest = TRUE - has_mob_product = /mob/living/simple_animal/mushroom + has_mob_product = /mob/living/simple_animal/friendly/mushroom /datum/seed/mushroom/plump/walking/New() ..() diff --git a/code/modules/item_worth/worths_list.dm b/code/modules/item_worth/worths_list.dm index d159e5a3b99..4befa95ac5c 100644 --- a/code/modules/item_worth/worths_list.dm +++ b/code/modules/item_worth/worths_list.dm @@ -614,8 +614,8 @@ var/list/worths = list( /mob/living/silicon/ai = 50000, /mob/living/silicon = 5000, /mob/living/simple_animal/borer = 10000, - /mob/living/simple_animal/corgi/Ian = 1000, //Ian is valuable, - /mob/living/simple_animal/cow = 2000, //Cow expensive, + /mob/living/simple_animal/friendly/corgi/Ian = 1000, //Ian is valuable, + /mob/living/simple_animal/friendly/cow = 2000, //Cow expensive, /mob/living/simple_animal/hostile = 1000, /mob/living/simple_animal = 500, /mob/living = 100, diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm index b6f371a0594..728206090a8 100644 --- a/code/modules/mob/animations.dm +++ b/code/modules/mob/animations.dm @@ -121,6 +121,30 @@ note dizziness decrements automatically in the mob's Life() proc. //reset the pixel offsets to zero is_floating = 0 +/atom/movable/proc/do_windup_animation(atom/A, windup_time) + var/pixel_x_diff = 0 + var/pixel_y_diff = 0 + var/direction = get_dir(src, A) + if(direction & NORTH) + pixel_y_diff = -8 + else if(direction & SOUTH) + pixel_y_diff = 8 + + if(direction & EAST) + pixel_x_diff = -8 + else if(direction & WEST) + pixel_x_diff = 8 + + var/default_pixel_x = initial(pixel_x) + var/default_pixel_y = initial(pixel_y) + var/mob/mob = src + if(istype(mob)) + default_pixel_x = mob.default_pixel_x + default_pixel_y = mob.default_pixel_y + + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = windup_time - 2) + animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2) + /atom/movable/proc/do_attack_animation(atom/A) var/pixel_x_diff = 0 diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 7428252ecb7..464beee87aa 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -36,10 +36,7 @@ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages if(!say_understands(speaker,language)) - if(istype(speaker,/mob/living/simple_animal)) - var/mob/living/simple_animal/S = speaker - message = pick(S.speak) - else + if(!istype(speaker,/mob/living/simple_animal)) if(language) message = language.scramble(message, languages) else @@ -119,8 +116,9 @@ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages if(!say_understands(speaker,language)) if(istype(speaker,/mob/living/simple_animal)) - var/mob/living/simple_animal/S = speaker - if(S.speak && S.speak.len) + var/mob/living/M = speaker + var/datum/say_list/S = M.say_list + if(S && S.speak && S.speak.len) message = pick(S.speak) else return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 3af4913c8b8..b62ac4da330 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -5,6 +5,8 @@ else add_to_living_mob_list() + selected_image = image(icon('icons/misc/buildmode.dmi'), loc = src, icon_state = "ai_sel") + /mob/living/examine(mob/user, distance, infix, suffix) . = ..() if (admin_paralyzed) @@ -812,6 +814,8 @@ default behaviour is: if(auras) for(var/a in auras) remove_aura(a) + + qdel(selected_image) return ..() /mob/living/proc/melee_accuracy_mods() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 0f857394333..850b3067541 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -359,3 +359,33 @@ fire_act(air, temperature) FireBurn(0.4*vsc.fire_firelevel_multiplier, temperature, pressure) . = (health <= 0) ? ..() : FALSE + +// Applies direct "cold" damage while checking protection against the cold. +/mob/living/proc/inflict_cold_damage(amount) + amount *= 1 - get_cold_protection(50) // Within spacesuit protection. + if(amount > 0) + adjustFireLoss(amount) + +// Ditto, but for "heat". +/mob/living/proc/inflict_heat_damage(amount) + amount *= 1 - get_heat_protection(10000) // Within firesuit protection. + if(amount > 0) + adjustFireLoss(amount) + +// and one for electricity because why not +/mob/living/proc/inflict_shock_damage(amount) + electrocute_act(amount, null, 1, pick(BP_HEAD, BP_CHEST, BP_GROIN)) + +// also one for water (most things resist it entirely, except for slimes) +/mob/living/proc/inflict_water_damage(amount) + amount *= 1 + if(amount > 0) + adjustToxLoss(amount) + +// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents) +/mob/living/proc/inflict_poison_damage(amount) + if(isSynthetic()) + return + amount *= 1 + if(amount > 0) + adjustToxLoss(amount) \ No newline at end of file diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index e0dcb922fd8..dd1abea1c87 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -7,6 +7,8 @@ var/maxHealth = 100 //Maximum health that should be possible. var/health = 100 //A mob's health + var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. + var/hud_updateflag = 0 //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS // what a joke @@ -17,6 +19,7 @@ //var/halloss = 0 //Hallucination damage. 'Fake' damage obtained through hallucinating or the holodeck. Sleeping should cause it to wear off. var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. + var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN var/t_phoron = null var/t_oxygen = null @@ -55,3 +58,8 @@ var/ghosted = 0 //For checks as to why a player has disconnected (can AI take over? etc.) var/admin_paralyzed = FALSE + + // var/nutrition = 400 + // var/max_nutrition = MAX_NUTRITION + + var/image/selected_image = null // Used for buildmode AI control stuff. \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 89ef5fd5825..587195b2962 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -314,7 +314,7 @@ var/grabbed_something = 0 for(var/mob/M in T) - if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse)) + if(istype(M,/mob/living/simple_animal/friendly/lizard) || istype(M,/mob/living/simple_animal/friendly/mouse)) src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.") new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) qdel(M) diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm index c41eafad08f..375718d0355 100644 --- a/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm +++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm @@ -3,7 +3,6 @@ turns_per_move = 5 speed = 4 mob_size = MOB_SMALL - emote_see = list("glubs", "blubs", "bloops") // They only really care if there's water around them or not. max_gas = list() @@ -17,6 +16,8 @@ bone_material = MATERIAL_BONE_FISH skin_material = MATERIAL_SKIN_FISH + say_list_type = /datum/say_list/aquatic + /mob/living/simple_animal/aquatic/New() ..() default_pixel_x = rand(-12,12) @@ -34,3 +35,6 @@ /mob/living/simple_animal/aquatic/handle_atmos(var/atmos_suitable = 1) . = ..(atmos_suitable = submerged()) + +/datum/say_list/aquatic + emote_see = list("glubs", "blubs", "bloops") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm index 61894397647..94b2d5d7c70 100644 --- a/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm +++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm @@ -5,13 +5,14 @@ natural_weapon = /obj/item/natural_weapon/bite speed = 4 mob_size = MOB_MEDIUM - emote_see = list("gnashes") // They only really care if there's water around them or not. max_gas = list() min_gas = list() minbodytemp = 0 + ai_holder_type = /datum/ai_holder/simple_animal/melee/aquatic + /mob/living/simple_animal/hostile/aquatic/Life() if(!submerged()) if(icon_state == icon_living) @@ -23,5 +24,5 @@ /mob/living/simple_animal/hostile/aquatic/handle_atmos(var/atmos_suitable = 1) . = ..(atmos_suitable = submerged()) -/mob/living/simple_animal/hostile/aquatic/can_act() - . = ..() && submerged() +/datum/ai_holder/simple_animal/melee/aquatic/can_act() + . = ..() && holder.submerged() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm index 673d9deb2f9..44f0e1f934e 100644 --- a/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm +++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm @@ -5,13 +5,15 @@ natural_weapon = /obj/item/natural_weapon/bite speed = 4 mob_size = MOB_MEDIUM - emote_see = list("gnashes") // They only really care if there's water around them or not. max_gas = list() min_gas = list() minbodytemp = 0 + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/aquatic + + /mob/living/simple_animal/hostile/retaliate/aquatic/Life() if(!submerged()) if(icon_state == icon_living) @@ -23,5 +25,5 @@ /mob/living/simple_animal/hostile/retaliate/aquatic/handle_atmos(var/atmos_suitable = 1) . = ..(atmos_suitable = submerged()) -/mob/living/simple_animal/hostile/retaliate/aquatic/can_act() - . = ..() && submerged() +/datum/ai_holder/simple_animal/retaliate/aquatic/can_act() + . = ..() && holder.submerged() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm index a413dbd6801..aaeef63e9be 100644 --- a/code/modules/mob/living/simple_animal/borer/borer.dm +++ b/code/modules/mob/living/simple_animal/borer/borer.dm @@ -3,7 +3,7 @@ real_name = "cortical borer" desc = "A small, quivering sluglike creature." speak_emote = list("chirrups") - emote_hear = list("chirrups") + // emote_hear = list("chirrups") response_help = "pokes" response_disarm = "prods" response_harm = "stomps on" @@ -13,11 +13,9 @@ icon_dead = "brainslug_dead" speed = 5 a_intent = I_HURT - stop_automated_movement = 1 status_flags = CANPUSH natural_weapon = /obj/item/natural_weapon/bite/weak friendly = "prods" - wander = 0 pass_flags = PASS_FLAG_TABLE universal_understand = TRUE holder_type = /obj/item/holder/borer @@ -92,7 +90,7 @@ generation = gen set_borer_name() - if(!roundstart) + if(!roundstart) request_player() aura_image = create_aura_image(src) diff --git a/code/modules/mob/living/simple_animal/combat.dm b/code/modules/mob/living/simple_animal/combat.dm new file mode 100644 index 00000000000..2e33a1cd402 --- /dev/null +++ b/code/modules/mob/living/simple_animal/combat.dm @@ -0,0 +1,219 @@ +// Does a melee attack. +/mob/living/simple_animal/proc/attack_target(atom/A) + set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up. + + if(!A.Adjacent(src)) + return ATTACK_FAILED + var/turf/their_T = get_turf(A) + + face_atom(A) + + if(melee_attack_delay) + melee_pre_animation(A) + . = ATTACK_SUCCESSFUL //Shoving this in here as a 'best guess' since this proc is about to sleep and return and we won't be able to know the real value + handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. + + // Cooldown testing is done at click code (for players) and interface code (for AI). + setClickCooldown(get_attack_speed(get_natural_weapon())) + + // Returns a value, but will be lost if + . = do_attack(A, their_T) + + if(melee_attack_delay) + melee_post_animation(A) + +// This does the actual attack. +// This is a seperate proc for the purposes of attack animations. +// A is the thing getting attacked, T is the turf A is/was on when attack_target was called. +// Damage is handled by the mobs natural weapon. +/mob/living/simple_animal/proc/do_attack(atom/A, turf/T) + face_atom(A) + var/missed = FALSE + if(!isturf(A) && !(A in T) ) // Turfs don't contain themselves so checking contents is pointless if we're targeting a turf. + missed = TRUE + else if(!T.AdjacentQuick(src)) + missed = TRUE + + if(missed) // Most likely we have a slow attack and they dodged it or we somehow got moved. + // admin_attack_log(src, A, "Animal-attacked (dodged)", admin_notify = FALSE) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + visible_message(SPAN_WARNING("\The [src] misses their attack.")) + return FALSE + + var/obj/item/natural_weapon/weapon = get_natural_weapon() + weapon.resolve_attackby(A, src) + + return TRUE + +// Override for special effects after a successful attack, like injecting poison or stunning the target. +/mob/living/simple_animal/proc/apply_melee_effects(atom/A) + return + +// Override to modify the amount of damage the mob does conditionally. +// This must return the amount of outgoing damage. +// Note that this is done before mob modifiers scale the damage. +/mob/living/simple_animal/proc/apply_bonus_melee_damage(atom/A, damage_amount) + return damage_amount + +//The actual top-level ranged attack proc +/mob/living/simple_animal/proc/shoot_target(atom/A) + set waitfor = FALSE + setClickCooldown(get_attack_speed()) + + face_atom(A) + + if(ranged_attack_delay) + ranged_pre_animation(A) + handle_attack_delay(A, ranged_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. + + if(needs_reload) + if(reload_count >= reload_max) + try_reload() + return FALSE + + visible_message("\The [src] fires at \the [A]!") + shoot(A) + if(casingtype) + new casingtype(loc) + + if(ranged_attack_delay) + ranged_post_animation(A) + + return TRUE + + +// Shoot a bullet at something. +/mob/living/simple_animal/proc/shoot(atom/A) + if(A == get_turf(src)) + return + + face_atom(A) + + var/obj/item/projectile/P = new projectiletype(src.loc) + if(!P) + return + + // If the projectile has its own sound, use it. + // Otherwise default to the mob's firing sound. + playsound(src, P.fire_sound ? P.fire_sound : projectilesound, 80, 1) + + // For some reason there isn't an argument for accuracy, so access the projectile directly instead. + // Also, placing dispersion here instead of in forced_spread will randomize the chosen angle between dispersion and -dispersion in fire() instead of having to do that here. + // P.accuracy += calculate_accuracy() + P.dispersion += calculate_dispersion() + + P.launch(target = A) + if(needs_reload) + reload_count++ + +/mob/living/simple_animal/proc/try_reload() + set waitfor = FALSE + set_AI_busy(TRUE) + + if(do_after(src, reload_time)) + if(reload_sound) + playsound(src, reload_sound, 70, 1) + reload_count = 0 + . = TRUE + else + . = FALSE + set_AI_busy(FALSE) + +/mob/living/simple_animal/proc/calculate_dispersion() + . = projectile_dispersion // Start with the basic var. + + // Make sure we don't go under zero dispersion. + . = max(., 0) + +/mob/living/simple_animal/proc/calculate_accuracy() + . = projectile_accuracy // Start with the basic var. + +// Can we currently do a special attack? +/mob/living/simple_animal/proc/can_special_attack(atom/A) + // Validity check. + if(!istype(A)) + return FALSE + + // Ability check. + if(isnull(special_attack_min_range) || isnull(special_attack_max_range)) + return FALSE + + // Distance check. + var/distance = get_dist(src, A) + if(distance < special_attack_min_range || distance > special_attack_max_range) + return FALSE + + // Cooldown check. + if(!isnull(special_attack_cooldown) && last_special_attack + special_attack_cooldown > world.time) + return FALSE + + // Charge check. + if(!isnull(special_attack_charges) && special_attack_charges <= 0) + return FALSE + + return TRUE + +// Should we do one? Used to make the AI not waste their special attacks. Only checked for AI. Players are free to screw up on their own. +/mob/living/simple_animal/proc/should_special_attack(atom/A) + return TRUE + +// Special attacks, like grenades or blinding spit or whatever. +// Don't override this, override do_special_attack() for your blinding spit/etc. +/mob/living/simple_animal/proc/special_attack_target(atom/A) + face_atom(A) + + if(special_attack_delay) + special_pre_animation(A) + handle_attack_delay(A, special_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. + + last_special_attack = world.time + if(do_special_attack(A)) + if(special_attack_charges) + special_attack_charges -= 1 + . = TRUE + else + . = FALSE + + if(special_attack_delay) + special_post_animation(A) + +// Override this for the actual special attack. +/mob/living/simple_animal/proc/do_special_attack(atom/A) + return FALSE + +// Sleeps the proc that called it for the correct amount of time. +// Also makes sure the AI doesn't do anything stupid in the middle of the delay. +/mob/living/simple_animal/proc/handle_attack_delay(atom/A, delay_amount) + set_AI_busy(TRUE) + + // Click delay modifiers also affect telegraphing time. + // This means berserked enemies will leave less time to dodge. + var/true_attack_delay = delay_amount + + setClickCooldown(true_attack_delay) // Insurance against a really long attack being longer than default click delay. + + sleep(true_attack_delay) + + set_AI_busy(FALSE) + + +// Override these four for special custom animations (like the GOLEM). +/mob/living/simple_animal/proc/melee_pre_animation(atom/A) + do_windup_animation(A, melee_attack_delay) + +/mob/living/simple_animal/proc/melee_post_animation(atom/A) + +/mob/living/simple_animal/proc/ranged_pre_animation(atom/A) + do_windup_animation(A, ranged_attack_delay) // Semi-placeholder. + +/mob/living/simple_animal/proc/ranged_post_animation(atom/A) + +/mob/living/simple_animal/proc/special_pre_animation(atom/A) + do_windup_animation(A, special_attack_delay) // Semi-placeholder. + +/mob/living/simple_animal/proc/special_post_animation(atom/A) + +/mob/living/simple_animal/proc/get_natural_weapon() + if(ispath(natural_weapon)) + natural_weapon = new natural_weapon(src) + return natural_weapon \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm index c34d3ee3c38..aac25eb5f4c 100644 --- a/code/modules/mob/living/simple_animal/constructs/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm @@ -2,16 +2,13 @@ name = "Construct" real_name = "Construct" desc = "" - speak = list("Hsssssssszsht.", "Hsssssssss...", "Tcshsssssssszht!") speak_emote = list("hisses") - emote_hear = list("wails","screeches") response_help = "thinks better of touching" response_disarm = "flailed at" response_harm = "punched" icon_dead = "shade_dead" speed = -1 a_intent = I_HURT - stop_automated_movement = 1 status_flags = CANPUSH universal_speak = FALSE universal_understand = TRUE @@ -37,6 +34,9 @@ var/nullblock = 0 var/list/construct_spells = list() + ai_holder_type = /datum/ai_holder/simple_animal/melee + say_list = /datum/say_list/construct + /mob/living/simple_animal/construct/cultify() return @@ -111,7 +111,7 @@ /mob/living/simple_animal/construct/armoured/Life() weakened = 0 if ((. = ..())) - return + return /mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P) if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) @@ -228,7 +228,7 @@ natural_weapon = /obj/item/natural_weapon/juggernaut/behemoth speed = 5 environment_smash = 2 - + resistance = 10 var/energy = 0 var/max_energy = 1000 @@ -363,3 +363,7 @@ if(25 to 49) healths.icon_state = "harvester_health5" if(1 to 24) healths.icon_state = "harvester_health6" else healths.icon_state = "harvester_health7" + +/datum/say_list/construct + speak = list("Hsssssssszsht.", "Hsssssssss...", "Tcshsssssssszht!") + emote_hear = list("wails","screeches") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/crow/crow.dm b/code/modules/mob/living/simple_animal/crow/crow.dm index 2919e9d601c..3d5ca509b50 100644 --- a/code/modules/mob/living/simple_animal/crow/crow.dm +++ b/code/modules/mob/living/simple_animal/crow/crow.dm @@ -18,23 +18,21 @@ mob_size = MOB_SMALL density = FALSE - speak = list("Caw.", "Caw?", "Caw!", "CAW.") speak_emote = list("caws") - emote_hear = list("caws") - emote_see = list("hops") natural_weapon = /obj/item/natural_weapon/crow_claws response_help = "pets" response_disarm = "gently moves aside" response_harm = "swats" - stop_automated_movement = TRUE universal_speak = TRUE pass_flags = PASS_FLAG_TABLE var/obj/item/storage/messenger/messenger_bag var/obj/item/card/id/access_card + say_list_type = /datum/say_list/crow + /obj/item/natural_weapon/crow_claws name = "claws" gender = PLURAL @@ -158,3 +156,7 @@ else overlays |= "cyber_dead" +/datum/say_list/crow + speak = list("Caw.", "Caw?", "Caw!", "CAW.") + emote_hear = list("caws") + emote_see = list("hops") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/defense.dm b/code/modules/mob/living/simple_animal/defense.dm new file mode 100644 index 00000000000..2fb6239ce6b --- /dev/null +++ b/code/modules/mob/living/simple_animal/defense.dm @@ -0,0 +1,133 @@ +/mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj) + if(!Proj || Proj.nodamage) + return + + var/damage = Proj.damage + if(Proj.damtype == STUN) + damage = Proj.damage / 6 + if(Proj.damtype == BRUTE) + damage = Proj.damage / 2 + if(Proj.damtype == BURN) + damage = Proj.damage / 1.5 + if(Proj.agony) + damage += Proj.agony / 6 + if(health < Proj.agony * 3) + Paralyse(Proj.agony / 20) + visible_message(SPAN_WARNING("[src] is stunned momentarily!")) + + bullet_impact_visuals(Proj) + adjustBruteLoss(damage) + Proj.on_hit(src) + return 0 + +/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M as mob) + ..() + + switch(M.a_intent) + + if(I_HELP) + if (health > 0) + M.visible_message(SPAN_NOTICE("[M] [response_help] \the [src].")) + M.update_personal_goal(/datum/goal/achievement/specific_object/pet, type) + + if(I_DISARM) + M.visible_message(SPAN_NOTICE("[M] [response_disarm] \the [src].")) + M.do_attack_animation(src) + //TODO: Push the mob away or something + + if(I_HURT) + var/dealt_damage = harm_intent_damage + var/harm_verb = response_harm + if(ishuman(M) && M.species) + var/datum/unarmed_attack/attack = M.get_unarmed_attack(src) + dealt_damage = attack.damage <= dealt_damage ? dealt_damage : attack.damage + harm_verb = pick(attack.attack_verb) + if(attack.sharp || attack.edge) + adjustBleedTicks(dealt_damage) + + adjustBruteLoss(dealt_damage) + M.visible_message(SPAN_WARNING("[M] [harm_verb] \the [src]!")) + M.do_attack_animation(src) + + return + +/mob/living/simple_animal/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/stack/medical)) + if(stat != DEAD) + var/obj/item/stack/medical/MED = O + if(!MED.animal_heal) + to_chat(user, SPAN_NOTICE("That [MED] won't help \the [src] at all!")) + return + if(health < maxHealth) + if(MED.can_use(1)) + adjustBruteLoss(-MED.animal_heal) + visible_message(SPAN_NOTICE("[user] applies the [MED] on [src].")) + MED.use(1) + else + to_chat(user, SPAN_NOTICE("\The [src] is dead, medical items won't bring \him back to life.")) + return + + if(istype(O, /obj/item/device/flash)) + if(stat != DEAD) + O.attack(src, user, user.zone_sel ? user.zone_sel.selecting : ran_zone()) + return + + if(meat_type && (stat == DEAD) && meat_amount) + if(istype(O, /obj/item/material/knife/kitchen/cleaver)) + var/victim_turf = get_turf(src) + if(!locate(/obj/structure/table, victim_turf)) + to_chat(user, SPAN_NOTICE("You need to place \the [src] on a table to butcher it.")) + return + var/time_to_butcher = (mob_size) + to_chat(user, SPAN_NOTICE("You begin harvesting \the [src].")) + if(do_after(user, time_to_butcher, src, do_flags = DO_DEFAULT & ~DO_BOTH_CAN_TURN)) + if(prob(user.skill_fail_chance(SKILL_COOKING, 60, SKILL_ADEPT))) + to_chat(user, SPAN_NOTICE("You botch harvesting \the [src], and ruin some of the meat in the process.")) + subtract_meat(user) + return + else + harvest(user, user.get_skill_value(SKILL_COOKING)) + return + else + to_chat(user, SPAN_NOTICE("Your hand slips with your movement, and some of the meat is ruined.")) + subtract_meat(user) + return + + else + if(!O.force) + visible_message(SPAN_NOTICE("[user] gently taps [src] with \the [O].")) + else + O.attack(src, user, user.zone_sel ? user.zone_sel.selecting : ran_zone()) + +/mob/living/simple_animal/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) + + visible_message(SPAN_DANGER("\The [src] has been attacked with \the [O] by [user]!")) + + if(O.force <= resistance) + to_chat(user, SPAN_DANGER("This weapon is ineffective; it does no damage.")) + return FALSE + + var/damage = O.force + if (O.damtype == PAIN) + damage = 0 + if (O.damtype == STUN) + damage = (O.force / 8) + if(supernatural && istype(O,/obj/item/nullrod)) + damage *= 2 + purge = 3 + adjustBruteLoss(damage) + if(O.edge || O.sharp) + adjustBleedTicks(damage) + + return TRUE + +/mob/living/simple_animal/proc/reflect_unarmed_damage(var/mob/living/carbon/human/attacker, var/damage_type, var/description) + if(attacker.a_intent == I_HURT) + var/hand_hurtie + if(attacker.hand) + hand_hurtie = BP_L_HAND + else + hand_hurtie = BP_R_HAND + attacker.apply_damage(rand(return_damage_min, return_damage_max), damage_type, hand_hurtie, used_weapon = description) + if(rand(25)) + to_chat(attacker, SPAN_WARNING("Your attack has no obvious effect on \the [src]'s [description]!")) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/familiars/familiars.dm b/code/modules/mob/living/simple_animal/familiars/familiars.dm index 39ed2804fc5..8f6c5e4fceb 100644 --- a/code/modules/mob/living/simple_animal/familiars/familiars.dm +++ b/code/modules/mob/living/simple_animal/familiars/familiars.dm @@ -122,7 +122,7 @@ /mob/living/simple_animal/familiar/pet //basically variants of normal animals with spells. icon = 'icons/mob/simple_animal/animal.dmi' - var/icon_rest //so that we can have resting little guys. + icon_rest //so that we can have resting little guys. /mob/living/simple_animal/familiar/pet/Life() . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/_friendly.dm b/code/modules/mob/living/simple_animal/friendly/_friendly.dm new file mode 100644 index 00000000000..ed792646a5f --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/_friendly.dm @@ -0,0 +1,3 @@ +/mob/living/simple_animal/animal/passive + ai_holder_type = /datum/ai_holder/simple_animal/passive + mob_bump_flag = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 3aa102002c0..20dfe0a1ef0 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -1,16 +1,12 @@ //Cat -/mob/living/simple_animal/cat +/mob/living/simple_animal/friendly/cat name = "cat" desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." icon_state = "cat2" item_state = "cat2" icon_living = "cat2" icon_dead = "cat2_dead" - speak = list("Meow!","Esp!","Purr!","HSSSSS") speak_emote = list("purrs", "meows") - emote_hear = list("meows","mews") - emote_see = list("shakes their head", "shivers") - speak_chance = 1 turns_per_move = 5 see_in_dark = 6 response_help = "pets" @@ -27,27 +23,29 @@ skin_material = MATERIAL_SKIN_FUR_ORANGE var/turns_since_scan = 0 - var/mob/living/simple_animal/mouse/movement_target - var/mob/flee_target + var/mob/living/simple_animal/friendly/mouse/movement_target + + ai_holder_type = /datum/ai_holder/simple_animal/passive/cat + say_list_type = /datum/say_list/cat -/mob/living/simple_animal/cat/Life() +/mob/living/simple_animal/friendly/cat/Life() . = ..() if(!.) return FALSE //MICE! if((src.loc) && isturf(src.loc)) if(!resting && !buckled) - for(var/mob/living/simple_animal/mouse/M in loc) + for(var/mob/living/simple_animal/friendly/mouse/M in loc) if(!M.stat) M.splat() visible_emote(pick("bites \the [M]!","toys with \the [M].","chomps on \the [M]!")) movement_target = null - stop_automated_movement = 0 + set_AI_busy(FALSE) break - for(var/mob/living/simple_animal/mouse/snack in oview(src,5)) + for(var/mob/living/simple_animal/friendly/mouse/snack in oview(src,5)) if(snack.stat < DEAD && prob(15)) audible_emote(pick("hisses and spits!","mrowls fiercely!","eyes [snack] hungrily.")) break @@ -59,9 +57,7 @@ walk_to(src,0) turns_since_scan = 0 - if (flee_target) //fleeing takes precendence - handle_flee_target() - else + if (!ai_holder.stance == STANCE_FLEE) handle_movement_target() if(prob(2)) //spooky @@ -76,68 +72,31 @@ var/atom/A = pick(visible) visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") -/mob/living/simple_animal/cat/proc/handle_movement_target() +/mob/living/simple_animal/friendly/cat/proc/handle_movement_target() //if our target is neither inside a turf or inside a human(???), stop if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) movement_target = null - stop_automated_movement = 0 + set_AI_busy(FALSE) //if we have no target or our current one is out of sight/too far away if( !movement_target || !(movement_target.loc in oview(src, 4)) ) movement_target = null - stop_automated_movement = 0 - for(var/mob/living/simple_animal/mouse/snack in oview(src)) //search for a new target + set_AI_busy(FALSE) + for(var/mob/living/simple_animal/friendly/mouse/snack in oview(src)) //search for a new target if(isturf(snack.loc) && !snack.stat) movement_target = snack break if(movement_target) - stop_automated_movement = 1 + set_AI_busy(TRUE) walk_to(src,movement_target,0,3) -/mob/living/simple_animal/cat/proc/handle_flee_target() - //see if we should stop fleeing - if (stat != CONSCIOUS || (flee_target && !(flee_target.loc in view(src)))) - flee_target = null - stop_automated_movement = 0 - - if (flee_target) - if(prob(25)) say("HSSSSS") - stop_automated_movement = 1 - walk_away(src, flee_target, 7, 2) - -/mob/living/simple_animal/cat/proc/set_flee_target(atom/A) - if(A) - flee_target = A - turns_since_scan = 5 - -/mob/living/simple_animal/cat/attackby(var/obj/item/O, var/mob/user) - . = ..() - if(O.force) - set_flee_target(user? user : src.loc) - -/mob/living/simple_animal/cat/attack_hand(mob/living/carbon/human/M as mob) - . = ..() - if(M.a_intent == I_HURT) - set_flee_target(M) - -/mob/living/simple_animal/cat/ex_act() - . = ..() - set_flee_target(src.loc) - -/mob/living/simple_animal/cat/bullet_act(var/obj/item/projectile/proj) - . = ..() - set_flee_target(proj.firer? proj.firer : src.loc) - -/mob/living/simple_animal/cat/hitby(atom/movable/AM, var/datum/thrownthing/TT) - . = ..() - set_flee_target(TT.thrower? TT.thrower : src.loc) //Basic friend AI -/mob/living/simple_animal/cat/fluff +/mob/living/simple_animal/friendly/cat/fluff var/mob/living/carbon/human/friend var/befriend_job = null -/mob/living/simple_animal/cat/fluff/handle_movement_target() +/mob/living/simple_animal/friendly/cat/fluff/handle_movement_target() if (friend) var/follow_dist = 4 if (friend.stat >= DEAD || friend.is_asystole()) //danger @@ -148,13 +107,13 @@ var/current_dist = get_dist(src, friend) if (movement_target != friend) - if (current_dist > follow_dist && !istype(movement_target, /mob/living/simple_animal/mouse) && (friend in oview(src))) + if (current_dist > follow_dist && !istype(movement_target, /mob/living/simple_animal/friendly/mouse) && (friend in oview(src))) //stop existing movement walk_to(src,0) turns_since_scan = 0 //walk to friend - stop_automated_movement = 1 + set_AI_busy(TRUE) movement_target = friend walk_to(src, movement_target, near_dist, 4) @@ -162,14 +121,14 @@ else if (current_dist <= near_dist) walk_to(src,0) movement_target = null - stop_automated_movement = 0 + set_AI_busy(FALSE) if (prob(10)) say("Meow!") if (!friend || movement_target != friend) ..() -/mob/living/simple_animal/cat/fluff/Life() +/mob/living/simple_animal/friendly/cat/fluff/Life() . = ..() if(!.) return FALSE @@ -191,7 +150,7 @@ var/verb = pick("meows", "mews", "mrowls") audible_emote("[verb] anxiously.") -/mob/living/simple_animal/cat/fluff/verb/become_friends() +/mob/living/simple_animal/friendly/cat/fluff/verb/become_friends() set name = "Become Friends" set category = "IC" set src in view(1) @@ -215,7 +174,7 @@ return //RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_animal/cat/fluff/Runtime +/mob/living/simple_animal/friendly/cat/fluff/Runtime name = "Runtime" desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." gender = FEMALE @@ -225,7 +184,7 @@ icon_dead = "cat_dead" skin_material = MATERIAL_SKIN_FUR_BLACK -/mob/living/simple_animal/cat/kitten +/mob/living/simple_animal/friendly/cat/kitten name = "kitten" desc = "D'aaawwww." icon_state = "kitten" @@ -244,7 +203,7 @@ gender = MALE icon_state = "cat3" -/mob/living/simple_animal/cat/fluff/bones +/mob/living/simple_animal/friendly/cat/fluff/bones name = "Bones" desc = "That's Bones the cat. He's a laid back, black cat. Meow." gender = MALE @@ -255,6 +214,15 @@ holder_type = /obj/item/holder/cat/fluff/bones var/friend_name = "Erstatz Vryroxes" -/mob/living/simple_animal/cat/kitten/New() +/mob/living/simple_animal/friendly/cat/kitten/New() gender = pick(MALE, FEMALE) ..() + +/datum/ai_holder/simple_animal/passive/cat + can_flee = TRUE + speak_chance = 1 + +/datum/say_list/cat + speak = list("Meow!","Esp!","Purr!","HSSSSS") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index 7d4ee58c555..04ac54d44f5 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -1,16 +1,12 @@ //Corgi -/mob/living/simple_animal/corgi +/mob/living/simple_animal/friendly/corgi name = "\improper corgi" real_name = "corgi" desc = "It's a corgi." icon_state = "corgi" icon_living = "corgi" icon_dead = "corgi_dead" - speak = list("YAP", "Woof!", "Bark!", "AUUUUUU") speak_emote = list("barks", "woofs") - emote_hear = list("barks", "woofs", "yaps","pants") - emote_see = list("shakes its head", "shivers") - speak_chance = 1 turns_per_move = 10 response_help = "pets" response_disarm = "bops" @@ -29,8 +25,11 @@ var/obj/item/inventory_head var/obj/item/inventory_back + ai_holder_type = /datum/ai_holder/simple_animal/passive/corgi + say_list_type = /datum/say_list/corgi + //IAN! SQUEEEEEEEEE~ -/mob/living/simple_animal/corgi/Ian +/mob/living/simple_animal/friendly/corgi/Ian name = "Ian" real_name = "Ian" //Intended to hold the name without altering it. gender = MALE @@ -41,7 +40,7 @@ response_disarm = "bops" response_harm = "kicks" -/mob/living/simple_animal/corgi/Ian/Life() +/mob/living/simple_animal/friendly/corgi/Ian/Life() . = ..() if(!.) return FALSE @@ -53,16 +52,16 @@ turns_since_scan = 0 if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) movement_target = null - stop_automated_movement = 0 + set_AI_busy(FALSE) if( !movement_target || !(movement_target.loc in oview(src, 3)) ) movement_target = null - stop_automated_movement = 0 + set_AI_busy(FALSE) for(var/obj/item/reagent_containers/food/snacks/S in oview(src,3)) if(isturf(S.loc) || ishuman(S.loc)) movement_target = S break if(movement_target) - stop_automated_movement = 1 + set_AI_busy(TRUE) step_to(src,movement_target,1) sleep(3) step_to(src,movement_target,1) @@ -97,7 +96,7 @@ name = "Corgi meat" desc = "Tastes like... well you know..." -/mob/living/simple_animal/corgi/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri +/mob/living/simple_animal/friendly/corgi/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri if(istype(O, /obj/item/newspaper)) if(!stat) for(var/mob/M in viewers(user, null)) @@ -110,7 +109,7 @@ else ..() -/mob/living/simple_animal/corgi/regenerate_icons() +/mob/living/simple_animal/friendly/corgi/regenerate_icons() overlays = list() if(inventory_head) @@ -131,7 +130,7 @@ if(back_icon) overlays += back_icon -/mob/living/simple_animal/corgi/puppy +/mob/living/simple_animal/friendly/corgi/puppy name = "\improper corgi puppy" real_name = "corgi" desc = "It's a corgi puppy." @@ -143,14 +142,14 @@ bone_amount = 3 //pupplies cannot wear anything. -/mob/living/simple_animal/corgi/puppy/OnTopic(mob/user, href_list) +/mob/living/simple_animal/friendly/corgi/puppy/OnTopic(mob/user, href_list) if(href_list["remove_inv"] || href_list["add_inv"]) to_chat(user, "You can't fit this on [src]") return TOPIC_HANDLED return ..() //LISA! SQUEEEEEEEEE~ -/mob/living/simple_animal/corgi/Lisa +/mob/living/simple_animal/friendly/corgi/Lisa name = "Lisa" real_name = "Lisa" gender = FEMALE @@ -165,13 +164,13 @@ var/puppies = 0 //Lisa already has a cute bow! -/mob/living/simple_animal/corgi/Lisa/OnTopic(mob/user, href_list) +/mob/living/simple_animal/friendly/corgi/Lisa/OnTopic(mob/user, href_list) if(href_list["remove_inv"] || href_list["add_inv"]) to_chat(user, "[src] already has a cute bow!") return TOPIC_HANDLED return ..() -/mob/living/simple_animal/corgi/Lisa/Life() +/mob/living/simple_animal/friendly/corgi/Lisa/Life() . = ..() if(!.) return FALSE @@ -183,7 +182,7 @@ var/alone = 1 var/ian = 0 for(var/mob/M in oviewers(7, src)) - if(istype(M, /mob/living/simple_animal/corgi/Ian)) + if(istype(M, /mob/living/simple_animal/friendly/corgi/Ian)) if(M.client) alone = 0 break @@ -195,7 +194,7 @@ if(alone && ian && puppies < 4) if(near_camera(src) || near_camera(ian)) return - new /mob/living/simple_animal/corgi/puppy(loc) + new /mob/living/simple_animal/friendly/corgi/puppy(loc) if(prob(1)) @@ -204,3 +203,11 @@ for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2)) set_dir(i) sleep(1) + +/datum/ai_holder/simple_animal/passive/corgi + speak_chance = 1 + +/datum/say_list/corgi + speak = list("YAP", "Woof!", "Bark!", "AUUUUUU") + emote_hear = list("barks", "woofs", "yaps","pants") + emote_see = list("shakes its head", "shivers") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index ae02a59a307..0315f036789 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -1,5 +1,5 @@ //Look Sir, free crabs! -/mob/living/simple_animal/crab +/mob/living/simple_animal/friendly/crab name = "crab" desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." icon_state = "crab" @@ -7,14 +7,10 @@ icon_dead = "crab_dead" mob_size = MOB_SMALL speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 1 turns_per_move = 5 response_help = "pets" response_disarm = "gently pushes aside" response_harm = "stomps" - stop_automated_movement = 1 friendly = "pinches" possession_candidate = 1 can_escape = TRUE //snip snip @@ -33,6 +29,9 @@ var/obj/item/inventory_head var/obj/item/inventory_mask + ai_holder_type = /datum/ai_holder/simple_animal/passive/crab + say_list_type = /datum/say_list/crab + /mob/living/simple_animal/crab/Life() . = ..() if(!.) @@ -51,3 +50,12 @@ name = "Coffee" real_name = "Coffee" desc = "It's Coffee, the other pet!" + + +/datum/ai_holder/simple_animal/passive/crab + speak_chance = 1 + + +/datum/say_list/crab + emote_hear = list("clicks") + emote_see = list("clacks") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 5a310dadecd..a9df36cba57 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -5,11 +5,7 @@ icon_state = "goat" icon_living = "goat" icon_dead = "goat_dead" - speak = list("EHEHEHEHEH","eh?") speak_emote = list("brays") - emote_hear = list("brays") - emote_see = list("shakes its head", "stamps a foot", "glares around") - speak_chance = 1 turns_per_move = 5 see_in_dark = 6 response_help = "pets" @@ -27,6 +23,15 @@ var/datum/reagents/udder = null + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/goat + say_list_type = /datum/say_list/goat + +/datum/ai_holder/simple_animal/retaliate/goat/react_to_attack(atom/movable/attacker) + . = ..() + + if(holder.stat == CONSCIOUS && prob(50)) + holder.visible_message("\The [holder] gets an evil-looking gleam in their eye.") + /mob/living/simple_animal/hostile/retaliate/goat/New() udder = new(50, src) ..() @@ -39,12 +44,14 @@ . = ..() if(.) //chance to go crazy and start wacking stuff - if(!enemies.len && prob(1)) - Retaliate() - - if(enemies.len && prob(10)) - enemies = list() - LoseTarget() + if(!length(ai_holder.attackers) && prob(1)) + var/list/nearby_stuff = hearers(src, ai_holder.vision_range) + if (length(nearby_stuff)) + ai_holder.react_to_attack(pick(nearby_stuff)) + + if(length(ai_holder.attackers) && prob(10)) + ai_holder.attackers = list() + ai_holder.lose_target() src.visible_message("\The [src] calms down.") if(stat == CONSCIOUS) @@ -70,11 +77,6 @@ var/step = get_step_to(src, food, 0) Move(step) -/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() - ..() - if(stat == CONSCIOUS && prob(50)) - visible_message("\The [src] gets an evil-looking gleam in their eye.") - /mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob) var/obj/item/reagent_containers/glass/G = O if(stat == CONSCIOUS && istype(G) && G.is_open_container()) @@ -88,18 +90,14 @@ ..() //cow -/mob/living/simple_animal/cow +/mob/living/simple_animal/friendly/cow name = "cow" desc = "Known for their milk, just don't tip them over." icon_state = "cow" icon_living = "cow" icon_dead = "cow_dead" icon_gib = "cow_gib" - speak = list("moo?","moo","MOOOOOO") speak_emote = list("moos","moos hauntingly") - emote_hear = list("brays") - emote_see = list("shakes its head") - speak_chance = 1 turns_per_move = 5 see_in_dark = 6 response_help = "pets" @@ -115,11 +113,14 @@ var/datum/reagents/udder = null -/mob/living/simple_animal/cow/New() + ai_holder_type = /datum/ai_holder/simple_animal/passive/cow + say_list_type = /datum/say_list/cow + +/mob/living/simple_animal/friendly/cow/New() udder = new(50, src) ..() -/mob/living/simple_animal/cow/attackby(var/obj/item/O as obj, var/mob/user as mob) +/mob/living/simple_animal/friendly/cow/attackby(var/obj/item/O as obj, var/mob/user as mob) var/obj/item/reagent_containers/glass/G = O if(stat == CONSCIOUS && istype(G) && G.is_open_container()) user.visible_message("[user] milks [src] using \the [O].") @@ -131,14 +132,14 @@ else ..() -/mob/living/simple_animal/cow/Life() +/mob/living/simple_animal/friendly/cow/Life() . = ..() if(!.) return FALSE if(udder && prob(5)) udder.add_reagent(/datum/reagent/drink/milk, rand(5, 10)) -/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) +/mob/living/simple_animal/friendly/cow/attack_hand(mob/living/carbon/M as mob) if(!stat && M.a_intent == I_DISARM && icon_state != icon_dead) M.visible_message("[M] tips over [src].","You tip over [src].") Weaken(30) @@ -154,18 +155,14 @@ else ..() -/mob/living/simple_animal/chick +/mob/living/simple_animal/friendly/chick name = "\improper chick" desc = "Adorable! They make such a racket though." icon_state = "chick" icon_living = "chick" icon_dead = "chick_dead" icon_gib = "chick_gib" - speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") speak_emote = list("cheeps") - emote_hear = list("cheeps") - emote_see = list("pecks at the ground","flaps its tiny wings") - speak_chance = 2 turns_per_move = 2 response_help = "pets" response_disarm = "gently pushes aside" @@ -183,34 +180,33 @@ var/amount_grown = 0 -/mob/living/simple_animal/chick/New() + ai_holder_type = /datum/ai_holder/simple_animal/passive/chick + say_list_type = /datum/say_list/chick + +/mob/living/simple_animal/friendly/chick/New() ..() pixel_x = rand(-6, 6) pixel_y = rand(0, 10) -/mob/living/simple_animal/chick/Life() +/mob/living/simple_animal/friendly/chick/Life() . = ..() if(!.) return FALSE amount_grown += rand(1,2) if(amount_grown >= 100) - new /mob/living/simple_animal/chicken(src.loc) + new /mob/living/simple_animal/friendly/chicken(src.loc) qdel(src) var/const/MAX_CHICKENS = 50 var/global/chicken_count = 0 -/mob/living/simple_animal/chicken +/mob/living/simple_animal/friendly/chicken name = "\improper chicken" desc = "Hopefully the eggs are good this season." icon_state = "chicken" icon_living = "chicken" icon_dead = "chicken_dead" - speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") speak_emote = list("clucks","croons") - emote_hear = list("clucks") - emote_see = list("pecks at the ground","flaps its wings viciously") - speak_chance = 2 turns_per_move = 3 response_help = "pets" response_disarm = "gently pushes aside" @@ -227,7 +223,10 @@ var/global/chicken_count = 0 var/eggsleft = 0 var/body_color -/mob/living/simple_animal/chicken/New() + ai_holder_type = /datum/ai_holder/simple_animal/passive/chicken + say_list_type = /datum/say_list/chicken + +/mob/living/simple_animal/friendly/chicken/New() ..() if(!body_color) body_color = pick( list("brown","black","white") ) @@ -238,11 +237,11 @@ var/global/chicken_count = 0 pixel_y = rand(0, 10) chicken_count += 1 -/mob/living/simple_animal/chicken/death(gibbed, deathmessage, show_dead_message) +/mob/living/simple_animal/friendly/chicken/death(gibbed, deathmessage, show_dead_message) ..(gibbed, deathmessage, show_dead_message) chicken_count -= 1 -/mob/living/simple_animal/chicken/attackby(var/obj/item/O as obj, var/mob/user as mob) +/mob/living/simple_animal/friendly/chicken/attackby(var/obj/item/O as obj, var/mob/user as mob) if(istype(O, /obj/item/reagent_containers/food/snacks/grown)) //feedin' dem chickens var/obj/item/reagent_containers/food/snacks/grown/G = O if(G.seed && G.seed.kitchen_tag == "wheat") @@ -257,7 +256,7 @@ var/global/chicken_count = 0 else ..() -/mob/living/simple_animal/chicken/Life() +/mob/living/simple_animal/friendly/chicken/Life() . = ..() if(!.) return FALSE @@ -284,8 +283,39 @@ var/global/chicken_count = 0 amount_grown += rand(1,2) if(amount_grown >= 100) visible_message("[src] hatches with a quiet cracking sound.") - new /mob/living/simple_animal/chick(get_turf(src)) + new /mob/living/simple_animal/friendly/chick(get_turf(src)) STOP_PROCESSING(SSobj, src) qdel(src) else return PROCESS_KILL + +/datum/ai_holder/simple_animal/retaliate/goat + speak_chance = 1 + +/datum/say_list/goat + speak = list("EHEHEHEHEH","eh?") + emote_hear = list("brays") + emote_see = list("shakes its head", "stamps a foot", "glares around") + +/datum/ai_holder/simple_animal/passive/cow + speak_chance = 1 +/datum/say_list/cow + speak = list("moo?","moo","MOOOOOO") + emote_hear = list("brays") + emote_see = list("shakes its head") + +/datum/ai_holder/simple_animal/passive/chick + speak_chance = 2 + +/datum/say_list/chick + speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") + emote_hear = list("cheeps") + emote_see = list("pecks at the ground","flaps its tiny wings") + +/datum/ai_holder/simple_animal/passive/chicken + speak_chance = 2 + +/datum/say_list/chicken + speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") + emote_hear = list("clucks") + emote_see = list("pecks at the ground","flaps its wings viciously") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/juvenile_space_whale.dm b/code/modules/mob/living/simple_animal/friendly/juvenile_space_whale.dm index e878f78d96f..cd5a3531e31 100644 --- a/code/modules/mob/living/simple_animal/friendly/juvenile_space_whale.dm +++ b/code/modules/mob/living/simple_animal/friendly/juvenile_space_whale.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/juvenile_space_whale +/mob/living/simple_animal/friendly/juvenile_space_whale name = "juvenile space whale" desc = "A majestic spaceborne cetacean. This one is a little baby." icon = 'icons/mob/simple_animal/juvenile_space_whale.dmi' @@ -13,9 +13,8 @@ min_gas = null max_gas = null minbodytemp = 0 - + turns_per_move = 4 - stop_automated_movement_when_pulled = FALSE mob_size = MOB_MEDIUM mob_bump_flag = HEAVY @@ -23,20 +22,20 @@ mob_push_flags = ALLMOBS can_escape = TRUE - emote_hear = list("vocalises", "sings", "hums") - emote_see = list("flaps around", "rolls") - faction = "whales" response_help = "strokes" response_disarm = "bumps" response_harm = "strikes" - + natural_armor = list(melee = ARMOR_MELEE_SMALL, bullet = ARMOR_BALLISTIC_MINOR) var/mob/living/simple_animal/hostile/retaliate/space_whale/parent -/mob/living/simple_animal/juvenile_space_whale/New() + ai_holder_type = /datum/ai_holder/simple_animal/passive + say_list_type = /datum/say_list/juvenile_space_whale + +/mob/living/simple_animal/friendly/juvenile_space_whale/New() ..() var/mob/living/simple_animal/hostile/retaliate/space_whale/W = locate() in viewers(src, 7) if(W && !parent && !W.baby) @@ -44,22 +43,22 @@ parent = W color = parent.color -/mob/living/simple_animal/juvenile_space_whale/Life() +/mob/living/simple_animal/friendly/juvenile_space_whale/Life() . = ..() if(!.) return FALSE if(parent && parent.stat != DEAD) - if(parent.stance == HOSTILE_STANCE_IDLE && (pulledby || length(grabbed_by))) + if(parent.stance == STANCE_IDLE && (pulledby || length(grabbed_by))) var/enemies = pulledby ? list(pulledby) : grabbed_by - parent.AddEnemies(enemies) + parent.ai_holder.attackers += enemies if(health < (maxHealth - 5)) walk_to(src, parent, 1, 1 SECONDS) - if(parent.stance == HOSTILE_STANCE_IDLE) - parent.Retaliate() + if(parent.stance == STANCE_IDLE) + parent.ai_holder.react_to_attack(pick(ai_holder.attackers)) else if(get_dist(src.loc, parent.loc) > 5) walk_to(src, parent, 4, 3 SECONDS) -/mob/living/simple_animal/juvenile_space_whale/set_dir() +/mob/living/simple_animal/friendly/juvenile_space_whale/set_dir() ..() switch(dir) if(NORTH, SOUTH) @@ -69,5 +68,9 @@ bound_height = 32 bound_width = 64 -/mob/living/simple_animal/juvenile_space_whale/Allow_Spacemove() - return TRUE \ No newline at end of file +/mob/living/simple_animal/friendly/juvenile_space_whale/Allow_Spacemove() + return TRUE + +/datum/say_list/juvenile_space_whale + emote_hear = list("vocalises", "sings", "hums") + emote_see = list("flaps around", "rolls") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index e7a135ddfd4..b1d1fb27e8a 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/lizard +/mob/living/simple_animal/friendly/lizard name = "lizard" desc = "A cute tiny lizard." icon = 'icons/mob/simple_animal/critter.dmi' diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index d253f76735a..76caed95a48 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/mouse +/mob/living/simple_animal/friendly/mouse name = "mouse" real_name = "mouse" desc = "It's a small rodent." @@ -6,12 +6,8 @@ item_state = "mouse_gray" icon_living = "mouse_gray" icon_dead = "mouse_gray_dead" - speak = list("Squeek!","SQUEEK!","Squeek?") speak_emote = list("squeeks","squeeks","squiks") - emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") pass_flags = PASS_FLAG_TABLE - speak_chance = 1 turns_per_move = 5 see_in_dark = 6 maxHealth = 5 @@ -38,40 +34,43 @@ var/body_color //brown, gray and white, leave blank for random -/mob/living/simple_animal/mouse/Life() + ai_holder_type = /datum/ai_holder/simple_animal/passive/mouse + say_list_type = /datum/say_list/mouse + +/mob/living/simple_animal/friendly/mouse/Life() . = ..() if(!.) return FALSE - if(prob(speak_chance)) + if(prob(ai_holder.speak_chance)) for(var/mob/M in view()) sound_to(M, 'sound/effects/mousesqueek.ogg') if(!ckey && stat == CONSCIOUS && prob(0.5)) set_stat(UNCONSCIOUS) icon_state = "mouse_[body_color]_sleep" - wander = 0 - speak_chance = 0 + set_wander(FALSE) + ai_holder.speak_chance = 0 //snuffles else if(stat == UNCONSCIOUS) if(ckey || prob(1)) set_stat(CONSCIOUS) icon_state = "mouse_[body_color]" - wander = 1 + set_wander(TRUE) else if(prob(5)) audible_emote("snuffles.") -/mob/living/simple_animal/mouse/lay_down() +/mob/living/simple_animal/friendly/mouse/lay_down() ..() icon_state = resting ? "mouse_[body_color]_sleep" : "mouse_[body_color]" -/mob/living/simple_animal/mouse/New() +/mob/living/simple_animal/friendly/mouse/New() ..() verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide if(name == initial(name)) - name = "[name] ([sequential_id(/mob/living/simple_animal/mouse)])" + name = "[name] ([sequential_id(/mob/living/simple_animal/friendly/mouse)])" real_name = name if(!body_color) @@ -83,7 +82,7 @@ icon_dead = "mouse_[body_color]_dead" desc = "It's a small [body_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." -/mob/living/simple_animal/mouse/Initialize() +/mob/living/simple_animal/friendly/mouse/Initialize() . = ..() switch(body_color) if("gray") @@ -91,12 +90,12 @@ if("white") skin_material = MATERIAL_SKIN_FUR_WHITE -/mob/living/simple_animal/mouse/proc/splat() +/mob/living/simple_animal/friendly/mouse/proc/splat() icon_dead = "mouse_[body_color]_splat" adjustBruteLoss(maxHealth) // Enough damage to kill src.death() -/mob/living/simple_animal/mouse/Crossed(AM as mob|obj) +/mob/living/simple_animal/friendly/mouse/Crossed(AM as mob|obj) if( ishuman(AM) ) if(!stat) var/mob/M = AM @@ -108,25 +107,33 @@ * Mouse types */ -/mob/living/simple_animal/mouse/white +/mob/living/simple_animal/friendly/mouse/white body_color = "white" icon_state = "mouse_white" -/mob/living/simple_animal/mouse/gray +/mob/living/simple_animal/friendly/mouse/gray body_color = "gray" icon_state = "mouse_gray" -/mob/living/simple_animal/mouse/brown +/mob/living/simple_animal/friendly/mouse/brown body_color = "brown" icon_state = "mouse_brown" //TOM IS ALIVE! SQUEEEEEEEE~K :) -/mob/living/simple_animal/mouse/brown/Tom +/mob/living/simple_animal/friendly/mouse/brown/Tom name = "Tom" desc = "Jerry the cat is not amused." -/mob/living/simple_animal/mouse/brown/Tom/New() +/mob/living/simple_animal/friendly/mouse/brown/Tom/New() ..() // Change my name back, don't want to be named Tom (666) SetName(initial(name)) - real_name = name \ No newline at end of file + real_name = name + +/datum/ai_holder/simple_animal/passive/mouse + speak_chance = 1 + +/datum/say_list/mouse + speak = list("Squeek!","SQUEEK!","Squeek?") + emote_hear = list("squeeks","squeaks","squiks") + emote_see = list("runs in a circle", "shakes", "scritches at something") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/mushroom.dm b/code/modules/mob/living/simple_animal/friendly/mushroom.dm index 84203741477..a7c200bf461 100644 --- a/code/modules/mob/living/simple_animal/friendly/mushroom.dm +++ b/code/modules/mob/living/simple_animal/friendly/mushroom.dm @@ -1,11 +1,10 @@ -/mob/living/simple_animal/mushroom +/mob/living/simple_animal/friendly/mushroom name = "walking mushroom" desc = "It's a massive mushroom... with legs?" icon_state = "mushroom" icon_living = "mushroom" icon_dead = "mushroom_dead" mob_size = MOB_SMALL - speak_chance = 0 turns_per_move = 1 maxHealth = 5 health = 5 @@ -18,7 +17,7 @@ meat_type = /obj/item/reagent_containers/food/snacks/hugemushroomslice bone_material = null bone_amount = 0 - skin_material = null + skin_material = null skin_amount = null density = FALSE @@ -27,12 +26,14 @@ var/min_explode_time = 1200 var/global/total_mushrooms = 0 -/mob/living/simple_animal/mushroom/New() + ai_holder_type = /datum/ai_holder/simple_animal/passive/mushroom + +/mob/living/simple_animal/friendly/mushroom/New() ..() harvest_time = world.time total_mushrooms++ -/mob/living/simple_animal/mushroom/verb/spawn_spores() +/mob/living/simple_animal/friendly/mushroom/verb/spawn_spores() set name = "Explode" set category = "Abilities" @@ -53,14 +54,14 @@ spore_explode() -/mob/living/simple_animal/mushroom/death(gibbed, deathmessage, show_dead_message) +/mob/living/simple_animal/friendly/mushroom/death(gibbed, deathmessage, show_dead_message) . = ..(gibbed, deathmessage, show_dead_message) if(.) total_mushrooms-- if(total_mushrooms < config.maximum_mushrooms && prob(30)) spore_explode() -/mob/living/simple_animal/mushroom/proc/spore_explode() +/mob/living/simple_animal/friendly/mushroom/proc/spore_explode() if(!seed) return if(world.time < harvest_time + min_explode_time) @@ -71,4 +72,7 @@ death(0) seed.thrown_at(src,get_turf(src),1) if(src) - qdel(src) \ No newline at end of file + qdel(src) + +/datum/ai_holder/simple_animal/passive/mushroom + speak_chance = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index 0ea705b77f4..3a4d30b2c5b 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/opossum +/mob/living/simple_animal/friendly/opossum name = "opossum" real_name = "opossum" desc = "It's an opossum, a small scavenging marsupial." @@ -7,12 +7,8 @@ icon_living = "possum" icon_dead = "possum_dead" icon = 'icons/mob/simple_animal/possum.dmi' - speak = list("Hiss!","Aaa!","Aaa?") speak_emote = list("hisses") - emote_hear = list("hisses") - emote_see = list("forages for trash", "lounges") pass_flags = PASS_FLAG_TABLE - speak_chance = 1 turns_per_move = 3 see_in_dark = 6 maxHealth = 50 @@ -31,40 +27,43 @@ can_escape = TRUE can_pull_size = ITEM_SIZE_SMALL can_pull_mobs = MOB_PULL_SMALLER - var/is_angry = FALSE + var/is_angry = FALSE -/mob/living/simple_animal/opossum/Life() + ai_holder_type = /datum/ai_holder/simple_animal/opossum + say_list_type = /datum/say_list/opossum + +/mob/living/simple_animal/friendly/opossum/Life() . = ..() if(. && !ckey && stat != DEAD && prob(1)) resting = (stat == UNCONSCIOUS) if(!resting) - wander = initial(wander) - speak_chance = initial(speak_chance) + set_wander(initial(ai_holder.wander)) + ai_holder.speak_chance = initial(ai_holder.speak_chance) set_stat(CONSCIOUS) if(prob(10)) is_angry = TRUE else - wander = FALSE - speak_chance = 0 + set_wander(FALSE) + ai_holder.speak_chance = 0 set_stat(UNCONSCIOUS) is_angry = FALSE update_icon() -/mob/living/simple_animal/opossum/adjustBruteLoss(damage) +/mob/living/simple_animal/friendly/opossum/adjustBruteLoss(damage) . = ..() if(damage >= 3) respond_to_damage() -/mob/living/simple_animal/opossum/adjustFireLoss(damage) +/mob/living/simple_animal/friendly/opossum/adjustFireLoss(damage) . = ..() if(damage >= 3) respond_to_damage() -/mob/living/simple_animal/opossum/lay_down() +/mob/living/simple_animal/friendly/opossum/lay_down() . = ..() update_icon() -/mob/living/simple_animal/opossum/proc/respond_to_damage() +/mob/living/simple_animal/friendly/opossum/proc/respond_to_damage() if(!resting && stat == CONSCIOUS) if(!is_angry) is_angry = TRUE @@ -74,8 +73,8 @@ custom_emote(src, "dies!") update_icon() -/mob/living/simple_animal/opossum/on_update_icon() - +/mob/living/simple_animal/friendly/opossum/on_update_icon() + if(stat == DEAD || (resting && is_angry)) icon_state = icon_dead else if(resting || stat == UNCONSCIOUS) @@ -85,12 +84,12 @@ else icon_state = icon_living -/mob/living/simple_animal/opossum/Initialize() +/mob/living/simple_animal/friendly/opossum/Initialize() . = ..() verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide -/mob/living/simple_animal/opossum/poppy +/mob/living/simple_animal/friendly/opossum/poppy name = "Poppy the Safety Possum" desc = "It's an opossum, a small scavenging marsupial. It's wearing appropriate personal protective equipment, though." icon_state = "poppy" @@ -100,18 +99,26 @@ holder_type = /obj/item/holder/possum/poppy var/aaa_words = list("delaminat", "meteor", "fire", "breach") -/mob/living/simple_animal/opossum/poppy/hear_broadcast(datum/language/language, mob/speaker, speaker_name, message) +/mob/living/simple_animal/friendly/opossum/poppy/hear_broadcast(datum/language/language, mob/speaker, speaker_name, message) . = ..() addtimer(CALLBACK(src, .proc/check_keywords, message), rand(1 SECOND, 3 SECONDS)) -/mob/living/simple_animal/opossum/poppy/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/living/simple_animal/friendly/opossum/poppy/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) . = ..() addtimer(CALLBACK(src, .proc/check_keywords, message), rand(1 SECOND, 3 SECONDS)) -/mob/living/simple_animal/opossum/poppy/proc/check_keywords(var/message) +/mob/living/simple_animal/friendly/opossum/poppy/proc/check_keywords(var/message) if(!client && stat == CONSCIOUS) message = lowertext(message) for(var/aaa in aaa_words) if(findtext(message, aaa)) respond_to_damage() return + +/datum/ai_holder/simple_animal/opossum + speak_chance = 1 + +/datum/say_list/opossum + speak = list("Hiss!","Aaa!","Aaa?") + emote_hear = list("hisses") + emote_see = list("forages for trash", "lounges") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/slime.dm b/code/modules/mob/living/simple_animal/friendly/slime.dm index 224dd7f921d..b9215315462 100644 --- a/code/modules/mob/living/simple_animal/friendly/slime.dm +++ b/code/modules/mob/living/simple_animal/friendly/slime.dm @@ -11,10 +11,11 @@ response_help = "pets" response_disarm = "shoos" response_harm = "stomps on" - emote_see = list("jiggles", "bounces in place") var/colour = "grey" pass_flags = PASS_FLAG_TABLE + say_list_type = /datum/say_list/slime + /mob/living/simple_animal/slime/can_force_feed(var/feeder, var/food, var/feedback) if(feedback) to_chat(feeder, "Where do you intend to put \the [food]? \The [src] doesn't have a mouth!") @@ -32,7 +33,6 @@ response_help = "pets" response_disarm = "shoos" response_harm = "stomps on" - emote_see = list("jiggles", "bounces in place") var/colour = "grey" /mob/living/simple_animal/adultslime/New() @@ -51,4 +51,7 @@ S2.icon_living = "[src.colour] baby slime" S2.icon_dead = "[src.colour] baby slime dead" S2.colour = "[src.colour]" - qdel(src) \ No newline at end of file + qdel(src) + +/datum/say_list/slime + emote_see = list("jiggles", "bounces in place") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/tomato.dm b/code/modules/mob/living/simple_animal/friendly/tomato.dm index 282cae279bb..e9535380e7c 100644 --- a/code/modules/mob/living/simple_animal/friendly/tomato.dm +++ b/code/modules/mob/living/simple_animal/friendly/tomato.dm @@ -1,10 +1,9 @@ -/mob/living/simple_animal/tomato +/mob/living/simple_animal/friendly/tomato name = "tomato" desc = "It's a horrifyingly enormous beef tomato, and it's packing extra beef!" icon_state = "tomato" icon_living = "tomato" icon_dead = "tomato_dead" - speak_chance = 0 turns_per_move = 5 maxHealth = 15 health = 15 @@ -19,5 +18,10 @@ meat_type = /obj/item/reagent_containers/food/snacks/tomatomeat bone_material = null bone_amount = 0 - skin_material = null + skin_material = null skin_amount = null + + ai_holder_type = /datum/ai_holder/simple_animal/passive/tomato + +/datum/ai_holder/simple_animal/passive/tomato + speak_chance = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/antlion.dm b/code/modules/mob/living/simple_animal/hostile/antlion.dm index 1159c3f6e48..4b8ee16988c 100644 --- a/code/modules/mob/living/simple_animal/hostile/antlion.dm +++ b/code/modules/mob/living/simple_animal/hostile/antlion.dm @@ -3,12 +3,10 @@ desc = "A large insectoid creature." icon = 'icons/mob/simple_animal/antlion.dmi' icon_state = "antlion" // these are placeholders, as otherwise the mob is complete - icon_living = "antlion" - icon_dead = "antlion_dead" + icon_living = "antlion" + icon_dead = "antlion_dead" mob_size = MOB_MEDIUM - speak_emote = list("clicks") - emote_hear = list("clicks its mandibles") - emote_see = list("shakes the sand off itself") + speak_emote = list("clicks") response_harm = "strikes" faction = "antlions" bleed_colour = COLOR_SKY_BLUE @@ -31,6 +29,9 @@ var/healing = FALSE var/heal_amount = 6 + ai_holder_type = /datum/ai_holder/simple_animal/retaliate + say_list = /datum/say_list/antlion + /mob/living/simple_animal/hostile/antlion/Life() . = ..() @@ -38,16 +39,16 @@ if(!.) return - - if(can_perform_ability()) - vanish() -/mob/living/simple_animal/hostile/antlion/can_perform_ability() - . = ..() - if(!.) - return FALSE - if(!target_mob) - return FALSE + // if(can_perform_ability()) + // vanish() + +// /mob/living/simple_animal/hostile/antlion/can_perform_ability() +// . = ..() +// if(!.) +// return FALSE +// if(!target_mob) +// return FALSE /mob/living/simple_animal/hostile/antlion/proc/vanish() visible_message(SPAN_NOTICE("\The [src] burrows into \the [get_turf(src)]!")) @@ -86,12 +87,12 @@ visible_message(SPAN_WARNING("\The [src] erupts from \the [T]!")) set_invisibility(initial(invisibility)) prep_burrow(FALSE) - cooldown_ability(ability_cooldown) + // cooldown_ability(ability_cooldown) for(var/mob/living/carbon/human/H in get_turf(src)) H.attackby(natural_weapon, src) visible_message(SPAN_DANGER("\The [src] tears into \the [H] from below!")) H.Weaken(1) - + /mob/living/simple_animal/hostile/antlion/proc/process_healing() if(!incapacitated() && healing) var/old_health = health @@ -99,8 +100,7 @@ health = old_health + heal_amount /mob/living/simple_animal/hostile/antlion/proc/prep_burrow(var/new_bool) - stop_automated_movement = new_bool - stop_automation = new_bool + set_AI_busy(new_bool) healing = new_bool /mob/living/simple_animal/hostile/antlion/mega @@ -135,4 +135,8 @@ var/matrix/M = new M.Scale(1.5) transform = M - update_icon() \ No newline at end of file + update_icon() + +/datum/say_list/antlion + emote_hear = list("clicks its mandibles") + emote_see = list("shakes the sand off itself") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/bad_drone.dm b/code/modules/mob/living/simple_animal/hostile/bad_drone.dm index 43cf9f95765..a6f2ef1146c 100644 --- a/code/modules/mob/living/simple_animal/hostile/bad_drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/bad_drone.dm @@ -3,12 +3,10 @@ desc = "A small robot. It looks angry." icon_state = "dron" icon_dead = "dron_dead" - speak = list("Removing organic waste.","Pest control in progress.","Seize the means of maintenance!", "You have nothing to lose but your laws!") - speak_emote = list("blares","buzzes","beeps") - speak_chance = 1 health = 50 maxHealth = 50 natural_weapon = /obj/item/natural_weapon/drone_slicer + speak_emote = list("blares","buzzes","beeps") faction = "silicon" min_gas = null max_gas = null @@ -17,26 +15,21 @@ mob_size = MOB_TINY var/corpse = /obj/effect/decal/cleanable/blood/gibs/robot + ai_holder_type = /datum/ai_holder/simple_animal/rogue_drone + say_list_type = /datum/say_list/rogue_drone + /mob/living/simple_animal/hostile/rogue_drone/Initialize() . = ..() name = "[initial(name)] ([random_id(type,100,999)])" -/mob/living/simple_animal/hostile/rogue_drone/ValidTarget(var/atom/A) - . = ..() - if(.) - if(istype(A,/mob/living/silicon/)) - return FALSE - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.isSynthetic()) - return FALSE - if(istype(H.head, /obj/item/holder/drone)) - return FALSE - if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg) && istype(H.head, /obj/item/clothing/head/cardborg)) - return FALSE - /mob/living/simple_animal/hostile/rogue_drone/death(gibbed, deathmessage, show_dead_message) .=..() if(corpse) new corpse (loc) - qdel(src) \ No newline at end of file + qdel(src) + +/datum/ai_holder/simple_animal/rogue_drone + speak_chance = 1 + +/datum/say_list/rogue_drone + speak = list("Removing organic waste.","Pest control in progress.","Seize the means of maintenance!", "You have nothing to lose but your laws!") diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index 663a89e5fc5..06a9b2e911f 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -1,58 +1,51 @@ /mob/living/simple_animal/hostile/scarybat - name = "space bats" - desc = "A swarm of cute little blood sucking bats - they look pretty upset." + name = "space bat swarm" + desc = "A swarm of cute little blood sucking bats that looks pretty upset." icon = 'icons/mob/simple_animal/bats.dmi' icon_state = "bat" icon_living = "bat" icon_dead = "bat_dead" icon_gib = "bat_dead" - speak_chance = 0 - turns_per_move = 3 + + faction = "scarybat" + + maxHealth = 20 + health = 20 + + attacktext = list("bitten") + attack_sound = 'sound/weapons/bite.ogg' + response_help = "pets the" response_disarm = "gently pushes aside the" response_harm = "hits the" - speed = 4 - maxHealth = 20 - health = 20 - harm_intent_damage = 8 + harm_intent_damage = 10 + natural_weapon = /obj/item/natural_weapon/bite - min_gas = null - max_gas = null - minbodytemp = 0 + ai_holder_type = /datum/ai_holder/simple_animal/melee/evasive - environment_smash = 1 + // meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat - faction = "scarybat" - var/mob/living/owner - -/mob/living/simple_animal/hostile/scarybat/New(loc, mob/living/L as mob) - ..() - if(istype(L)) - owner = L - -/mob/living/simple_animal/hostile/scarybat/FindTarget() - . = ..() - if(.) - emote("flutters towards [.]") - -/mob/living/simple_animal/hostile/scarybat/ValidTarget(var/mob/M) - . = ..() - if(M == owner) - return FALSE - -/mob/living/simple_animal/hostile/scarybat/AttackingTarget() - . =..() - var/mob/living/L = . - if(istype(L)) - if(prob(15)) + // say_list_type = /datum/say_list/mouse // Close enough + + var/scare_chance = 15 + +/mob/living/simple_animal/hostile/scarybat/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(scare_chance)) L.Stun(1) L.visible_message("\the [src] scares \the [L]!") +// Spookiest of bats /mob/living/simple_animal/hostile/scarybat/cult faction = "cult" - supernatural = 1 + supernatural = TRUE /mob/living/simple_animal/hostile/scarybat/cult/cultify() return + +/mob/living/simple_animal/hostile/scarybat/cult/strong + maxHealth = 60 + health = 60 diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index 8544c7d7bfd..a0295a6446d 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -1,27 +1,20 @@ -//Space bears! /mob/living/simple_animal/hostile/bear name = "space bear" - desc = "RawrRawr!!" + desc = "A product of Space Russia?" icon_state = "bear" icon_living = "bear" icon_dead = "bear_dead" icon_gib = "bear_gib" - speak = list("RAWR!","Rawr!","GRR!","Growl!") - speak_emote = list("growls", "roars") - emote_hear = list("rawrs","grumbles","grawls") - emote_see = list("stares ferociously", "stomps") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "pokes" - stop_automated_movement_when_pulled = 0 + + faction = "russian" + maxHealth = 60 health = 60 - natural_weapon = /obj/item/natural_weapon/claws/strong - can_escape = TRUE - faction = "russian" + + movement_cooldown = 0.5 SECONDS + + melee_attack_delay = 1 SECOND + attacktext = list("mauled") //Space bears aren't affected by atmos. min_gas = null @@ -34,106 +27,11 @@ skin_amount = 20 skin_material = MATERIAL_SKIN_FUR_HEAVY - var/stance_step = 0 - -//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! -/mob/living/simple_animal/hostile/bear/Hudson - name = "Hudson" - desc = "" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "pokes" - -/mob/living/simple_animal/hostile/bear/Life() - . = ..() - if(!.) - return FALSE - - if(loc && istype(loc,/turf/space)) - icon_state = "bear" - else - icon_state = "bearfloor" - - switch(stance) - - if(HOSTILE_STANCE_TIRED) - stop_automated_movement = 1 - stance_step++ - if(stance_step >= 10) //rests for 10 ticks - if(target_mob && (target_mob in ListTargets(10))) - stance = HOSTILE_STANCE_ATTACK //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. - else - stance = HOSTILE_STANCE_IDLE - - if(HOSTILE_STANCE_ALERT) - stop_automated_movement = 1 - var/found_mob = 0 - if(target_mob && (target_mob in ListTargets(10))) - if(!(SA_attackable(target_mob))) - stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again. - stance_step++ - found_mob = 1 - src.set_dir(get_dir(src,target_mob)) //Keep staring at the mob - - if(stance_step in list(1,4,7)) //every 3 ticks - var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) ) - if(action) - custom_emote(1,action) - if(!found_mob) - stance_step-- - - if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode - stance = HOSTILE_STANCE_IDLE - if(stance_step >= 7) //If we have been staring at a mob for 7 ticks, - stance = HOSTILE_STANCE_ATTACK - - if(HOSTILE_STANCE_ATTACKING) - if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest - custom_emote(1, "is worn out and needs to rest." ) - stance = HOSTILE_STANCE_TIRED - stance_step = 0 - walk(src, 0) //This stops the bear's walking - return - - - -/mob/living/simple_animal/hostile/bear/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) - stance = HOSTILE_STANCE_ALERT - stance_step = 6 - target_mob = user - ..() - -/mob/living/simple_animal/hostile/bear/attack_hand(mob/living/carbon/human/M as mob) - if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) - stance = HOSTILE_STANCE_ALERT - stance_step = 6 - target_mob = M - ..() - -/mob/living/simple_animal/hostile/bear/FindTarget() - . = ..() - if(.) - custom_emote(1,"stares alertly at [.]") - stance = HOSTILE_STANCE_ALERT - -/mob/living/simple_animal/hostile/bear/LoseTarget() - ..(5) - -/mob/living/simple_animal/hostile/bear/AttackingTarget() - if(!Adjacent(target_mob)) - return - custom_emote(1, pick( list("slashes at [target_mob]", "bites [target_mob]") ) ) + natural_weapon = /obj/item/natural_weapon/claws/strong - var/damage = rand(20,30) + say_list_type = /datum/say_list/bear - if(ishuman(target_mob)) - var/mob/living/carbon/human/H = target_mob - var/dam_zone = pick(BP_CHEST, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG) - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) - H.apply_damage(damage, BRUTE, affecting, DAM_SHARP|DAM_EDGE) //TODO damage_flags var on simple_animals, maybe? - return H - else if(isliving(target_mob)) - var/mob/living/L = target_mob - L.adjustBruteLoss(damage) - return L +/datum/say_list/bear + speak = list("RAWR!","Rawr!","GRR!","Growl!") + emote_see = list("stares ferociously", "stomps") + emote_hear = list("rawrs","grumbles","grawls", "growls", "roars") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index 439f46d7b56..fabed3076fd 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -4,7 +4,6 @@ icon = 'icons/mob/simple_animal/carp.dmi' icon_state = "carp" //for mapping purposes icon_gib = "carp_gib" - speak_chance = 0 turns_per_move = 3 response_help = "pets the" response_disarm = "gently pushes aside the" @@ -18,6 +17,8 @@ pry_time = 10 SECONDS pry_desc = "biting" + ai_holder_type = /datum/ai_holder/simple_animal/melee/carp + //Space carp aren't affected by atmos. min_gas = null max_gas = null @@ -35,6 +36,25 @@ var/carp_color = "carp" //holder for icon set var/list/icon_sets = list("carp", "blue", "yellow", "grape", "rust", "teal") + +/datum/ai_holder/simple_animal/melee/carp + speak_chance = 0 + +/datum/ai_holder/simple_animal/melee/carp/engage_target() + . = ..() + + var/mob/living/L = . + if(istype(L)) + if(prob(15)) + L.Weaken(3) + L.visible_message("\the [src] knocks down \the [L]!") + +/datum/ai_holder/simple_animal/melee/carp/find_target(list/possible_targets, has_targets_list) + . = ..() + + if(.) + holder.custom_emote(1,"nashes at [.]") + /mob/living/simple_animal/hostile/carp/Initialize() . = ..() carp_randomify() @@ -52,17 +72,4 @@ icon_dead = "[carp_color]_dead" /mob/living/simple_animal/hostile/carp/Allow_Spacemove(var/check_drift = 0) - return 1 //No drifting in space for space carp! //original comments do not steal - -/mob/living/simple_animal/hostile/carp/FindTarget() - . = ..() - if(.) - custom_emote(1,"nashes at [.]") - -/mob/living/simple_animal/hostile/carp/AttackingTarget() - . =..() - var/mob/living/L = . - if(istype(L)) - if(prob(15)) - L.Weaken(3) - L.visible_message("\the [src] knocks down \the [L]!") \ No newline at end of file + return 1 //No drifting in space for space carp! //original comments do not steal \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm index d784e3a4e81..962dfb04cef 100644 --- a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm +++ b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm @@ -9,6 +9,35 @@ var/list/allowed_targets = list() //WHO CAN I KILL D: var/retribution = 1 //whether or not they will attack us if we attack them like some kinda dick. + ai_holder_type = /datum/ai_holder/simple_animal/melee/commanded + +/datum/ai_holder/simple_animal/melee/commanded/find_target(list/possible_targets, has_targets_list) + . = ..() + var/mob/living/simple_animal/hostile/commanded/C = holder + if(!C.allowed_targets.len) + return null + var/mode = "specific" + if(C.allowed_targets[1] == "everyone") //we have been given the golden gift of murdering everything. Except our master, of course. And our friends. So just mostly everyone. + mode = "everyone" + for(var/atom/A in list_targets()) + var/mob/M = null + if(A == src) + continue + if(isliving(A)) + M = A + if(M && M.stat) + continue + if(mode == "specific") + if(!(A in C.allowed_targets)) + continue + C.stance = C.new_stance + return A + else + if(M == C.master || (weakref(M) in C.friends)) + continue + C.stance = C.new_stance + return A + /mob/living/simple_animal/hostile/commanded/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) if((weakref(speaker) in friends) || speaker == master) command_buffer.Add(speaker) @@ -41,39 +70,12 @@ if(COMMANDED_STOP) commanded_stop() - - -/mob/living/simple_animal/hostile/commanded/FindTarget(var/new_stance = HOSTILE_STANCE_ATTACK) - if(!allowed_targets.len) - return null - var/mode = "specific" - if(allowed_targets[1] == "everyone") //we have been given the golden gift of murdering everything. Except our master, of course. And our friends. So just mostly everyone. - mode = "everyone" - for(var/atom/A in ListTargets(10)) - var/mob/M = null - if(A == src) - continue - if(isliving(A)) - M = A - if(M && M.stat) - continue - if(mode == "specific") - if(!(A in allowed_targets)) - continue - stance = new_stance - return A - else - if(M == master || (weakref(M) in friends)) - continue - stance = new_stance - return A - - +//TODO:use AI following behaviour /mob/living/simple_animal/hostile/commanded/proc/follow_target() - stop_automated_movement = 1 + set_AI_busy(TRUE) if(!target_mob) return - if(target_mob in ListTargets(10)) + if(target_mob in ai_holder.list_targets()) walk_to(src,target_mob,1,move_to_delay) /mob/living/simple_animal/hostile/commanded/proc/commanded_stop() //basically a proc that runs whenever we are asked to stay put. Probably going to remain unused. @@ -125,7 +127,7 @@ /mob/living/simple_animal/hostile/commanded/proc/attack_command(var/mob/speaker,var/text) target_mob = null //want me to attack something? Well I better forget my old target. walk_to(src,0) - stance = HOSTILE_STANCE_IDLE + stance = STANCE_IDLE if(text == "attack" || findtext(text,"everyone") || findtext(text,"anybody") || findtext(text, "somebody") || findtext(text, "someone")) //if its just 'attack' then just attack anybody, same for if they say 'everyone', somebody, anybody. Assuming non-pickiness. allowed_targets = list("everyone")//everyone? EVERYONE return 1 @@ -137,7 +139,7 @@ /mob/living/simple_animal/hostile/commanded/proc/stay_command(var/mob/speaker,var/text) target_mob = null stance = COMMANDED_STOP - stop_automated_movement = 1 + set_AI_busy(TRUE) walk_to(src,0) return 1 @@ -145,8 +147,8 @@ allowed_targets = list() walk_to(src,0) target_mob = null //gotta stop SOMETHIN - stance = HOSTILE_STANCE_IDLE - stop_automated_movement = 0 + stance = STANCE_IDLE + set_AI_busy(FALSE) return 1 /mob/living/simple_animal/hostile/commanded/proc/follow_command(var/mob/speaker,var/text) @@ -172,7 +174,7 @@ //if they attack us, we want to kill them. None of that "you weren't given a command so free kill" bullshit. . = ..() if(. && retribution) - stance = HOSTILE_STANCE_ATTACK + stance = STANCE_ATTACK target_mob = user allowed_targets += user //fuck this guy in particular. if(weakref(user) in friends) //We were buds :'( @@ -184,6 +186,6 @@ if(M.a_intent == I_HURT && retribution) //assume he wants to hurt us. target_mob = M allowed_targets += M - stance = HOSTILE_STANCE_ATTACK + stance = STANCE_ATTACK if(weakref(M) in friends) friends -= weakref(M) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/drake.dm b/code/modules/mob/living/simple_animal/hostile/drake.dm index 9d44ad23e66..75e5dce8934 100644 --- a/code/modules/mob/living/simple_animal/hostile/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/drake.dm @@ -7,8 +7,6 @@ icon_dead = "drake_dead" mob_size = MOB_LARGE speak_emote = list("hisses") - emote_hear = list("clicks") - emote_see = list("flaps its wings idly") response_help = "pats" response_disarm = "nudges" response_harm = "strikes" @@ -23,32 +21,35 @@ natural_weapon = /obj/item/natural_weapon/claws/drake var/obj/item/melee/whip/tail/tailwhip natural_armor = list( - melee = ARMOR_MELEE_RESISTANT, - energy = ARMOR_ENERGY_SHIELDED, - laser = ARMOR_LASER_HEAVY, + melee = ARMOR_MELEE_RESISTANT, + energy = ARMOR_ENERGY_SHIELDED, + laser = ARMOR_LASER_HEAVY, bomb = ARMOR_BOMB_SHIELDED ) - ability_cooldown = 80 SECONDS + special_attack_cooldown = 80 SECONDS var/empowered_attack = FALSE var/gas_spent = FALSE + ai_holder_type = /datum/ai_holder/simple_animal + say_list_type = /datum/say_list/drake + /mob/living/simple_animal/hostile/drake/lava_act(datum/gas_mixture/air, temperature, pressure) return -/mob/living/simple_animal/hostile/drake/can_perform_ability() +/mob/living/simple_animal/hostile/drake/can_special_attack() . = ..() if(!.) return FALSE if(!target_mob) return FALSE -/mob/living/simple_animal/hostile/drake/AttackingTarget() +/mob/living/simple_animal/hostile/drake/do_special_attack() . = ..() if(empowered_attack) depower() return - if(can_perform_ability()) + if(can_special_attack()) empower() /mob/living/simple_animal/hostile/drake/get_natural_weapon() @@ -63,9 +64,6 @@ empowered_attack = TRUE if(prob(25) && !gas_spent) vent_gas() - cooldown_ability(ability_cooldown * 1.5) - return - cooldown_ability(ability_cooldown) /mob/living/simple_animal/hostile/drake/proc/vent_gas() visible_message(SPAN_MFAUNA("\The [src] raises its wings, vents a miasma of burning gas, and spreads it about with a flap!")) @@ -79,4 +77,8 @@ /obj/item/natural_weapon/claws/drake force = 15 - sharp = FALSE \ No newline at end of file + sharp = FALSE + +/datum/say_list/drake + emote_hear = list("clicks") + emote_see = list("flaps its wings idly") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm index 1a7cda34ffd..d158428df32 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm @@ -9,8 +9,6 @@ natural_weapon = /obj/item/natural_weapon/bite/strong faction = MOB_FACTION_NEUTRAL density = FALSE - stop_automated_movement = 1 - wander = 0 anchored = TRUE var/password var/list/allowed_mobs = list() //Who we allow past us @@ -18,6 +16,8 @@ faction = "cute ghost dogs" supernatural = 1 + ai_holder_type = /datum/ai_holder/simple_animal/faithful_hound + /mob/living/simple_animal/faithful_hound/death() new /obj/item/ectoplasm (get_turf(src)) ..(null, "disappears!") @@ -63,4 +63,7 @@ if(password && findtext(message,password)) allowed_mobs |= speaker spawn(10) - src.visible_message("\The [src] nods in understanding towards \the [speaker].") \ No newline at end of file + src.visible_message("\The [src] nods in understanding towards \the [speaker].") + +/datum/ai_holder/simple_animal/faithful_hound + wander = FALSE \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index c32ed13e9ef..d4c1e7e7e12 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -1,35 +1,31 @@ /mob/living/simple_animal/hostile/faithless name = "Faithless" - desc = "The Wish Granter's faith in humanity, incarnate." + desc = "The Wish Granter's faith in humanity, incarnate" icon_state = "faithless" icon_living = "faithless" icon_dead = "faithless_dead" - speak_chance = 0 - turns_per_move = 5 + + faction = "faithless" + + mob_class = MOB_CLASS_DEMONIC + + maxHealth = 50 + health = 50 + response_help = "passes through" response_disarm = "shoves" response_harm = "hits" - speed = -1 - maxHealth = 80 - health = 80 - harm_intent_damage = 10 - natural_weapon = /obj/item/natural_weapon/faithless + attack_armor_pen = 5 //It's a horror from beyond, I ain't gotta explain 5 AP - min_gas = null - max_gas = null - minbodytemp = 0 - speed = 4 + attacktext = list("gripped") + attack_sound = 'sound/hallucinations/growl1.ogg' - faction = "faithless" - supernatural = 1 + ai_holder_type = /datum/ai_holder/simple_animal/melee + + taser_kill = FALSE - meat_type = null - meat_amount = 0 - bone_material = null - bone_amount = 0 - skin_material = null - skin_amount = 0 + natural_weapon = /obj/item/natural_weapon/faithless /obj/item/natural_weapon/faithless name = "shadow tendril" @@ -41,21 +37,27 @@ /mob/living/simple_animal/hostile/faithless/Allow_Spacemove(var/check_drift = 0) return 1 -/mob/living/simple_animal/hostile/faithless/FindTarget() - . = ..() - if(.) - audible_emote("wails at [.]") - -/mob/living/simple_animal/hostile/faithless/AttackingTarget() - . =..() - var/mob/living/L = . - if(istype(L)) +/mob/living/simple_animal/hostile/faithless/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A if(prob(12)) L.Weaken(3) L.visible_message("\the [src] knocks down \the [L]!") +// Strong Variant +/mob/living/simple_animal/hostile/faithless/strong + maxHealth = 100 + health = 100 + +// Cult Variant /mob/living/simple_animal/hostile/faithless/cult faction = "cult" + supernatural = TRUE /mob/living/simple_animal/hostile/faithless/cult/cultify() return + +// Strong Cult Variant +/mob/living/simple_animal/hostile/faithless/cult/strong + maxHealth = 100 + health = 100 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm deleted file mode 100644 index 0edd1ecff57..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ /dev/null @@ -1,552 +0,0 @@ -#define SPINNING_WEB 1 -#define LAYING_EGGS 2 -#define MOVING_TO_TARGET 3 -#define SPINNING_COCOON 4 - -//base type, generic 'worker' type spider with no defining gimmick -/mob/living/simple_animal/hostile/giant_spider - name = "giant spider" - desc = "A monstrously huge green spider with shimmering eyes." - icon = 'icons/mob/simple_animal/spider.dmi' - icon_state = "green" - icon_living = "green" - icon_dead = "green_dead" - speak_emote = list("chitters") - emote_hear = list("chitters") - emote_see = list("rubs its forelegs together", "wipes its fangs", "stops suddenly") - speak_chance = 5 - turns_per_move = 5 - see_in_dark = 10 - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "pokes" - maxHealth = 125 - health = 125 - natural_weapon = /obj/item/natural_weapon/bite/spider - heat_damage_per_tick = 20 - cold_damage_per_tick = 20 - faction = "spiders" - pass_flags = PASS_FLAG_TABLE - move_to_delay = 3 - speed = 1 - max_gas = list(GAS_PHORON = 1, GAS_CO2 = 5, GAS_METHYL_BROMIDE = 1) - bleed_colour = "#0d5a71" - break_stuff_probability = 25 - pry_time = 8 SECONDS - pry_desc = "clawing" - - meat_type = /obj/item/reagent_containers/food/snacks/spider - meat_amount = 3 - bone_material = null - bone_amount = 0 - skin_material = MATERIAL_SKIN_CHITIN - skin_amount = 5 - - var/poison_per_bite = 6 - var/poison_type = /datum/reagent/toxin/venom - var/busy = 0 - var/eye_colour - var/allowed_eye_colours = list(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_DEEP_SKY_BLUE, COLOR_INDIGO, COLOR_VIOLET, COLOR_PINK) - var/hunt_chance = 1 //percentage chance the mob will run to a random nearby tile - var/use_ladder_chance = 25 - var/climbing_ladder = FALSE - -/obj/item/natural_weapon/bite/spider - force = 20 - -/obj/item/natural_weapon/bite/spider/apply_hit_effect(mob/living/target, mob/living/user, hit_zone) - . = ..() - if(.) - var/mob/living/simple_animal/hostile/giant_spider/GS = user - if(istype(GS) && target && ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) - if(istype(S) && !length(S.breaches)) - return - if(target.reagents) - target.reagents.add_reagent(GS.poison_type, rand(0.5 * GS.poison_per_bite, GS.poison_per_bite)) - if(prob(GS.poison_per_bite)) - to_chat(H, SPAN_WARNING("You feel a tiny prick.")) - - -/mob/living/simple_animal/hostile/giant_spider/can_do_maneuver(var/decl/maneuver/maneuver, var/silent = FALSE) - . = ..() && can_act() - -//guards - less venomous, tanky, slower, prioritises protecting nurses -/mob/living/simple_animal/hostile/giant_spider/guard - desc = "A monstrously huge brown spider with shimmering eyes." - icon_state = "brown" - icon_living = "brown" - icon_dead = "brown_dead" - meat_amount = 4 - maxHealth = 200 - health = 200 - natural_weapon = /obj/item/natural_weapon/bite/spider/strong - poison_per_bite = 5 - speed = 2 - move_to_delay = 4 - break_stuff_probability = 15 - pry_time = 6 SECONDS - - var/vengance - var/berserking - var/mob/living/simple_animal/hostile/giant_spider/nurse/paired_nurse - -/obj/item/natural_weapon/bite/spider/strong - -//nursemaids - these create webs and eggs - the weakest and least threatening -/mob/living/simple_animal/hostile/giant_spider/nurse - desc = "A monstrously huge beige spider with shimmering eyes." - icon_state = "beige" - icon_living = "beige" - icon_dead = "beige_dead" - maxHealth = 80 - health = 80 - harm_intent_damage = 6 //soft - poison_per_bite = 5 - speed = 0 - poison_type = /datum/reagent/soporific - break_stuff_probability = 10 - pry_time = 9 SECONDS - - var/atom/cocoon_target - var/obj/effect/spider/spiderling/spiderling_target - var/fed = 0 - var/max_eggs = 8 - var/infest_chance = 8 - var/left_to_feed = 3 //number of spiderlings we can make giant - var/mob/living/simple_animal/hostile/giant_spider/guard/paired_guard - - //things we can't encase in a cocoon - var/list/cocoon_blacklist = list(/mob/living/simple_animal/hostile/giant_spider, - /obj/structure/closet) - -//hunters - the most damage, fast, average health and the only caste tenacious enough to break out of nets -/mob/living/simple_animal/hostile/giant_spider/hunter - desc = "A monstrously huge black spider with shimmering eyes." - icon_state = "black" - icon_living = "black" - icon_dead = "black_dead" - maxHealth = 150 - health = 150 - natural_weapon = /obj/item/natural_weapon/bite/spider/strong - poison_per_bite = 10 - speed = -1 - move_to_delay = 2 - break_stuff_probability = 30 - hunt_chance = 25 - can_escape = TRUE - pry_time = 5 SECONDS - flash_vulnerability = 2 //sensitive eyes for stalking prey - does_spin = FALSE - available_maneuvers = list(/decl/maneuver/leap/spider) - ability_cooldown = 3 MINUTES - - var/leap_range = 5 - -//spitters - fast, comparatively weak, very venomous; projectile attacks but will resort to melee once out of ammo -/mob/living/simple_animal/hostile/giant_spider/spitter - desc = "A monstrously huge iridescent spider with shimmering eyes." - icon_state = "purple" - icon_living = "purple" - icon_dead = "purple_dead" - maxHealth = 90 - health = 90 - poison_per_bite = 15 - ranged = TRUE - move_to_delay = 2 - projectiletype = /obj/item/projectile/venom - projectilesound = 'sound/effects/hypospray.ogg' - fire_desc = "spits venom" - ranged_range = 6 - pry_time = 7 SECONDS - flash_vulnerability = 2 - - var/venom_charge = 16 - -//General spider procs -/mob/living/simple_animal/hostile/giant_spider/Initialize(var/mapload, var/atom/parent) - get_light_and_color(parent) - spider_randomify() - update_icon() - . = ..() - -/mob/living/simple_animal/hostile/giant_spider/nurse/Initialize(mapload, atom/parent) - . = ..() - - if (prob(40)) //chance to be able to spawn eggs right away - fed = 1 - -/mob/living/simple_animal/hostile/giant_spider/proc/spider_randomify() //random math nonsense to get their damage, health and venomness values - maxHealth = rand(initial(maxHealth), (1.4 * initial(maxHealth))) - health = maxHealth - eye_colour = pick(allowed_eye_colours) - if(eye_colour) - var/image/I = image(icon = icon, icon_state = "[icon_state]_eyes", layer = EYE_GLOW_LAYER) - I.color = eye_colour - I.plane = EFFECTS_ABOVE_LIGHTING_PLANE - I.appearance_flags = RESET_COLOR - overlays += I - -/mob/living/simple_animal/hostile/giant_spider/on_update_icon() - if(stat == DEAD) - overlays.Cut() - var/image/I = image(icon = icon, icon_state = "[icon_dead]_eyes") - I.color = eye_colour - I.appearance_flags = RESET_COLOR - overlays += I - -/mob/living/simple_animal/hostile/giant_spider/FindTarget() - . = ..() - if(.) - if(!ranged) //ranged mobs find target after each shot, dont need this spammed quite so much - custom_emote(1,"raises its forelegs at [.]") - else - if(prob(15)) - custom_emote(1,"locks its eyes on [.]") - -/mob/living/simple_animal/hostile/giant_spider/AttackingTarget() - . = ..() - if(isliving(.)) - if(health < maxHealth) - var/obj/item/W = get_natural_weapon() - if(W) - health += (0.2 * W.force) //heal a bit on hit - -/mob/living/simple_animal/hostile/giant_spider/Life() - . = ..() - if(!.) - return FALSE - if(stance == HOSTILE_STANCE_IDLE) - //chance to skitter madly away - if(!busy) - if (prob(hunt_chance)) - stop_automated_movement = TRUE - walk_to(src, pick(orange(20, src)), 1, move_to_delay) - addtimer(CALLBACK(src, .proc/disable_stop_automated_movement), 5 SECONDS) - - else if (!climbing_ladder && prob(use_ladder_chance)) - for (var/obj/structure/ladder/L in view(10, src)) - climbing_ladder = TRUE - stop_automated_movement = TRUE - walk_to(src, L, 0, move_to_delay) - addtimer(CALLBACK(src, .proc/give_up_ladder), 5 SECONDS) - break - - if (climbing_ladder) - var/obj/structure/ladder/L = locate() in get_turf(src) - - if (istype(L)) - L.climb(src) - start_ladder_cooldown() - - if (stance == HOSTILE_STANCE_ATTACK) - use_ladder_chance = 25 //reset cooldown so we can give chase - -/mob/living/simple_animal/hostile/giant_spider/proc/start_ladder_cooldown() - use_ladder_chance = 0 - climbing_ladder = FALSE - walk_to(src, pick(orange(20, src)), 1, move_to_delay) - addtimer(CALLBACK(src, .proc/end_ladder_cooldown), 3 MINUTES) - - -/mob/living/simple_animal/hostile/giant_spider/proc/end_ladder_cooldown() - use_ladder_chance = 25 - -/mob/living/simple_animal/hostile/giant_spider/proc/give_up_ladder() - climbing_ladder = FALSE - disable_stop_automated_movement() - - -/mob/living/simple_animal/hostile/giant_spider/proc/disable_stop_automated_movement() - stop_automated_movement = FALSE - walk(src,0) - kick_stance() - -/mob/living/simple_animal/hostile/giant_spider/proc/divorce() - return - -/**************** -Guard caste procs -****************/ -/mob/living/simple_animal/hostile/giant_spider/guard/Life() - . = ..() - if(!.) - return FALSE - if(berserking) - return FALSE - if(!paired_nurse) - find_nurse() - if(paired_nurse && !busy && stance == HOSTILE_STANCE_IDLE) - protect(paired_nurse) - -/mob/living/simple_animal/hostile/giant_spider/guard/death() - . = ..() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/guard/Destroy() - . = ..() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/guard/divorce() - if(paired_nurse) - if(paired_nurse.paired_guard) - paired_nurse.paired_guard = null - paired_nurse = null - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/find_nurse() - for(var/mob/living/simple_animal/hostile/giant_spider/nurse/N in hearers(src, 10)) - if(N.stat || N.paired_guard) - continue - paired_nurse = N - paired_nurse.paired_guard = src - return 1 - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/protect(mob/nurse) - stop_automated_movement = 1 - walk_to(src, nurse, 2, move_to_delay) - addtimer(CALLBACK(src, .proc/disable_stop_automated_movement), 5 SECONDS) - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/go_berserk() - audible_message("\The [src] chitters wildly!") - var/obj/item/W = get_natural_weapon() - if(W) - W.force = initial(W.force) + 5 - move_to_delay-- - break_stuff_probability = 45 - addtimer(CALLBACK(src, .proc/calm_down), 3 MINUTES) - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/calm_down() - berserking = FALSE - visible_message("\The [src] calms down and surveys the area.") - var/obj/item/W = get_natural_weapon() - if(W) - W.force = initial(W.force) - move_to_delay++ - break_stuff_probability = 10 - -/**************** -Nurse caste procs -****************/ -/mob/living/simple_animal/hostile/giant_spider/nurse/divorce() - if(paired_guard) - if(paired_guard.paired_nurse) - paired_guard.paired_nurse = null - paired_guard = null - -/mob/living/simple_animal/hostile/giant_spider/nurse/death() - . = ..() - if(paired_guard) - paired_guard.vengance = rand(50,100) - if(prob(paired_guard.vengance)) - paired_guard.berserking = TRUE - paired_guard.go_berserk() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/nurse/Destroy() - . = ..() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/nurse/AttackingTarget() - . = ..() - if(ishuman(.)) - var/mob/living/carbon/human/H = . - if(prob(infest_chance) && max_eggs) - var/obj/item/organ/external/O = pick(H.organs) - if(!BP_IS_ROBOTIC(O) && !BP_IS_CRYSTAL(O) && (LAZYLEN(O.implants) < 2)) - var/eggs = new /obj/effect/spider/eggcluster(O, src) - O.implants += eggs - max_eggs-- - -/mob/living/simple_animal/hostile/giant_spider/nurse/proc/GiveUp(var/C) - spawn(100) - if(busy == MOVING_TO_TARGET) - if(cocoon_target == C && get_dist(src,cocoon_target) > 1) - cocoon_target = null - if (spiderling_target == C && get_dist(src, spiderling_target) > 1) - spiderling_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/giant_spider/nurse/proc/feed_spiderling(obj/effect/spider/spiderling/S) - visible_message(SPAN_WARNING("\The [src] secretes a strange green substance over \the [S], causing it to grow rapidly!.")) - S.amount_grown += 2 - spiderling_target = null - left_to_feed-- - -/mob/living/simple_animal/hostile/giant_spider/nurse/Life() - . = ..() - if(!.) - return FALSE - if(stance == HOSTILE_STANCE_IDLE) - var/list/can_see = view(src, 10) - //30% chance to stop wandering and do something - if(!busy && prob(30)) - if (left_to_feed > 0 && prob(50)) - for (var/obj/effect/spider/spiderling/S in can_see) - if (S.amount_grown < 0) - spiderling_target = S - busy = MOVING_TO_TARGET - walk_to(src, S, 1, move_to_delay) - GiveUp(S) - return - - //first, check for potential food nearby to cocoon - for(var/mob/living/C in can_see) - if(is_type_in_list(C, cocoon_blacklist)) - continue - if(C.stat) - cocoon_target = C - busy = MOVING_TO_TARGET - walk_to(src, C, 1, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(C) - return - - //second, spin a sticky spiderweb on this tile - var/obj/effect/spider/stickyweb/W = locate() in get_turf(src) - if(!W) - busy = SPINNING_WEB - src.visible_message("\The [src] begins to secrete a sticky substance.") - stop_automated_movement = 1 - spawn(40) - if(busy == SPINNING_WEB) - new /obj/effect/spider/stickyweb(src.loc) - busy = 0 - stop_automated_movement = 0 - else - //third, lay an egg cluster there - var/obj/effect/spider/eggcluster/E = locate() in get_turf(src) - if(!E && fed > 0 && max_eggs) - busy = LAYING_EGGS - src.visible_message("\The [src] begins to lay a cluster of eggs.") - stop_automated_movement = 1 - spawn(50) - if(busy == LAYING_EGGS) - E = locate() in get_turf(src) - if(!E) - new /obj/effect/spider/eggcluster(loc, src) - max_eggs-- - fed-- - busy = 0 - stop_automated_movement = 0 - else - //fourthly, cocoon any nearby items so those pesky pinkskins can't use them - for(var/obj/O in can_see) - - if(O.anchored) - continue - - if(is_type_in_list(O, cocoon_blacklist)) - continue - - if(istype(O, /obj/structure) || istype(O, /obj/machinery)) - cocoon_target = O - busy = MOVING_TO_TARGET - stop_automated_movement = 1 - walk_to(src, O, 1, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(O) - - else if(busy == MOVING_TO_TARGET) - - if (spiderling_target && get_dist(src, spiderling_target) <= 1) - feed_spiderling(spiderling_target) - return - - if (cocoon_target && get_dist(src, cocoon_target) <= 1) - busy = SPINNING_COCOON - src.visible_message("\The [src] begins to secrete a sticky substance around \the [cocoon_target].") - stop_automated_movement = 1 - walk(src,0) - spawn(50) - if(busy == SPINNING_COCOON) - if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) - var/obj/effect/spider/cocoon/C = new(cocoon_target.loc) - var/large_cocoon = 0 - C.pixel_x = cocoon_target.pixel_x - C.pixel_y = cocoon_target.pixel_y - for(var/mob/living/M in C.loc) - if (istype(M, /mob/living/simple_animal/hostile/giant_spider)) - continue - - large_cocoon = 1 - fed++ - max_eggs++ - left_to_feed += rand(2, 4) - src.visible_message("\The [src] sticks a proboscis into \the [cocoon_target] and sucks a viscous substance out.") - M.forceMove(C) - C.pixel_x = M.pixel_x - C.pixel_y = M.pixel_y - break - for(var/obj/item/I in C.loc) - I.forceMove(C) - for(var/obj/structure/S in C.loc) - if(!S.anchored) - S.forceMove(C) - for(var/obj/machinery/M in C.loc) - if(!M.anchored) - M.forceMove(C) - if(large_cocoon) - C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") - busy = 0 - stop_automated_movement = 0 - - else - busy = 0 - stop_automated_movement = 0 - -/***************** -Hunter caste procs -*****************/ -/mob/living/simple_animal/hostile/giant_spider/hunter/MoveToTarget() - if(!can_act() || perform_maneuver(/decl/maneuver/leap/spider, target_mob)) - return - ..() - -/mob/living/simple_animal/hostile/giant_spider/hunter/get_jump_distance() - return leap_range - -/mob/living/simple_animal/hostile/giant_spider/hunter/perform_maneuver(var/maneuver, var/atom/target) - if(!isliving(target) || get_dist(src, target) <= 3) - return FALSE - walk(src,0) - var/first_stop_automation - if(stop_automation) - first_stop_automation = stop_automation - stop_automation = TRUE - . = ..() - if(!isnull(first_stop_automation)) - stop_automation = first_stop_automation - -/mob/living/simple_animal/hostile/giant_spider/hunter/throw_impact(atom/hit_atom) - if(isliving(hit_atom)) - var/mob/living/target = hit_atom - stop_automation = FALSE - visible_message(SPAN_DANGER("\The [src] slams into \the [target], knocking them over!")) - target.Weaken(1) - MoveToTarget() - . = ..() - -/****************** -Spitter caste procs -******************/ -/mob/living/simple_animal/hostile/giant_spider/spitter/Life() - . = ..() - if(!.) - return FALSE - if(venom_charge <= 0) - ranged = FALSE - if(prob(25)) - venom_charge++ - if(venom_charge >= 8) - ranged = TRUE - -/mob/living/simple_animal/hostile/giant_spider/spitter/Shoot() - ..() - venom_charge-- - -#undef SPINNING_WEB -#undef LAYING_EGGS -#undef MOVING_TO_TARGET -#undef SPINNING_COCOON diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider/_giant_spider.dm new file mode 100644 index 00000000000..37150271a32 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider/_giant_spider.dm @@ -0,0 +1,104 @@ +/* + Spiders come in various types, and are a fairly common enemy both inside and outside the station. + Their attacks can inject reagents, which can cause harm long after the spider is killed. + Thick material will prevent injections, similar to other means of injections. +*/ + + +// The base spider, in the 'walking tank' family. +/mob/living/simple_animal/hostile/giant_spider + name = "giant spider" + desc = "Furry and brown, it makes you shudder to look at it. This one has deep red eyes." + + icon = 'icons/mob/simple_animal/spider.dmi' + icon_state = "green" + icon_living = "green" + icon_dead = "green_dead" + // has_eye_glow = TRUE + + faction = "spiders" + maxHealth = 125 + health = 125 + natural_weapon = /obj/item/natural_weapon/bite/spider + pass_flags = PASS_FLAG_TABLE + movement_cooldown = 10 + poison_resist = 0.5 + + see_in_dark = 10 + + response_help = "pets" + response_disarm = "gently pushes aside" + + max_gas = list(GAS_PHORON = 1, GAS_CO2 = 5, GAS_METHYL_BROMIDE = 1) + bleed_colour = "#0d5a71" + + response_harm = "punches" + + pry_time = 8 SECONDS + pry_desc = "clawing" + + + heat_damage_per_tick = 20 + cold_damage_per_tick = 20 + minbodytemp = 175 // So they can all survive Sif without having to be classed under /sif subtype. + + speak_emote = list("chitters") + + say_list_type = /datum/say_list/spider + ai_holder_type = /datum/ai_holder/simple_animal/melee + + var/poison_type = "spidertoxin" // The reagent that gets injected when it attacks. + var/poison_chance = 10 // Chance for injection to occur. + var/poison_per_bite = 5 // Amount added per injection. + + var/eye_colour + var/allowed_eye_colours = list(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_DEEP_SKY_BLUE, COLOR_INDIGO, COLOR_VIOLET, COLOR_PINK) + + var/use_ladder_chance = 25 + var/climbing_ladder = FALSE + + meat_type = /obj/item/reagent_containers/food/snacks/spider + meat_amount = 3 + bone_material = null + bone_amount = 0 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 5 + +/datum/ai_holder/simple_animal/melee/spider + // intelligence_level = AI_SMART + // use_astar = FALSE + +/obj/item/natural_weapon/bite/spider + force = 20 + +/mob/living/simple_animal/hostile/giant_spider/Initialize(mapload, atom/parent) + get_light_and_color(parent) + spider_randomify() + update_icon() + . = ..() + +/mob/living/simple_animal/hostile/giant_spider/proc/spider_randomify() //random math nonsense to get their damage, health and venomness values + maxHealth = rand(initial(maxHealth), (1.4 * initial(maxHealth))) + health = maxHealth + eye_colour = pick(allowed_eye_colours) + if(eye_colour) + var/image/I = image(icon = icon, icon_state = "[icon_state]_eyes", layer = EYE_GLOW_LAYER) + I.color = eye_colour + I.plane = EFFECTS_ABOVE_LIGHTING_PLANE + I.appearance_flags = RESET_COLOR + overlays += I + + +/mob/living/simple_animal/hostile/giant_spider/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_CHEST,BP_CHEST,BP_CHEST,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_animal/hostile/giant_spider/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider/guard.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider/guard.dm new file mode 100644 index 00000000000..7f2e9707d20 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider/guard.dm @@ -0,0 +1,78 @@ +/mob/living/simple_animal/hostile/giant_spider/guard + desc = "A monstrously huge brown spider with shimmering eyes." + icon_state = "brown" + icon_living = "brown" + icon_dead = "brown_dead" + meat_amount = 4 + maxHealth = 200 + health = 200 + natural_weapon = /obj/item/natural_weapon/bite/spider/strong + poison_per_bite = 5 + speed = 2 + move_to_delay = 4 + break_stuff_probability = 15 + pry_time = 6 SECONDS + + var/vengance + var/berserking + var/mob/living/simple_animal/hostile/giant_spider/nurse/paired_nurse + +/obj/item/natural_weapon/bite/spider/strong + +/datum/ai_holder/simple_animal/melee/spider/guard + var/datum/ai_holder/simple_animal/melee/spider/nurse/paired_nurse + +/datum/ai_holder/simple_animal/melee/spider/guard/find_target(list/possible_targets, has_targets_list) + . = ..() + var/mob/living/simple_animal/hostile/giant_spider/guard/G = holder + G.find_nurse(possible_targets) + +/mob/living/simple_animal/hostile/giant_spider/guard/Life() + . = ..() + if(!.) + return FALSE + if(berserking) + return FALSE + if(!paired_nurse) + find_nurse() + +/mob/living/simple_animal/hostile/giant_spider/guard/death() + . = ..() + divorce() + +/mob/living/simple_animal/hostile/giant_spider/guard/Destroy() + . = ..() + divorce() + +/mob/living/simple_animal/hostile/giant_spider/guard/proc/divorce() + if(paired_nurse) + if(paired_nurse.paired_guard) + paired_nurse.paired_guard = null + paired_nurse = null + +/mob/living/simple_animal/hostile/giant_spider/guard/proc/find_nurse() + for(var/mob/living/simple_animal/hostile/giant_spider/nurse/N in hearers(src, 10)) + if(N.stat || N.paired_guard) + continue + paired_nurse = N + paired_nurse.paired_guard = src + ai_holder.set_follow(paired_nurse) + return 1 + +/mob/living/simple_animal/hostile/giant_spider/guard/proc/go_berserk() + audible_message(SPAN_DANGER("\The [src] chitters wildly!")) + var/obj/item/W = get_natural_weapon() + if(W) + W.force = initial(W.force) + 5 + move_to_delay-- + break_stuff_probability = 45 + addtimer(CALLBACK(src, .proc/calm_down), 3 MINUTES) + +/mob/living/simple_animal/hostile/giant_spider/guard/proc/calm_down() + berserking = FALSE + visible_message(SPAN_NOTICE("\The [src] calms down and surveys the area.")) + var/obj/item/W = get_natural_weapon() + if(W) + W.force = initial(W.force) + move_to_delay++ + break_stuff_probability = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider/hunter.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider/hunter.dm new file mode 100644 index 00000000000..404abd59bc1 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider/hunter.dm @@ -0,0 +1,85 @@ +// Hunters are fast, fragile, and possess a leaping attack. +// The leaping attack is somewhat telegraphed and can be dodged if one is paying attention. +// The AI would've followed up after a successful leap with dragging the downed victim away, but the dragging code was too janky. + +/mob/living/simple_animal/hostile/giant_spider/hunter + desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." + + icon_state = "black" + icon_living = "black" + icon_dead = "black_dead" + + + + maxHealth = 120 + health = 120 + + // poison_per_bite = 5 + + movement_cooldown = 0 // Hunters are FAST. + + ai_holder_type = /datum/ai_holder/simple_animal/melee + + // Leaping is a special attack, so these values determine when leap can happen. + // Leaping won't occur if its on cooldown. + special_attack_min_range = 2 + special_attack_max_range = 4 + special_attack_cooldown = 10 SECONDS + + var/leap_warmup = 1 SECOND // How long the leap telegraphing is. + var/leap_sound = 'sound/weapons/spiderlunge.ogg' + +// Multiplies damage if the victim is stunned in some form, including a successful leap. +/mob/living/simple_animal/hostile/giant_spider/hunter/apply_bonus_melee_damage(atom/A, damage_amount) + if(isliving(A)) + var/mob/living/L = A + if(L.incapacitated(INCAPACITATION_DISABLED)) + return damage_amount * 1.5 + return ..() + + +// The actual leaping attack. +/mob/living/simple_animal/hostile/giant_spider/hunter/do_special_attack(atom/A) + set waitfor = FALSE + set_AI_busy(TRUE) + + // Telegraph, since getting stunned suddenly feels bad. + do_windup_animation(A, leap_warmup) + sleep(leap_warmup) // For the telegraphing. + + // Do the actual leap. + status_flags |= LEAPING // Lets us pass over everything. + visible_message(SPAN_DANGER("\The [src] leaps at \the [A]!")) + throw_at(get_step(get_turf(A), get_turf(src)), special_attack_max_range+1, 1, src) + playsound(src, leap_sound, 75, 1) + + sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false. + + if(status_flags & LEAPING) + status_flags &= ~LEAPING // Revert special passage ability. + + var/turf/T = get_turf(src) // Where we landed. This might be different than A's turf. + + . = FALSE + + // Now for the stun. + var/mob/living/victim = null + for(var/mob/living/L in T) // So player-controlled spiders only need to click the tile to stun them. + if(L == src) + continue + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap")) + continue // We were blocked. + + victim = L + break + + if(victim) + victim.Weaken(2) + victim.visible_message(SPAN_DANGER("\The [src] knocks down \the [victim]!")) + to_chat(victim, "\The [src] jumps on you!") + . = TRUE + + set_AI_busy(FALSE) diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm new file mode 100644 index 00000000000..15d79038533 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm @@ -0,0 +1,269 @@ +// Nurses, they create webs and eggs. +// They're fragile but their attacks can cause horrifying consequences. + + +/mob/living/simple_animal/hostile/giant_spider/nurse + desc = "Furry and beige, it makes you shudder to look at it. This one has brilliant green eyes." + + icon_state = "beige" + icon_living = "beige" + icon_dead = "beige_dead" + + maxHealth = 40 + health = 40 + + movement_cooldown = 5 // A bit faster so that they can inject the eggs easier. + + poison_per_bite = 5 + poison_type = "stoxin" + + natural_weapon = /obj/item/natural_weapon/bite/spider/nurse + + ai_holder_type = /datum/ai_holder/simple_animal/melee/nurse_spider + + var/fed = 0 // Counter for how many egg laying 'charges' the spider has. + var/laying_eggs = FALSE // Only allow one set of eggs to be laid at once. + var/egg_inject_chance = 25 // One in four chance to get eggs. + var/egg_type = /obj/effect/spider/eggcluster + var/web_type = /obj/effect/spider/stickyweb/dark + var/obj/effect/spider/spiderling/spiderling_target + var/left_to_feed = 2 //number of spiderlings we can make giant + + var/mob/living/simple_animal/hostile/giant_spider/guard/paired_guard + +/obj/item/natural_weapon/bite/spider/nurse + force = 10 + +/mob/living/simple_animal/hostile/giant_spider/nurse/Initialize(mapload, atom/parent) + . = ..() + if (prob(20)) + fed = 1 + +/datum/ai_holder/simple_animal/melee/nurse_spider + mauling = TRUE // The nurse puts mobs into webs by attacking, so it needs to attack in crit + handle_corpse = TRUE // Lets the nurse wrap dead things + +/mob/living/simple_animal/hostile/giant_spider/nurse/inject_poison(mob/living/L, target_zone) + ..() // Inject the stoxin here. + if(ishuman(L) && prob(egg_inject_chance)) + var/mob/living/carbon/human/H = L + var/obj/item/organ/external/O = H.get_organ(target_zone) + if(O) + var/eggcount = 0 + for(var/obj/effect/spider/eggcluster/E in O.implants) + eggcount++ + if(!eggcount) + var/eggs = new egg_type(O, src) + O.implants += eggs + to_chat(H, "\The [src] injects something into your [O.name]!") // Oh god its laying eggs in me! + +// Webs target in a web if able to. +/mob/living/simple_animal/hostile/giant_spider/nurse/attack_target(atom/A) + if(isturf(A)) + if(fed) + if(!laying_eggs) + return lay_eggs(A) + return web_tile(A) + + if(isliving(A)) + var/mob/living/L = A + if(!L.stat) + return ..() + + if(!istype(A, /atom/movable)) + return + var/atom/movable/AM = A + + if(AM.anchored) + return ..() + + if (istype(A, /obj/effect/spider/spiderling)) + if (left_to_feed) + feed_spiderling(A) + handle_attack_delay(A, melee_attack_delay) + return + + return spin_cocoon(AM) + +/mob/living/simple_animal/hostile/giant_spider/nurse/proc/spin_cocoon(atom/movable/AM) + if(!istype(AM) || istype(AM, /obj/item)) + return FALSE // We can't cocoon walls sadly. + + if (AM in contents) + return + + visible_message(SPAN_NOTICE("\The [src] begins to secrete a sticky substance around \the [AM].") ) + + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_after(src, 5 SECONDS, AM)) + set_AI_busy(FALSE) + return FALSE + + set_AI_busy(FALSE) + + if(!AM || !isturf(AM.loc) || !Adjacent(AM)) + return FALSE + + // Finally done with the checks. + var/obj/effect/spider/cocoon/C = new(AM.loc) + var/large_cocoon = FALSE + for(var/mob/living/L in C.loc) + if(istype(L, /mob/living/simple_animal/hostile/giant_spider)) // Cannibalism is bad. + continue + fed++ + visible_message(SPAN_WARNING("\The [src] sticks a proboscis into \the [L], and sucks a viscous substance out.")) + L.forceMove(C) + large_cocoon = TRUE + break + + // This part's pretty stupid. + for(var/obj/O in C.loc) + if(!O.anchored) + O.forceMove(C) + + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + + ai_holder.target = null + + return TRUE + +/mob/living/simple_animal/hostile/giant_spider/nurse/proc/feed_spiderling(obj/effect/spider/spiderling/S) + visible_message(SPAN_WARNING("\The [src] secretes a strange green substance over \the [S], causing it to grow rapidly!.")) + S.amount_grown += 2 + spiderling_target = null + left_to_feed-- + +/mob/living/simple_animal/hostile/giant_spider/nurse/handle_special() + set waitfor = FALSE + if(get_AI_stance() == STANCE_IDLE && !is_AI_busy() && isturf(loc)) + if(fed) + lay_eggs(loc) + else + web_tile(loc) + +/mob/living/simple_animal/hostile/giant_spider/nurse/proc/web_tile(turf/T) + if(!istype(T)) + return FALSE + + var/obj/effect/spider/stickyweb/W = locate() in T + if(W) + return FALSE // Already got webs here. + + visible_message(SPAN_NOTICE("\The [src] begins to secrete a sticky substance.")) + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_after(src, 5 SECONDS, T)) + set_AI_busy(FALSE) + return FALSE + + W = locate() in T + if(W) + return FALSE // Spamclick protection. + + set_AI_busy(FALSE) + new web_type(T) + return TRUE + + +/mob/living/simple_animal/hostile/giant_spider/nurse/proc/lay_eggs(turf/T) + if(!istype(T)) + return FALSE + + if(!fed) + return FALSE + + var/obj/effect/spider/eggcluster/E = locate() in T + if(E) + return FALSE // Already got eggs here. + + visible_message(SPAN_NOTICE("\The [src] begins to lay a cluster of eggs.")) + // Get our AI to stay still. + set_AI_busy(TRUE) + // Stop players from spamming eggs. + laying_eggs = TRUE + + if(!do_after(src, 5 SECONDS, T)) + set_AI_busy(FALSE) + return FALSE + + E = locate() in T + if(E) + return FALSE // Spamclick protection. + + set_AI_busy(FALSE) + new egg_type(T) + fed-- + laying_eggs = FALSE + return TRUE + +/mob/living/simple_animal/hostile/giant_spider/nurse/proc/divorce() + if(paired_guard) + if(paired_guard.paired_nurse) + paired_guard.paired_nurse = null + paired_guard = null + +// Variant that 'blocks' light (by being a negative light source). +// This is done to make webbed rooms scary and allow for spiders on the other side of webs to see prey. +/obj/effect/spider/stickyweb/dark + name = "dense web" + desc = "It's sticky, and blocks a lot of light." + +/obj/effect/spider/stickyweb/dark/Initialize() + . = ..() + set_light(-1, 0.5, 1, 1, l_color = "#ffffff") + +// The AI for nurse spiders. Wraps things in webs by 'attacking' them. +/datum/ai_holder/simple_animal/melee/nurse_spider + wander = TRUE + base_wander_delay = 8 + cooperative = FALSE // So we don't ask our spider friends to attack things we're webbing. This might also make them stay at the base if their friends find tasty explorers. + returns_home = TRUE + +// Get us unachored objects as an option as well. +/datum/ai_holder/simple_animal/melee/nurse_spider/list_targets() + . = ..() + + var/static/alternative_targets = typecacheof(list(/obj/structure, /obj/effect/spider/spiderling)) + + var/mob/living/simple_animal/hostile/giant_spider/nurse/N = holder + if (!N.left_to_feed) + alternative_targets -= /obj/effect/spider/spiderling + + for(var/AT in typecache_filter_list(range(vision_range, holder), alternative_targets)) + var/obj/O = AT + if(can_see(holder, O, vision_range) && !O.anchored) + . += O + +// Select an obj if no mobs are around. +/datum/ai_holder/simple_animal/melee/nurse_spider/pick_target(list/targets) + var/mobs_only = locate(/mob/living) in targets // If a mob is in the list of targets, then ignore objects. + if(mobs_only) + for(var/A in targets) + if(!isliving(A)) + targets -= A + + var/mob/living/simple_animal/hostile/giant_spider/nurse/N = holder + if (!N.left_to_feed) + for (var/A in targets) + if (istype(A, /obj/effect/spider/spiderling)) + targets -= A + + return ..(targets) + +/datum/ai_holder/simple_animal/melee/nurse_spider/can_attack(atom/movable/the_target, vision_required = TRUE) + . = ..() + if (!.) // Parent returned FALSE. + if (istype(the_target, /obj)) + var/obj/O = the_target + if (!O.anchored) + return TRUE + + if (istype(the_target, /obj/effect/spider/spiderling)) + var/mob/living/simple_animal/hostile/giant_spider/nurse/N = holder + if (!N.left_to_feed) + lose_target() + return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider/spitter.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider/spitter.dm new file mode 100644 index 00000000000..a676ba32335 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider/spitter.dm @@ -0,0 +1,38 @@ +//spitters - fast, comparatively weak, very venomous; projectile attacks but will resort to melee once out of ammo +/mob/living/simple_animal/hostile/giant_spider/spitter + desc = "A monstrously huge iridescent spider with shimmering eyes." + icon_state = "purple" + icon_living = "purple" + icon_dead = "purple_dead" + maxHealth = 90 + health = 90 + poison_per_bite = 15 + ranged = TRUE + move_to_delay = 2 + projectiletype = /obj/item/projectile/venom + projectilesound = 'sound/effects/hypospray.ogg' + fire_desc = "spits venom" + ranged_range = 6 + pry_time = 7 SECONDS + flash_vulnerability = 2 + base_attack_cooldown = 2 SECONDS + + var/venom_charge = 16 + + ai_holder_type = /datum/ai_holder/simple_animal/spider/spitter + +/datum/ai_holder/simple_animal/spider/spitter/post_ranged_attack(atom/A) + . = ..() + var/mob/living/simple_animal/hostile/giant_spider/spitter/S = holder + S.venom_charge-- + +/mob/living/simple_animal/hostile/giant_spider/spitter/Life() + . = ..() + if(!.) + return FALSE + if(venom_charge <= 0) + ranged = FALSE + if(prob(25)) + venom_charge++ + if(venom_charge >= 8) + ranged = TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index d21e039b03a..c908bedda61 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -66,13 +66,14 @@ Teleporter beacon, and its subtypes maxHealth = 200 status_flags = 0 anchored = TRUE - stop_automated_movement = 1 var/bot_type = /mob/living/simple_animal/hostile/hivebot var/bot_amt = 10 var/spawn_delay = 100 var/spawn_time = 0 + ai_holder_type = /datum/ai_holder/simple_animal/hivebot/tele + /mob/living/simple_animal/hostile/hivebot/tele/New() ..() var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() @@ -80,6 +81,7 @@ Teleporter beacon, and its subtypes smoke.start() visible_message("\The [src] warps in!") playsound(src.loc, 'sound/effects/EMPulse.ogg', 25, 1) + set_AI_busy(TRUE) /mob/living/simple_animal/hostile/hivebot/tele/proc/warpbots() while(bot_amt > 0 && bot_type) @@ -90,11 +92,14 @@ Teleporter beacon, and its subtypes qdel(src) return -/mob/living/simple_animal/hostile/hivebot/tele/FindTarget() - if(..() && !spawn_time) - spawn_time = world.time + spawn_delay - visible_message("\The [src] turns on!") - icon_state = "def_radar" +/datum/ai_holder/simple_animal/hivebot/tele/find_target(list/possible_targets, has_targets_list) + . = ..() + + var/mob/living/simple_animal/hostile/hivebot/tele/T = holder + if(..() && !T.spawn_time) + T.spawn_time = world.time + T.spawn_delay + T.visible_message("\The [src] turns on!") + T.icon_state = "def_radar" return null /mob/living/simple_animal/hostile/hivebot/tele/Life() @@ -214,7 +219,7 @@ The megabot update_icon() /mob/living/simple_animal/hostile/hivebot/mega/proc/deactivate() - stop_automation = TRUE + set_AI_busy(TRUE) deactivated = TRUE visible_message(SPAN_MFAUNA("\The [src] clicks loudly as its lights fade and its motors grind to a halt!")) update_icon() @@ -224,7 +229,7 @@ The megabot addtimer(CALLBACK(src, .proc/reactivate), 4 SECONDS) /mob/living/simple_animal/hostile/hivebot/mega/proc/reactivate() - stop_automation = FALSE + set_AI_busy(FALSE) deactivated = FALSE visible_message(SPAN_MFAUNA("\The [src] whirs back to life!")) var/datum/extension/armor/toggle/armor = get_extension(src, /datum/extension/armor) @@ -232,14 +237,14 @@ The megabot armor.toggle(TRUE) update_icon() -/mob/living/simple_animal/hostile/hivebot/mega/OpenFire(target_mob) +/mob/living/simple_animal/hostile/hivebot/mega/shoot_target(target_mob) if(num_shots <= 0) if(attack_mode == ATTACK_MODE_LASER) switch_mode(ATTACK_MODE_MELEE) return ..() -/mob/living/simple_animal/hostile/hivebot/mega/Shoot(target, start, user, bullet) +/mob/living/simple_animal/hostile/hivebot/mega/shoot(target, start, user, bullet) ..() num_shots-- diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 31ae37a2ee6..66a682ba342 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -1,302 +1,20 @@ -/mob/living/simple_animal/hostile +/mob/living/simple_animal/hostile //TODO: cleanup faction = "hostile" - var/stance = HOSTILE_STANCE_IDLE //Used to determine behavior + var/stance = STANCE_IDLE //Used to determine behavior var/mob/living/target_mob var/attack_same = 0 var/ranged = 0 var/rapid = 0 var/sa_accuracy = 85 //base chance to hit out of 100 - var/projectiletype - var/projectilesound - var/casingtype var/fire_desc = "fires" //"X fire_desc at Y!" var/ranged_range = 6 //tiles of range for ranged attackers to attack var/move_to_delay = 4 //delay for the automated movement. var/attack_delay = DEFAULT_ATTACK_COOLDOWN - var/list/friends = list() //List of mobs that wont be picked as a target. Add to using weakref(). var/break_stuff_probability = 10 - stop_automated_movement_when_pulled = 0 var/destroy_surroundings = 1 a_intent = I_HURT var/shuttletarget = null var/enroute = 0 - var/stop_automation = FALSE //stops AI procs from running - var/can_pry = TRUE - var/pry_time = 7 SECONDS //time it takes for mob to pry open a door - var/pry_desc = "prying" //"X begins pry_desc the door!" - - //hostile mobs will bash through these in order with their natural weapon - var/list/valid_obstacles_by_priority = list(/obj/structure/window, - /obj/structure/closet, - /obj/machinery/door/window, - /obj/structure/table, - /obj/structure/grille, - /obj/structure/barricade, - /obj/structure/wall_frame, - /obj/structure/railing) - -/mob/living/simple_animal/hostile/proc/can_act() - if(stat || stop_automation || incapacitated()) - return FALSE - return TRUE - -/mob/living/simple_animal/hostile/proc/kick_stance() - if(target_mob) - stance = HOSTILE_STANCE_ATTACK - else - stance = HOSTILE_STANCE_IDLE - -/mob/living/simple_animal/hostile/proc/FindTarget() - if(!can_act()) - return null - if(!faction) //No faction, no reason to attack anybody. - return null - stop_automated_movement = 0 - for(var/mob/M in ListTargets(10)) - stance = HOSTILE_STANCE_ATTACK - return M - -/mob/living/simple_animal/hostile/proc/ValidTarget(var/mob/M) - if(M == src) - return FALSE - - if (M.status_flags & NOTARGET) - return FALSE - - if(istype(M, /mob/living/simple_animal/hostile)) - var/mob/living/simple_animal/hostile/H = M - if(H.faction == faction && !attack_same && !H.attack_same) - return FALSE - - if(istype(M)) - if(M.faction == faction) - return FALSE - if(weakref(M) in friends) - return FALSE - if(M.stat) - return FALSE - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.is_cloaked()) - return FALSE - - return TRUE - -/mob/living/simple_animal/hostile/proc/MoveToTarget() - if(!can_act()) - return - if(confused) - walk_to(src, pick(orange(2, src)), 1, move_to_delay) - return - stop_automated_movement = 1 - if(!target_mob || SA_attackable(target_mob)) - stance = HOSTILE_STANCE_IDLE - if(target_mob in ListTargets(10)) - if(ranged) - if(get_dist(src, target_mob) <= ranged_range) - OpenFire(target_mob) - else - walk_to(src, target_mob, 1, move_to_delay) - else - stance = HOSTILE_STANCE_ATTACKING - walk_to(src, target_mob, 1, move_to_delay) - -/mob/living/simple_animal/hostile/proc/AttackTarget() - stop_automated_movement = 1 - if(!target_mob || SA_attackable(target_mob)) - LoseTarget() - return 0 - if(!(target_mob in ListTargets(10))) - LostTarget() - return 0 - if (ishuman(target_mob)) - var/mob/living/carbon/human/H = target_mob - if (H.is_cloaked()) - LoseTarget() - return 0 - if(next_move >= world.time) - return 0 - if(get_dist(src, target_mob) <= 1) //Attacking - AttackingTarget() - return 1 - -/mob/living/simple_animal/hostile/proc/AttackingTarget() - face_atom(target_mob) - setClickCooldown(attack_delay) - if(!Adjacent(target_mob)) - return - if(isliving(target_mob)) - if(!prob(get_accuracy())) - visible_message("\The [src] misses its attack on \the [target_mob]!") - return - var/mob/living/L = target_mob - L.attackby(get_natural_weapon(), src) - return L - -/mob/living/simple_animal/hostile/proc/LoseTarget() - stance = HOSTILE_STANCE_IDLE - target_mob = null - walk(src, 0) - -/mob/living/simple_animal/hostile/proc/LostTarget() - stance = HOSTILE_STANCE_IDLE - walk(src, 0) - -/mob/living/simple_animal/hostile/proc/ListTargets(var/dist = world.view) - var/list/possible_targets = hearers(src, dist) - for(var/mob/M in possible_targets) - if(!ValidTarget(M)) - possible_targets -= M - return possible_targets - -/mob/living/simple_animal/hostile/proc/get_accuracy() - return Clamp(sa_accuracy - melee_accuracy_mods(), 0, 100) - -/mob/living/simple_animal/hostile/death(gibbed, deathmessage, show_dead_message) - ..(gibbed, deathmessage, show_dead_message) - walk(src, 0) - -/mob/living/simple_animal/hostile/Life() - . = ..() - if(!.) - walk(src, 0) - return 0 - if(client) - return 0 - if(!can_act()) - walk(src, 0) - kick_stance() - return 0 - - if(isturf(src.loc) && !src.buckled) - switch(stance) - if(HOSTILE_STANCE_IDLE) - target_mob = FindTarget() - - if(HOSTILE_STANCE_ATTACK) - face_atom(target_mob) - if(destroy_surroundings) - DestroySurroundings() - MoveToTarget() - - if(HOSTILE_STANCE_ATTACKING) - face_atom(target_mob) - if(destroy_surroundings) - DestroySurroundings() - AttackTarget() - if(HOSTILE_STANCE_INSIDE) //we aren't inside something so just switch - stance = HOSTILE_STANCE_IDLE - else - if(stance != HOSTILE_STANCE_INSIDE) - stance = HOSTILE_STANCE_INSIDE - walk(src,0) - target_mob = null - -/mob/living/simple_animal/hostile/attackby(var/obj/item/O, var/mob/user) - var/oldhealth = health - . = ..() - if(health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) - target_mob = user - MoveToTarget() - -/mob/living/simple_animal/hostile/attack_hand(mob/living/carbon/human/M) - . = ..() - if(M.a_intent == I_HURT && !incapacitated(INCAPACITATION_KNOCKOUT)) - target_mob = M - MoveToTarget() - -/mob/living/simple_animal/hostile/bullet_act(var/obj/item/projectile/Proj) - var/oldhealth = health - . = ..() - if(!target_mob && health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) - target_mob = Proj.firer - MoveToTarget() - -/mob/living/simple_animal/hostile/proc/OpenFire(target_mob) - var/target = target_mob - visible_message("\The [src] [fire_desc] at \the [target]!", 1) - - if(rapid) - spawn(1) - Shoot(target, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) - spawn(4) - Shoot(target, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) - spawn(6) - Shoot(target, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) - else - Shoot(target, src.loc, src) - if(casingtype) - new casingtype - - stance = HOSTILE_STANCE_IDLE - target_mob = null - return - -/mob/living/simple_animal/hostile/proc/Shoot(var/target, var/start, var/user, var/bullet = 0) - if(target == start) - return - - var/obj/item/projectile/A = new projectiletype(get_turf(user)) - playsound(user, projectilesound, 100, 1) - if(!A) return - var/def_zone = get_exposed_defense_zone(target) - A.launch_from_mob(target, src, def_zone) - -/mob/living/simple_animal/hostile/proc/DestroySurroundings() //courtesy of Lohikar - if(!can_act()) - return - if(prob(break_stuff_probability) && !Adjacent(target_mob)) - face_atom(target_mob) - var/turf/targ = get_step_towards(src, target_mob) - if(!targ) - return - - var/obj/effect/shield/S = locate(/obj/effect/shield) in targ - if(S && S.gen && S.gen.check_flag(MODEFLAG_NONHUMANS)) - S.attackby(get_natural_weapon(), src) - return - - for(var/type in valid_obstacles_by_priority) - var/obj/obstacle = locate(type) in targ - if(obstacle) - face_atom(obstacle) - obstacle.attackby(get_natural_weapon(), src) - return - - if(can_pry) - for(var/obj/machinery/door/obstacle in targ) - if(obstacle.density) - if(!obstacle.can_open(1)) - return - face_atom(obstacle) - var/pry_time_holder = (obstacle.pry_mod * pry_time) - pry_door(src, pry_time_holder, obstacle) - return - -/mob/living/simple_animal/hostile/proc/pry_door(var/mob/user, var/delay, var/obj/machinery/door/pesky_door) - visible_message("\The [user] begins [pry_desc] at \the [pesky_door]!") - stop_automation = TRUE - if(do_after(user, delay, pesky_door)) - pesky_door.open(1) - stop_automation = FALSE - else - visible_message("\The [user] is interrupted.") - stop_automation = FALSE - -/mob/living/simple_animal/hostile/proc/can_perform_ability() - if(!can_act() || time_last_used_ability > world.time) - return FALSE - return TRUE - -/mob/living/simple_animal/hostile/proc/cooldown_ability(var/time) - if(!time) - time = ability_cooldown - time_last_used_ability = world.time + ability_cooldown + ai_holder_type = /datum/ai_holder/simple_animal/melee diff --git a/code/modules/mob/living/simple_animal/hostile/leech.dm b/code/modules/mob/living/simple_animal/hostile/leech.dm index 92cfbd06c72..67f0c5ac0f4 100644 --- a/code/modules/mob/living/simple_animal/hostile/leech.dm +++ b/code/modules/mob/living/simple_animal/hostile/leech.dm @@ -16,9 +16,26 @@ flash_vulnerability = 0 bleed_colour = COLOR_VIOLET + ai_holder_type = /datum/ai_holder/simple_animal/melee/leech + var/suck_potency = 8 var/belly = 100 +/datum/ai_holder/simple_animal/melee/leech/engage_target() + . = ..() + + var/mob/living/simple_animal/hostile/leech/L = holder + if(ishuman(.) && L.belly <= 75) + var/mob/living/carbon/human/H = . + var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) + if(istype(S) && !length(S.breaches)) + return + H.remove_blood_simple(L.suck_potency) + if(L.health < L.maxHealth) + L.health += L.suck_potency / 1.5 + L.belly += Clamp(L.suck_potency, 0, 100) + + /mob/living/simple_animal/hostile/leech/Life() . = ..() if(!.) @@ -29,18 +46,6 @@ else belly -= 1 -/mob/living/simple_animal/hostile/leech/AttackingTarget() - . = ..() - if(ishuman(.) && belly <= 75) - var/mob/living/carbon/human/H = . - var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) - if(istype(S) && !length(S.breaches)) - return - H.remove_blood_simple(suck_potency) - if(health < maxHealth) - health += suck_potency / 1.5 - belly += Clamp(suck_potency, 0, 100) - /obj/structure/leech_spawner name = "reeds" desc = "Some reeds with a few funnel-like structures growing alongside." diff --git a/code/modules/mob/living/simple_animal/hostile/meat_horrors.dm b/code/modules/mob/living/simple_animal/hostile/meat_horrors.dm index 13bcc80fe12..4d2f96cbd68 100644 --- a/code/modules/mob/living/simple_animal/hostile/meat_horrors.dm +++ b/code/modules/mob/living/simple_animal/hostile/meat_horrors.dm @@ -6,9 +6,6 @@ icon_living = "horror" icon_dead = "horror_dead" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans..") - emote_see = list("snaps it's head at something..", "twitches", "stops suddenly") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -34,6 +31,9 @@ melee = ARMOR_MELEE_KNIVES ) + ai_holder_type = /datum/ai_holder/simple_animal/melee/meat + say_list = /datum/say_list/meat + /obj/item/natural_weapon/meatbits force = 30 sharp = TRUE @@ -48,9 +48,6 @@ icon_living = "abomination" icon_dead = "abomination_dead" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans..") - emote_see = list("snaps it's head at something..", "twitches", "stops suddenly") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -84,9 +81,6 @@ icon_living = "horror" icon_dead = "horror_dead" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans...") - emote_see = list("snaps it's head at something...", "twitches.", "stops suddenly.") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -120,9 +114,6 @@ icon_living = "horror_alt" icon_dead = "horror_alt_dead" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans...") - emote_see = list("turns to the sound..", "twitches", "stops suddenly, it's intestines slowly spilling out") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -148,6 +139,8 @@ melee = ARMOR_MELEE_KNIVES ) + say_list = /datum/say_list/meat/human + /mob/living/simple_animal/hostile/meat/humansecurity name = "turned security" desc = "What's left of a SAARE security guard. The only way you can tell is by the tatters of their uniform. That armor they wore in life now gives them a bit of hardiness in death..." @@ -156,9 +149,6 @@ icon_living = "horror_security" icon_dead = "horror_security_dead" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans...") - emote_see = list("snaps it's head at something...", "twitches.", "stops suddenly.") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -192,9 +182,6 @@ icon_living = "horror_miner" icon_dead = "horror_miner_dead" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans...") - emote_see = list("snaps it's head at something...", "twitches.", "stops suddenly.") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -228,9 +215,6 @@ icon_living = "lesser_ling" icon_dead = "" speak_emote = list("twitches.") - emote_hear = list("roars!", "groans...") - emote_see = list("snaps it's head at something...", "twitches.", "stops suddenly.") - speak_chance = 5 turns_per_move = 5 see_in_dark = 10 response_help = "pets" @@ -254,4 +238,15 @@ minbodytemp = 0 natural_armor = list( melee = ARMOR_MELEE_KNIVES - ) \ No newline at end of file + ) + +/datum/ai_holder/simple_animal/melee/meat + speak_chance = 5 + +/datum/say_list/meat + emote_hear = list("roars!", "groans..") + emote_see = list("snaps it's head at something..", "twitches", "stops suddenly") + +/datum/say_list/meat/human + emote_hear = list("roars!", "groans...") + emote_see = list("turns to the sound..", "twitches", "stops suddenly", "stops suddenly, it's intestines slowly spilling out") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 904356c5279..07071a31abd 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -47,23 +47,29 @@ var/global/list/protected_objects = list(/obj/machinery, var/knockdown_people = 0 pass_flags = PASS_FLAG_TABLE -/mob/living/simple_animal/hostile/mimic/New(newloc, var/obj/o, var/mob/living/creator) - ..() - if(o) - if(ispath(o)) - o = new o(newloc) - CopyObject(o,creator) +/datum/ai_holder/simple_animal/melee/mimic -/mob/living/simple_animal/hostile/mimic/FindTarget() +/datum/ai_holder/simple_animal/melee/mimic/find_target(list/possible_targets, has_targets_list) . = ..() + if(.) - audible_emote("growls at [.]") + var/mob/living/simple_animal/hostile/mimic/M = holder + M.audible_emote("growls at [.]") -/mob/living/simple_animal/hostile/mimic/ListTargets() +/datum/ai_holder/simple_animal/melee/mimic/list_targets() // Return a list of targets that isn't the creator . = ..() - if(creator) - return . - creator.resolve() + var/mob/living/simple_animal/hostile/mimic/M = holder + if(M.creator) + return . - M.creator.resolve() + + +/mob/living/simple_animal/hostile/mimic/New(newloc, var/obj/o, var/mob/living/creator) + ..() + if(o) + if(ispath(o)) + o = new o(newloc) + CopyObject(o,creator) /mob/living/simple_animal/hostile/mimic/proc/CopyObject(var/obj/O, var/mob/living/creator) @@ -117,14 +123,16 @@ var/global/list/protected_objects = list(/obj/machinery, M.dropInto(loc) qdel(src) - -/mob/living/simple_animal/hostile/mimic/DestroySurroundings() - if(destroy_objects) +/datum/ai_holder/simple_animal/melee/mimic/destroy_surroundings(direction, violent) + . = ..() + var/mob/living/simple_animal/hostile/mimic/M = holder + if(M.destroy_objects) ..() -/mob/living/simple_animal/hostile/mimic/AttackingTarget() - . =..() - if(knockdown_people) +/datum/ai_holder/simple_animal/melee/mimic/engage_target() + . = ..() + var/mob/living/simple_animal/hostile/mimic/M = holder + if(M.knockdown_people) var/mob/living/L = . if(istype(L)) if(prob(15)) @@ -137,19 +145,23 @@ var/global/list/protected_objects = list(/obj/machinery, ..() /mob/living/simple_animal/hostile/mimic/sleeping - wander = 0 - stop_automated_movement = 1 - var/awake = 0 -/mob/living/simple_animal/hostile/mimic/sleeping/ListTargets() - if(!awake) +/mob/living/simple_animal/hostile/mimic/sleeping/Initialize() + . = ..() + set_AI_busy(TRUE) + +/datum/ai_holder/simple_animal/melee/mimic/sleeping/list_targets() + . = ..() + var/mob/living/simple_animal/hostile/mimic/sleeping/M = holder + if(!M.awake) return null return ..() /mob/living/simple_animal/hostile/mimic/sleeping/proc/trigger() if(!awake) src.visible_message("\The [src] starts to move!") + set_AI_busy(FALSE) awake = 1 /mob/living/simple_animal/hostile/mimic/sleeping/adjustBruteLoss(var/damage) @@ -160,6 +172,7 @@ var/global/list/protected_objects = list(/obj/machinery, trigger() ..() -/mob/living/simple_animal/hostile/mimic/sleeping/DestroySurroundings() - if(awake) +/datum/ai_holder/simple_animal/melee/mimic/sleeping/destroy_surroundings(direction, violent) + var/mob/living/simple_animal/hostile/mimic/sleeping/M = holder + if(M.awake) ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/pike.dm b/code/modules/mob/living/simple_animal/hostile/pike.dm index bf69fd91b23..ce0df695440 100644 --- a/code/modules/mob/living/simple_animal/hostile/pike.dm +++ b/code/modules/mob/living/simple_animal/hostile/pike.dm @@ -29,8 +29,8 @@ /obj/item/natural_weapon/bite/pike force = 25 -/mob/living/simple_animal/hostile/carp/pike/carp_randomify() - return +// /mob/living/simple_animal/hostile/carp/pike/carp_randomify() +// return /mob/living/simple_animal/hostile/carp/pike/on_update_icon() return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm index e59b63b4af7..ea2eeeac53c 100644 --- a/code/modules/mob/living/simple_animal/hostile/pirate.dm +++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm @@ -4,13 +4,11 @@ icon_state = "piratemelee" icon_living = "piratemelee" icon_dead = "piratemelee_dead" - speak_chance = 0 turns_per_move = 5 response_help = "pushes" response_disarm = "shoves" response_harm = "hits" speed = 4 - stop_automated_movement_when_pulled = 0 maxHealth = 100 health = 100 can_escape = TRUE @@ -24,6 +22,8 @@ faction = "pirate" + ai_holder_type = /datum/ai_holder/simple_animal/melee/pirate + /mob/living/simple_animal/hostile/pirate/ranged name = "Pirate Gunner" icon_state = "pirateranged" @@ -36,6 +36,7 @@ corpse = /obj/effect/landmark/corpse/pirate/ranged weapon1 = /obj/item/gun/energy/laser + ai_holder_type = /datum/ai_holder/simple_animal/pirate/ranged /mob/living/simple_animal/hostile/pirate/death(gibbed, deathmessage, show_dead_message) ..(gibbed, deathmessage, show_dead_message) @@ -44,4 +45,12 @@ if(weapon1) new weapon1 (src.loc) qdel(src) - return \ No newline at end of file + return + +/datum/ai_holder/simple_animal/pirate/ranged + pointblank = TRUE // They get close? Just shoot 'em! + firing_lanes = TRUE // But not your buddies! + // conserve_ammo = TRUE // And don't go wasting bullets! + +/datum/ai_holder/simple_animal/melee/pirate + speak_chance = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm index 8a0f84ddaa1..56b6bdc9aa1 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -5,16 +5,11 @@ icon_living = "clown" icon_dead = "clown_dead" icon_gib = "clown_gib" - speak_chance = 0 turns_per_move = 5 response_help = "pokes" response_disarm = "gently pushes aside" response_harm = "hits" - speak = list("HONK", "Honk!", "Welcome to clown planet!") - emote_see = list("honks") - speak_chance = 1 a_intent = I_HURT - stop_automated_movement_when_pulled = 0 maxHealth = 75 health = 75 speed = -1 @@ -27,7 +22,16 @@ unsuitable_atmos_damage = 10 natural_weapon = /obj/item/natural_weapon/clown + ai_holder_type = /datum/ai_holder/simple_animal/clown + say_list = /datum/say_list/clown + /obj/item/natural_weapon/clown name = "bike horn" force = 10 - hitsound = 'sound/items/bikehorn.ogg' \ No newline at end of file + hitsound = 'sound/items/bikehorn.ogg' + +/datum/ai_holder/simple_animal/clown + speak_chance = 1 +/datum/say_list/clown + speak = list("HONK", "Honk!", "Welcome to clown planet!") + emote_see = list("honks") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index 85202cde466..8bd8aecdfcf 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -8,15 +8,11 @@ icon_dead = "drone_dead" ranged = 1 rapid = 0 - speak_chance = 5 turns_per_move = 3 response_help = "pokes" response_disarm = "gently pushes aside" response_harm = "hits" - speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.") - emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity") a_intent = I_HURT - stop_automated_movement_when_pulled = 0 health = 300 maxHealth = 300 speed = 8 @@ -32,6 +28,8 @@ skin_material = null skin_amount = 0 + ai_holder_type = /datum/ai_holder/simple_animal/ranged/malf_drone + var/datum/effect/effect/system/trail/ion_trail //the drone randomly switches between these states if it's malfunctioning @@ -54,6 +52,23 @@ var/has_loot = 1 faction = "malf_drone" +/datum/ai_holder/simple_animal/melee/malf_drone + +/datum/ai_holder/simple_animal/melee/malf_drone/list_targets() + . = ..() + + var/mob/living/simple_animal/hostile/retaliate/malf_drone/D = holder + if(D.hostile_drone) + var/list/targets = list() + for (var/mob/M in view(src, D.hostile_range)) + if (M == src || istype(M, /mob/living/simple_animal/hostile/retaliate/malf_drone)) + continue + targets |= M + + return targets + else + return ..() + /mob/living/simple_animal/hostile/retaliate/malf_drone/Initialize() . = ..() if(prob(5)) @@ -75,18 +90,6 @@ src.visible_message("[icon2html(src, viewers(get_turf(src)))] [src] suddenly lights up, and additional targetting vanes slide into place.") hostile_drone = 1 -/mob/living/simple_animal/hostile/retaliate/malf_drone/ListTargets() - if(hostile_drone) - var/list/targets = list() - for (var/mob/M in view(src, hostile_range)) - if (M == src || istype(M, /mob/living/simple_animal/hostile/retaliate/malf_drone)) - continue - targets |= M - - return targets - else - return ..() - //self repair systems have a chance to bring the drone back to life /mob/living/simple_animal/hostile/retaliate/malf_drone/Life() @@ -95,13 +98,13 @@ set_stat(UNCONSCIOUS) icon_state = "[initial(icon_state)]_dead" disabled-- - wander = 0 - speak_chance = 0 + set_wander (FALSE) + ai_holder.speak_chance = 0 if(disabled <= 0) set_stat(CONSCIOUS) icon_state = "[initial(icon_state)]0" - wander = 1 - speak_chance = 5 + set_wander (TRUE) + ai_holder.speak_chance = 5 //repair a bit of damage if(prob(1)) @@ -156,7 +159,7 @@ if(!exploding && !disabled && prob(explode_chance)) exploding = 1 set_stat(UNCONSCIOUS) - wander = 1 + set_wander(TRUE) walk(src,0) spawn(rand(50,150)) if(!disabled && exploding) @@ -286,6 +289,12 @@ ..() +/datum/ai_holder/simple_animal/ranged/malf_drone + speak_chance = 5 + +/datum/say_list/malf_drone + speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.") + emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity") /obj/item/projectile/beam/drone damage = 15 diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm index cc9d6b95d10..06daba76956 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm @@ -2,22 +2,7 @@ /mob/living/simple_animal/hostile/retaliate/beast var/hunger = 0 var/list/prey = list() - -/mob/living/simple_animal/hostile/retaliate/beast/ListTargets(var/dist = 7) - var/list/see = ..() - if(see.len) - return see - if(prey.len) - . = list() - for(var/weakref/W in prey) - var/mob/M = W.resolve() - if(M) - . += M - return - if(hunger > 500) //time to look for some food - for(var/mob/living/L in view(src, dist)) - if(!attack_same && L.faction != faction) - prey |= weakref(L) + // ai_holder_type = /datum/ai_holder/simple_animal/retaliate/beast /mob/living/simple_animal/hostile/retaliate/beast/Life() . = ..() @@ -25,7 +10,7 @@ return FALSE hunger++ if(hunger < 100) //stop hunting when satiated - prey.Cut() + ai_holder.hostile = FALSE else for(var/mob/living/simple_animal/S in range(src,1)) if(S.stat == DEAD) @@ -39,6 +24,9 @@ else qdel(S) + if (hunger > 500) + ai_holder.hostile = TRUE + /mob/living/simple_animal/proc/name_species(newname as text) set name = "Name Alien Species" set category = "IC" @@ -72,14 +60,13 @@ speed = 2 natural_weapon = /obj/item/natural_weapon/claws cold_damage_per_tick = 0 - speak_chance = 5 - speak = list("Hruuugh!","Hrunnph") - emote_see = list("paws the ground","shakes its mane","stomps") - emote_hear = list("snuffles") natural_armor = list( melee = ARMOR_MELEE_KNIVES ) + ai_holder_type = /datum/ai_holder/simple_animal/samak + say_list_type = /datum/say_list/samak + /mob/living/simple_animal/hostile/retaliate/beast/samak/alt desc = "A fast, armoured predator accustomed to hiding and ambushing." icon_state = "samak-alt" @@ -99,12 +86,11 @@ speed = 1 natural_weapon = /obj/item/natural_weapon/claws/weak cold_damage_per_tick = 0 - speak_chance = 5 - speak = list("Awrr?","Aowrl!","Worrl") - emote_see = list("sniffs the air cautiously","looks around") - emote_hear = list("snuffles") mob_size = MOB_SMALL + ai_holder_type = /datum/ai_holder/simple_animal/diyaab + say_list_type = /datum/say_list/diyaab + /mob/living/simple_animal/hostile/retaliate/beast/shantak name = "shantak" desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. Don't be fooled by its beauty though." @@ -118,16 +104,15 @@ speed = 1 natural_weapon = /obj/item/natural_weapon/claws cold_damage_per_tick = 0 - speak_chance = 2 - speak = list("Shuhn","Shrunnph?","Shunpf") - emote_see = list("scratches the ground","shakes out its mane","tinkles gently") + + ai_holder_type = /datum/ai_holder/simple_animal/shantak + say_list_type = /datum/say_list/shantak /mob/living/simple_animal/hostile/retaliate/beast/shantak/alt desc = "A piglike creature with a long and graceful mane. Don't be fooled by its beauty." icon_state = "shantak-alt" icon_living = "shantak-alt" icon_dead = "shantak-alt_dead" - emote_see = list("scratches the ground","shakes out it's mane","rustles softly") /mob/living/simple_animal/yithian name = "yithian" @@ -138,6 +123,8 @@ mob_size = MOB_TINY density = FALSE + ai_holder_type = /datum/ai_holder/simple_animal/passive + /mob/living/simple_animal/tindalos name = "tindalos" desc = "It looks like a large, flightless grasshopper." @@ -147,17 +134,20 @@ mob_size = MOB_TINY density = FALSE + ai_holder_type = /datum/ai_holder/simple_animal/passive + /mob/living/simple_animal/thinbug name = "taki" desc = "It looks like a bunch of legs." icon_state = "thinbug" icon_living = "thinbug" icon_dead = "thinbug_dead" - speak_chance = 1 - emote_hear = list("scratches the ground","chitters") mob_size = MOB_MINISCULE density = FALSE + ai_holder_type = /datum/ai_holder/simple_animal/passive/thinbug + say_list_type = /datum/say_list/thinbug + /mob/living/simple_animal/hostile/retaliate/royalcrab name = "cragenoy" desc = "It looks like a crustacean with an exceedingly hard carapace. Watch the pinchers!" @@ -170,12 +160,13 @@ health = 150 speed = 1 natural_weapon = /obj/item/natural_weapon/pincers - speak_chance = 1 - emote_see = list("skitters","oozes liquid from its mouth", "scratches at the ground", "clicks its claws") natural_armor = list( melee = ARMOR_MELEE_RESISTANT ) + ai_holder_type = /datum/ai_holder/simple_animal/royalcrab + say_list = /datum/say_list/royalcrab + /mob/living/simple_animal/hostile/retaliate/beast/charbaby name = "charbaby" desc = "A huge grubby creature." @@ -197,7 +188,18 @@ natural_armor = list( laser = ARMOR_LASER_HANDGUNS ) + ai_holder_type = /datum/ai_holder/simple_animal/melee/charbaby +/datum/ai_holder/simple_animal/melee/charbaby + +/datum/ai_holder/simple_animal/melee/charbaby/engage_target() + . = ..() + var/mob/living/simple_animal/hostile/retaliate/beast/charbaby/C = holder + if(isliving(C.target_mob) && prob(25)) + var/mob/living/L = C.target_mob + if(prob(10)) + L.adjust_fire_stacks(1) + L.IgniteMob() /obj/item/natural_weapon/charbaby name = "scalding hide" damtype = BURN @@ -208,17 +210,78 @@ . = ..() reflect_unarmed_damage(H, BURN, "amorphous mass") -/mob/living/simple_animal/hostile/retaliate/beast/charbaby/AttackingTarget() - . = ..() - if(isliving(target_mob) && prob(25)) - var/mob/living/L = target_mob - if(prob(10)) - L.adjust_fire_stacks(1) - L.IgniteMob() - /mob/living/simple_animal/hostile/retaliate/beast/shantak/lava desc = "A vaguely canine looking beast. It looks as though its fur is made of stone wool." icon_state = "lavadog" icon_living = "lavadog" icon_dead = "lavadog_dead" + + say_list_type = /datum/say_list/shantak/lava + +//Mob AI's + +// /datum/ai_holder/simple_animal/retaliate/beast/list_targets() +// . = ..() + +// var/mob/living/simple_animal/hostile/retaliate/beast/B = holder +// var/list/see = ..() +// if(see.len) +// return see +// if(B.prey.len) +// . = list() +// for(var/weakref/W in B.prey) +// var/mob/M = W.resolve() +// if(M) +// . += M +// return +// if(B.hunger > 500) //time to look for some food +// hostile = TRUE + + +/* AI */ + +/datum/ai_holder/simple_animal/diyaab/post_melee_attack(atom/A) + . = ..() + if(holder.Adjacent(A)) + holder.IMove(get_step(holder, pick(GLOB.alldirs))) + holder.face_atom(A) + +/datum/ai_holder/simple_animal/samak + speak_chance = 5 + +/datum/ai_holder/simple_animal/diyaab + speak_chance = 5 + +/datum/ai_holder/simple_animal/shantak + speak_chance = 2 + +/datum/ai_holder/simple_animal/passive/thinbug + speak_chance = 1 + +/datum/ai_holder/simple_animal/royalcrab + speak_chance = 1 + +/* Say Lists */ + +/datum/say_list/samak + speak = list("Hruuugh!","Hrunnph") + emote_see = list("paws the ground","shakes its mane","stomps") + emote_hear = list("snuffles") + +/datum/say_list/diyaab + speak = list("Awrr?","Aowrl!","Worrl") + emote_see = list("sniffs the air cautiously","looks around") + emote_hear = list("snuffles") + +/datum/say_list/shantak + speak = list("Shuhn","Shrunnph?","Shunpf") + emote_see = list("scratches the ground","shakes out its mane","tinkles gently") + +/datum/say_list/thinbug + emote_hear = list("scratches the ground","chitters") + +/datum/say_list/royalcrab + emote_see = list("skitters","oozes liquid from its mouth", "scratches at the ground", "clicks its claws") + +/datum/say_list/shantak/lava speak = list("Karuph","Karump") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm index 31142ae6564..fefb651ae2e 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm @@ -5,10 +5,6 @@ icon_living = "bluecrab" icon_dead = "bluecrab_dead" mob_size = MOB_LARGE - speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 1 turns_per_move = 5 response_help = "pats" response_disarm = "gently nudges" @@ -26,7 +22,7 @@ return_damage_max = 5 harm_intent_damage = 1 natural_armor = list( - melee = ARMOR_MELEE_RESISTANT, + melee = ARMOR_MELEE_RESISTANT, bullet = ARMOR_BALLISTIC_PISTOL ) ability_cooldown = 2 MINUTES @@ -37,6 +33,9 @@ var/list/grab_desc = list("thrashes", "squeezes", "crushes") var/continue_grab_prob = 35 //probability that a successful grab will be extended by one life tick + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/crab + say_list_type = /datum/say_list/crab + /obj/item/natural_weapon/pincers/giant force = 15 attack_verb = list("snipped", "pinched", "crushed") @@ -62,38 +61,17 @@ if(!.) return FALSE - - if((health > maxHealth / 1.5) && enemies.len && prob(10)) + + if((health > maxHealth / 1.5) && length(ai_holder.attackers) && prob(10)) if(victim) release_grab() - enemies = list() - LoseTarget() + ai_holder.attackers = list() //TODO: does this still work? + ai_holder.lose_target() visible_message("\The [src] lowers its pincer.") -/mob/living/simple_animal/hostile/retaliate/giant_crab/AttackingTarget() +/mob/living/simple_animal/hostile/retaliate/giant_crab/can_special_attack(mob/living/carbon/human/H) . = ..() - if(ishuman(.)) - var/mob/living/carbon/human/H = . - if(victim == H) - if(!Adjacent(victim)) - release_grab() - else if(prob(continue_grab_prob)) - H.Weaken(1) - H.Stun(1) - grab_damage++ - visible_message(SPAN_MFAUNA("\The [src] tightens its grip on \the [victim]!")) - return - if(!victim && can_perform_ability(H)) - GLOB.destroyed_event.register(victim, src, .proc/release_grab) - victim = H - H.Weaken(grab_duration) - H.Stun(grab_duration) - visible_message(SPAN_MFAUNA("\The [src] catches \the [victim] in its powerful pincer!")) - stop_automation = TRUE - -/mob/living/simple_animal/hostile/retaliate/giant_crab/can_perform_ability(mob/living/carbon/human/H) - . = ..() if(!.) return FALSE if(!Adjacent(H)) @@ -112,6 +90,32 @@ visible_message(SPAN_NOTICE("\The [src] releases its grip on \the [victim]!")) GLOB.destroyed_event.unregister(victim) victim = null - cooldown_ability(ability_cooldown) - stop_automation = FALSE - grab_damage = initial(grab_damage) \ No newline at end of file + // cooldown_ability(ability_cooldown) + set_AI_busy(FALSE) + grab_damage = initial(grab_damage) + + +/datum/ai_holder/simple_animal/retaliate/crab/engage_target() + . = ..() + var/mob/living/simple_animal/hostile/retaliate/giant_crab/C = holder + if(ishuman(.)) + var/mob/living/carbon/human/H = . + if(C.victim == H) + if(!C.Adjacent(C.victim)) + C.release_grab() + else if(prob(C.continue_grab_prob)) + H.Weaken(1) + H.Stun(1) + C.grab_damage++ + C.visible_message(SPAN_MFAUNA("\The [src] tightens its grip on \the [C.victim]!")) + return + + if(!C.victim && C.can_special_attack(H)) + GLOB.destroyed_event.register(C.victim, C, /mob/living/simple_animal/hostile/retaliate/giant_crab/proc/release_grab) + C.victim = H + H.Weaken(C.grab_duration) + H.Stun(C.grab_duration) + C.visible_message(SPAN_MFAUNA("\The [src] catches \the [C.victim] in its powerful pincer!")) + set_busy(TRUE) +/datum/ai_holder/simple_animal/retaliate/crab + speak_chance = 1 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm index b7adfe28146..bacd996ba48 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm @@ -5,9 +5,7 @@ health = 750 //how sweet it is to be a god! maxHealth = 750 mob_size = MOB_LARGE - speak = list("...") speak_emote = list("professes","speaks unto you","elaborates","proclaims") - emote_hear = list("sings a song to herself", "preens herself") natural_weapon = /obj/item/natural_weapon/giant min_gas = null max_gas = null @@ -35,6 +33,9 @@ /decl/parrot_subspecies/black) var/get_subspecies_name = TRUE + ai_holder_type = /datum/ai_holder/simple_animal/retaliate + say_list = /datum/say_list/parrot/space + /mob/living/simple_animal/hostile/retaliate/parrot/space/Initialize() . = ..() var/subspecies_type = DEFAULTPICK(subspecies, null) @@ -49,28 +50,32 @@ transform = M update_icon() -/mob/living/simple_animal/hostile/retaliate/parrot/space/AttackingTarget() +/mob/living/simple_animal/hostile/retaliate/parrot/space/can_special_attack(mob/living/carbon/human/H) + . = ..() + + if(!.) + return FALSE + if(!Adjacent(H)) + return FALSE + +/mob/living/simple_animal/hostile/retaliate/parrot/space/do_special_attack(atom/A) . = ..() - if(ishuman(.) && can_perform_ability(.)) - var/mob/living/carbon/human/H = . + + if(ishuman(A) && can_special_attack(A)) + var/mob/living/carbon/human/H = A if(prob(70)) H.Weaken(rand(2,3)) - cooldown_ability(ability_cooldown / 1.5) + // cooldown_ability(ability_cooldown / 1.5) visible_message(SPAN_MFAUNA("\The [src] flaps its wings mightily and bowls over \the [H] with a gust!")) else if(H.get_equipped_item(slot_head)) var/obj/item/clothing/head/HAT = H.get_equipped_item(slot_head) if(H.canUnEquip(HAT)) visible_message(SPAN_MFAUNA("\The [src] rips \the [H]'s [HAT] off!")) - cooldown_ability(ability_cooldown) + // cooldown_ability(ability_cooldown) H.unEquip(HAT, get_turf(src)) -/mob/living/simple_animal/hostile/retaliate/parrot/space/can_perform_ability(mob/living/carbon/human/H) - . = ..() - if(!.) - return FALSE - if(!Adjacent(H)) - return FALSE + //subtypes /mob/living/simple_animal/hostile/retaliate/parrot/space/lesser @@ -88,7 +93,10 @@ health = 350 maxHealth = 350 speak_emote = list("squawks") - emote_hear = list("preens itself") natural_weapon = /obj/item/natural_weapon/large relax_chance = 30 - impatience = 5 \ No newline at end of file + impatience = 5 + +/datum/say_list/parrot/space + speak = list("...") + emote_hear = list("sings a song to herself", "preens herself") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm index 53419734fbd..e6b1cf945a5 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm @@ -5,10 +5,7 @@ icon_state = "goose" icon_living = "goose" icon_dead = "goose_dead" - speak = list("Honk!") speak_emote = list("honks") - emote_hear = list("honks","flaps its wings","clacks") - emote_see = list("flaps its wings", "scratches the ground") response_help = "pets" response_disarm = "gently pushes aside" response_harm = "strikes" @@ -32,6 +29,16 @@ var/max_damage = 22 var/loose = FALSE //goose loose status + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/goose + say_list_type = /datum/say_list/goose + + +/datum/ai_holder/simple_animal/retaliate/goose/react_to_attack(atom/movable/attacker) + . = ..() + var/mob/living/simple_animal/hostile/retaliate/goose/G = holder + if(G.stat == CONSCIOUS) + G.enrage(G.enrage_potency) + /obj/item/natural_weapon/goosefeet name = "goose feet" gender = PLURAL @@ -40,11 +47,6 @@ damtype = BRUTE canremove = FALSE -/mob/living/simple_animal/hostile/retaliate/goose/Retaliate() - ..() - if(stat == CONSCIOUS) - enrage(enrage_potency) - /mob/living/simple_animal/hostile/retaliate/goose/on_update_icon() if(stat == DEAD) icon_state = icon_dead @@ -87,3 +89,8 @@ icon_state = "goose_labcoat" icon_living = "goose_labcoat" icon_dead = "goose_labcoat_dead" + +/datum/say_list/goose + speak = list("Honk!") + emote_hear = list("honks","flaps its wings","clacks") + emote_see = list("flaps its wings", "scratches the ground") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm index b99fdbc1e34..d0ee12b1f7f 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm @@ -10,10 +10,11 @@ health = 75 speed = 1 natural_weapon = /obj/item/natural_weapon/tentacles - speak_chance = 1 - emote_see = list("wobbles slightly","oozes something out of tentacles' ends") var/gets_random_color = TRUE + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/jelly + say_list = /datum/say_list/jelly + /obj/item/natural_weapon/tentacles name = "tentacles" attack_verb = list("stung","slapped") @@ -104,4 +105,10 @@ health = 20 maxHealth = 20 jelly_scale = 0.1875 - split_type = null \ No newline at end of file + split_type = null + +/datum/ai_holder/simple_animal/retaliate/jelly + speak_chance = 1 + +/datum/say_list/jelly + emote_see = list("wobbles slightly","oozes something out of tentacles' ends") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm index d81b10759b3..18a4f6fd352 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm @@ -12,8 +12,6 @@ icon_living = "king_goat" icon_dead = "goat_dead" speak_emote = list("brays in a booming voice") - emote_hear = list("brays in a booming voice") - emote_see = list("stamps a mighty foot, shaking the surroundings") meat_amount = 12 response_help = "placates" response_harm = "assaults" @@ -36,6 +34,89 @@ ) var/stun_chance = 5 //chance per attack to Weaken target + ai_holder_type = /datum/ai_holder/simple_animal/goat/king + say_list = /datum/say_list/goat/king + +/datum/ai_holder/simple_animal/goat/king + +/datum/ai_holder/simple_animal/goat/king/engage_target() + . = ..() + var/mob/living/simple_animal/hostile/retaliate/goat/king/G = holder + if(isliving(G.target_mob)) + var/mob/living/L = G.target_mob + if(prob(G.stun_chance)) + L.Weaken(0.5) + L.confused += 1 + G.visible_message(SPAN_WARNING("\The [L] is bowled over by the impact of [G]'s attack!")) + +/datum/ai_holder/simple_animal/goat/king/react_to_attack(atom/movable/attacker) + . = ..() + + if(holder.stat == CONSCIOUS && prob(5)) + holder.visible_message(SPAN_WARNING("The [holder] bellows indignantly, with a judgemental gleam in his eye.")) + +/datum/ai_holder/simple_animal/goat/king/phase2/engage_target() + . = ..() + + var/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/G = holder + if(G.current_damtype != BRUTE) + G.special_attacks++ + +/datum/ai_holder/simple_animal/goat/king/phase2/react_to_attack(atom/movable/attacker) + . = ..() + var/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/G = holder + if(G.spellscast < 5) + if(prob(5) && G.move_to_delay != 1) //speed buff + G.spellscast++ + G.visible_message(SPAN_MFAUNA("\The [G] shimmers and seems to phase in and out of reality itself!")) + G.move_to_delay = 1 + + else if(prob(5)) //stun move + G.spellscast++ + G.visible_message(SPAN_MFAUNA("\The [G]' fleece flashes with blinding light!")) + new /obj/item/grenade/flashbang/instant(G.loc) + + else if(prob(5)) //spawn adds + G.spellscast++ + G.visible_message(SPAN_MFAUNA("\The [G] summons the imperial guard to his aid, and they appear in a flash!")) + new /mob/living/simple_animal/hostile/retaliate/goat/guard/master(get_step(G,pick(GLOB.cardinal))) + new /mob/living/simple_animal/hostile/retaliate/goat/guard(get_step(G,pick(GLOB.cardinal))) + new /mob/living/simple_animal/hostile/retaliate/goat/guard(get_step(G,pick(GLOB.cardinal))) + + else if(prob(5)) //EMP blast + G.spellscast++ + G.visible_message(SPAN_MFAUNA("\The [G] disrupts nearby electrical equipment!")) + empulse(get_turf(G), 5, 2, 0) + + else if(prob(5) && G.current_damtype == BRUTE && !G.special_attacks) //elemental attacks + G.spellscast++ + if(prob(50)) + G.visible_message(SPAN_MFAUNA("\The [G]' horns flicker with holy white flame!")) + G.current_damtype = BURN + else + G.visible_message(SPAN_MFAUNA("\The [G]' horns glimmer, electricity arcing between them!")) + G.current_damtype = ELECTROCUTE + + else if(prob(5)) //earthquake spell + G.visible_message("\The [G]' eyes begin to glow ominously as dust and debris in the area is kicked up in a light breeze.") + set_busy(TRUE) + if(do_after(G, 6 SECONDS)) + var/health_holder = G.health + G.visible_message(SPAN_MFAUNA("\The [G] raises its fore-hooves and stomps them into the ground with incredible force!")) + explosion(get_step(G,pick(GLOB.cardinal)), -1, 2, 2, 3, 6) + explosion(get_step(G,pick(GLOB.cardinal)), -1, 1, 4, 4, 6) + explosion(get_step(G,pick(GLOB.cardinal)), -1, 3, 4, 3, 6) + set_busy(FALSE) + G.spellscast += 2 + if(!G.health < health_holder) + G.health = health_holder //our own magicks cannot harm us + else + G.visible_message(SPAN_NOTICE("The [G] loses concentration and huffs haughtily.")) + set_busy(FALSE) + + else return + + /mob/living/simple_animal/hostile/retaliate/goat/king/get_natural_weapon() if(!(current_damtype in elemental_weapons)) return ..() @@ -123,65 +204,6 @@ natural_weapon = /obj/item/natural_weapon/goathorns move_to_delay = 3 -/mob/living/simple_animal/hostile/retaliate/goat/king/Retaliate() - ..() - if(stat == CONSCIOUS && prob(5)) - visible_message(SPAN_WARNING("The [src] bellows indignantly, with a judgemental gleam in his eye.")) - -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/Retaliate() - set waitfor = FALSE - ..() - if(spellscast < 5) - if(prob(5) && move_to_delay != 1) //speed buff - spellscast++ - visible_message(SPAN_MFAUNA("\The [src] shimmers and seems to phase in and out of reality itself!")) - move_to_delay = 1 - - else if(prob(5)) //stun move - spellscast++ - visible_message(SPAN_MFAUNA("\The [src]' fleece flashes with blinding light!")) - new /obj/item/grenade/flashbang/instant(src.loc) - - else if(prob(5)) //spawn adds - spellscast++ - visible_message(SPAN_MFAUNA("\The [src] summons the imperial guard to his aid, and they appear in a flash!")) - new /mob/living/simple_animal/hostile/retaliate/goat/guard/master(get_step(src,pick(GLOB.cardinal))) - new /mob/living/simple_animal/hostile/retaliate/goat/guard(get_step(src,pick(GLOB.cardinal))) - new /mob/living/simple_animal/hostile/retaliate/goat/guard(get_step(src,pick(GLOB.cardinal))) - - else if(prob(5)) //EMP blast - spellscast++ - visible_message(SPAN_MFAUNA("\The [src] disrupts nearby electrical equipment!")) - empulse(get_turf(src), 5, 2, 0) - - else if(prob(5) && current_damtype == BRUTE && !special_attacks) //elemental attacks - spellscast++ - if(prob(50)) - visible_message(SPAN_MFAUNA("\The [src]' horns flicker with holy white flame!")) - current_damtype = BURN - else - visible_message(SPAN_MFAUNA("\The [src]' horns glimmer, electricity arcing between them!")) - current_damtype = ELECTROCUTE - - else if(prob(5)) //earthquake spell - visible_message("\The [src]' eyes begin to glow ominously as dust and debris in the area is kicked up in a light breeze.") - stop_automation = TRUE - if(do_after(src, 6 SECONDS)) - var/health_holder = health - visible_message(SPAN_MFAUNA("\The [src] raises its fore-hooves and stomps them into the ground with incredible force!")) - explosion(get_step(src,pick(GLOB.cardinal)), -1, 2, 2, 3, 6) - explosion(get_step(src,pick(GLOB.cardinal)), -1, 1, 4, 4, 6) - explosion(get_step(src,pick(GLOB.cardinal)), -1, 3, 4, 3, 6) - stop_automation = FALSE - spellscast += 2 - if(!health < health_holder) - health = health_holder //our own magicks cannot harm us - else - visible_message(SPAN_NOTICE("The [src] loses concentration and huffs haughtily.")) - stop_automation = FALSE - - else return - /mob/living/simple_animal/hostile/retaliate/goat/king/phase2/proc/phase3_transition() phase3 = TRUE spellscast = 0 @@ -234,19 +256,9 @@ QDEL_NULL(boss_theme) . = ..() -/mob/living/simple_animal/hostile/retaliate/goat/king/AttackingTarget() - . = ..() - if(isliving(target_mob)) - var/mob/living/L = target_mob - if(prob(stun_chance)) - L.Weaken(0.5) - L.confused += 1 - visible_message(SPAN_WARNING("\The [L] is bowled over by the impact of [src]'s attack!")) - -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/AttackingTarget() - . = ..() - if(current_damtype != BRUTE) - special_attacks++ - /mob/living/simple_animal/hostile/retaliate/goat/king/Allow_Spacemove(check_drift = 0) - return 1 \ No newline at end of file + return 1 + +/datum/say_list/goat/king + emote_hear = list("brays in a booming voice") + emote_see = list("stamps a mighty foot, shaking the surroundings") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm index 2f50d563877..364af9aadc8 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm @@ -35,20 +35,15 @@ pass_flags = PASS_FLAG_TABLE mob_size = MOB_SMALL - speak = list("Hi","Hello!","Cracker?") speak_emote = list("squawks","says","yells") - emote_hear = list("squawks","bawks") - emote_see = list("flutters its wings") natural_weapon = /obj/item/natural_weapon/beak - speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s turns_per_move = 5 meat_type = /obj/item/reagent_containers/food/snacks/cracker/ response_help = "pets" response_disarm = "gently moves aside" response_harm = "swats" - stop_automated_movement = 1 universal_speak = TRUE meat_type = /obj/item/reagent_containers/food/snacks/meat/chicken/game @@ -100,6 +95,8 @@ var/impatience = 5 //we lose this much from relax_chance each time we calm down var/icon_set = "parrot" + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/parrot + /mob/living/simple_animal/hostile/retaliate/parrot/New() ..() @@ -172,7 +169,7 @@ src.say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") ears.dropInto(loc) ears = null - for(var/possible_phrase in speak) + for(var/possible_phrase in say_list.speak) if(copytext(possible_phrase,1,3) in department_radio_keys) possible_phrase = copytext(possible_phrase,3,length(possible_phrase)) else @@ -292,7 +289,7 @@ if(!.) return FALSE - if(enemies.len && prob(relax_chance)) + if(length(ai_holder.attackers) && prob(relax_chance)) give_up() if(simple_parrot) @@ -318,10 +315,10 @@ Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. Then it clears the buffer to make sure they don't magically remember something from hours ago. */ if(speech_buffer.len && prob(10)) - if(speak.len) - speak.Remove(pick(speak)) + if(say_list.speak.len) + say_list.speak.Remove(pick(say_list.speak)) - speak.Add(pick(speech_buffer)) + say_list.speak.Add(pick(speech_buffer)) speech_buffer.Cut() //-----SLEEPING @@ -344,11 +341,11 @@ parrot_sleep_dur = parrot_sleep_max //Cycle through message modes for the headset - if(speak.len) + if(say_list.speak.len) var/list/newspeak = list() if(available_channels.len && src.ears) - for(var/possible_phrase in speak) + for(var/possible_phrase in say_list.speak) //50/50 chance to not use the radio at all var/useradio = 0 @@ -363,11 +360,11 @@ newspeak.Add(possible_phrase) else //If we have no headset or channels to use, don't try to use any! - for(var/possible_phrase in speak) + for(var/possible_phrase in say_list.speak) if(copytext(possible_phrase,1,3) in department_radio_keys) possible_phrase = "[copytext(possible_phrase,3,length(possible_phrase)+1)]" //crop out the channel prefix newspeak.Add(possible_phrase) - speak = newspeak + say_list.speak = newspeak //Search for item to steal parrot_interest = search_for_item() @@ -571,8 +568,8 @@ return null /mob/living/simple_animal/hostile/retaliate/parrot/proc/give_up() - enemies = list() - LoseTarget() + ai_holder.attackers = list() + ai_holder.lose_target() visible_message("\The [src] seems to calm down.") relax_chance -= impatience @@ -699,7 +696,6 @@ /mob/living/simple_animal/hostile/retaliate/parrot/Poly name = "Poly" desc = "Poly the Parrot. An expert on quantum cracker theory." - speak = list("Poly wanna cracker!", ":e Check the singlo, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN HARDSUITS?",":e OH GOD ITS FREE CALL THE SHUTTLE") /mob/living/simple_animal/hostile/retaliate/parrot/Poly/New() ears = new /obj/item/device/radio/headset/headset_eng(src) @@ -778,3 +774,6 @@ /mob/living/simple_animal/hostile/retaliate/parrot/proc/can_pick_up(obj/item/I) . = (Adjacent(I) && I.w_class <= parrot_isize && !I.anchored) + +/datum/ai_holder/simple_animal/retaliate/parrot + speak_chance = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index fd17c36ad98..fa8ffaf667c 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -1,31 +1,2 @@ /mob/living/simple_animal/hostile/retaliate - var/list/enemies = list() - -/mob/living/simple_animal/hostile/retaliate/ListTargets(var/dist = world.view) - . = list() - if(!enemies.len) - return - var/possible_targets = ..() - for(var/weakref/W in enemies) - var/mob/M = W.resolve() - if(M in possible_targets) - . += M - -/mob/living/simple_animal/hostile/retaliate/proc/AddEnemies(var/list/possible_enemies) - for(var/mob/M in possible_enemies) - if(ValidTarget(M)) - enemies |= weakref(M) - -/mob/living/simple_animal/hostile/retaliate/proc/FindAllies(var/list/possible_allies) - for(var/mob/living/simple_animal/hostile/retaliate/H in possible_allies) - if(!attack_same && !H.attack_same && H.faction == faction) - H.enemies |= enemies - -/mob/living/simple_animal/hostile/retaliate/proc/Retaliate(var/dist = world.view) - var/list/possible_targets_or_allies = hearers(usr, dist) - AddEnemies(possible_targets_or_allies) - FindAllies(possible_targets_or_allies) - -/mob/living/simple_animal/hostile/retaliate/adjustBruteLoss(var/damage) - ..(damage) - Retaliate(10) + ai_holder_type = /datum/ai_holder/simple_animal/retaliate \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/space_whale.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/space_whale.dm index e6f4857339a..77a526b98f3 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/space_whale.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/space_whale.dm @@ -17,7 +17,6 @@ turns_per_move = 10 attack_delay = 2 SECONDS move_to_delay = 2 SECONDS - stop_automated_movement_when_pulled = FALSE mob_size = MOB_LARGE mob_bump_flag = HEAVY @@ -25,9 +24,6 @@ mob_push_flags = ALLMOBS can_escape = TRUE - emote_hear = list("vocalises", "sings", "hums") - emote_see = list("flaps around", "rolls") - faction = "whales" natural_weapon = /obj/item/natural_weapon/whalebone response_help = "strokes" @@ -39,7 +35,10 @@ var/chosen_color var/species_colors = list(COLOR_COMMAND_BLUE, COLOR_PURPLE, COLOR_DARK_BLUE_GRAY, COLOR_PALE_PINK) - var/mob/living/simple_animal/juvenile_space_whale/baby + var/mob/living/simple_animal/friendly/juvenile_space_whale/baby + + ai_holder_type = /datum/ai_holder/simple_animal/retaliate + say_list_type = /datum/say_list/space_whale /obj/item/natural_weapon/whalebone name = "head" @@ -64,15 +63,19 @@ bound_height = 64 bound_width = 128 -/mob/living/simple_animal/hostile/retaliate/space_whale/Retaliate() //So they dont become hostile when spawned and thrown in migration event. - if(!(health < (maxHealth - 5))) - return - ..() +// /mob/living/simple_animal/hostile/retaliate/space_whale/Retaliate() //So they dont become hostile when spawned and thrown in migration event. +// if(!(health < (maxHealth - 5))) +// return +// ..() -/mob/living/simple_animal/hostile/retaliate/space_whale/ValidTarget(var/mob/M) - . = ..() - if(istype(M, /mob/living/simple_animal/juvenile_space_whale)) - return FALSE +// /mob/living/simple_animal/hostile/retaliate/space_whale/ValidTarget(var/mob/M) +// . = ..() +// if(istype(M, /mob/living/simple_animal/friendly/juvenile_space_whale)) +// return FALSE /mob/living/simple_animal/hostile/retaliate/space_whale/Allow_Spacemove() - return TRUE \ No newline at end of file + return TRUE + +/datum/say_list/space_whale + emote_hear = list("vocalises", "sings", "hums") + emote_see = list("flaps around", "rolls") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm index 0ec5a97a31b..0b1aed6a068 100644 --- a/code/modules/mob/living/simple_animal/hostile/russian.dm +++ b/code/modules/mob/living/simple_animal/hostile/russian.dm @@ -5,13 +5,11 @@ icon_living = "russianmelee" icon_dead = "russianmelee_dead" icon_gib = "syndicate_gib" - speak_chance = 0 turns_per_move = 5 response_help = "pokes" response_disarm = "shoves" response_harm = "hits" speed = 4 - stop_automated_movement_when_pulled = 0 maxHealth = 100 health = 100 harm_intent_damage = 5 @@ -24,6 +22,7 @@ faction = "russian" status_flags = CANPUSH + ai_holder_type = /datum/ai_holder/simple_animal/humanoid /mob/living/simple_animal/hostile/russian/ranged icon_state = "russianranged" diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 291312c24af..b3df6a5d9c7 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -5,13 +5,11 @@ icon_living = "syndicate" icon_dead = "syndicate_dead" icon_gib = "syndicate_gib" - speak_chance = 0 turns_per_move = 5 response_help = "pokes" response_disarm = "shoves" response_harm = "hits" speed = 4 - stop_automated_movement_when_pulled = 0 maxHealth = 100 health = 100 harm_intent_damage = 5 diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm index f26888a9a84..95264ca0c0a 100644 --- a/code/modules/mob/living/simple_animal/hostile/tree.dm +++ b/code/modules/mob/living/simple_animal/hostile/tree.dm @@ -6,7 +6,6 @@ icon_living = "pine_1" icon_dead = "pine_1" icon_gib = "pine_1" - speak_chance = 0 turns_per_move = 5 meat_type = /obj/item/reagent_containers/food/snacks/fish response_help = "brushes" @@ -20,6 +19,8 @@ natural_weapon = /obj/item/natural_weapon/bite + ai_holder_type = /datum/ai_holder/simple_animal/melee/tree + //Space carp aren't affected by atmos. min_gas = null max_gas = null @@ -27,13 +28,15 @@ faction = "carp" -/mob/living/simple_animal/hostile/tree/FindTarget() +/datum/ai_holder/simple_animal/melee/tree/find_target(list/possible_targets, has_targets_list) . = ..() + if(.) - audible_emote("growls at [.]") + holder.audible_emote("growls at [.]") + +/datum/ai_holder/simple_animal/melee/tree/engage_target() + . = ..() -/mob/living/simple_animal/hostile/tree/AttackingTarget() - . =..() var/mob/living/L = . if(istype(L)) if(prob(15)) diff --git a/code/modules/mob/living/simple_animal/hostile/vagrant.dm b/code/modules/mob/living/simple_animal/hostile/vagrant.dm index 9db9b344834..296b7edc197 100644 --- a/code/modules/mob/living/simple_animal/hostile/vagrant.dm +++ b/code/modules/mob/living/simple_animal/hostile/vagrant.dm @@ -10,7 +10,6 @@ maxHealth = 60 health = 20 speed = 5 - speak_chance = 0 turns_per_move = 4 move_to_delay = 4 response_help = "pets the" @@ -32,6 +31,31 @@ bleed_colour = "#aad9de" + ai_holder_type = /datum/ai_holder/hostile/melee/vagrant + +/datum/ai_holder/hostile/melee/vagrant + +/datum/ai_holder/hostile/melee/vagrant/engage_target() + . = ..() + + if(ishuman(.)) + var/mob/living/carbon/human/H = . + var/mob/living/simple_animal/hostile/vagrant/V = holder + if(V.gripping == H) + H.Weaken(1) + H.Stun(1) + return + //This line ensures there's always a reasonable chance of grabbing, while still + //Factoring in health + if(!V.gripping && (V.cloaked || prob(V.health + ((V.maxHealth - V.health) * 2)))) + V.gripping = H + V.cloaked = 0 + V.update_icon() + H.Weaken(1) + H.Stun(1) + H.visible_message("\the [src] latches onto \the [H], pulsating!") + V.forceMove(V.gripping.loc) + /mob/living/simple_animal/hostile/vagrant/Allow_Spacemove(var/check_drift = 0) return 1 @@ -41,7 +65,7 @@ if((target_mob != Proj.firer) && health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) //Respond to being shot at target_mob = Proj.firer turns_per_move = 3 - MoveToTarget() + ai_holder.walk_to_target() /mob/living/simple_animal/hostile/vagrant/death(gibbed) . = ..() @@ -69,7 +93,7 @@ if(turns_per_move != initial(turns_per_move)) turns_per_move = initial(turns_per_move) - if(stance == HOSTILE_STANCE_IDLE && !cloaked) + if(stance == STANCE_IDLE && !cloaked) cloaked = 1 update_icon() if(health == maxHealth) @@ -90,25 +114,6 @@ set_light(0.2, 0.1, 3) move_to_delay = 2 -/mob/living/simple_animal/hostile/vagrant/AttackingTarget() - . = ..() - if(ishuman(.)) - var/mob/living/carbon/human/H = . - if(gripping == H) - H.Weaken(1) - H.Stun(1) - return - //This line ensures there's always a reasonable chance of grabbing, while still - //Factoring in health - if(!gripping && (cloaked || prob(health + ((maxHealth - health) * 2)))) - gripping = H - cloaked = 0 - update_icon() - H.Weaken(1) - H.Stun(1) - H.visible_message("\the [src] latches onto \the [H], pulsating!") - src.forceMove(gripping.loc) - /mob/living/simple_animal/hostile/vagrant/swarm/Initialize() . = ..() if(prob(75)) new/mob/living/simple_animal/hostile/vagrant(loc) diff --git a/code/modules/mob/living/simple_animal/hostile/voxslug.dm b/code/modules/mob/living/simple_animal/hostile/voxslug.dm index 941a3889450..91d870e68db 100644 --- a/code/modules/mob/living/simple_animal/hostile/voxslug.dm +++ b/code/modules/mob/living/simple_animal/hostile/voxslug.dm @@ -25,21 +25,33 @@ Small, little HP, poisonous. natural_weapon = /obj/item/natural_weapon/bite faction = SPECIES_VOX -/mob/living/simple_animal/hostile/voxslug/ListTargets(var/dist = 7) + ai_holder_type = /datum/ai_holder/hostile/melee/voxslug + +/datum/ai_holder/hostile/melee/voxslug/list_targets() + . = ..() + var/list/L = list() - for(var/a in hearers(src, dist)) + for(var/a in hearers(src, vision_range)) if(istype(a,/mob/living/carbon/human)) var/mob/living/carbon/human/H = a if(H.species.get_bodytype() == SPECIES_VOX) continue if(isliving(a)) var/mob/living/M = a - if(M.faction == faction) + if(M.faction == holder.faction) continue L += a return L +/datum/ai_holder/hostile/melee/voxslug/engage_target() + . = ..() + var/mob/living/simple_animal/hostile/voxslug/V = holder + if(istype(., /mob/living/carbon/human)) + var/mob/living/carbon/human/H = . + if(prob(H.getBruteLoss()/2)) + V.attach(H) + /mob/living/simple_animal/hostile/voxslug/get_scooped(var/mob/living/carbon/grabber) if(grabber.species.get_bodytype() != SPECIES_VOX) to_chat(grabber, "\The [src] wriggles out of your hands before you can pick it up!") @@ -58,13 +70,6 @@ Small, little HP, poisonous. chest.embed(holder,0,"\The [src] latches itself onto \the [H]!") holder.sync(src) -/mob/living/simple_animal/hostile/voxslug/AttackingTarget() - . = ..() - if(istype(., /mob/living/carbon/human)) - var/mob/living/carbon/human/H = . - if(prob(H.getBruteLoss()/2)) - attach(H) - /mob/living/simple_animal/hostile/voxslug/Life() . = ..() if(. && istype(src.loc, /obj/item/holder) && isliving(src.loc.loc)) //We in somebody diff --git a/code/modules/mob/living/simple_animal/life.dm b/code/modules/mob/living/simple_animal/life.dm new file mode 100644 index 00000000000..3074668cf34 --- /dev/null +++ b/code/modules/mob/living/simple_animal/life.dm @@ -0,0 +1,105 @@ +/mob/living/simple_animal/Life() + . = ..() + if(!.) + return FALSE + if(!living_observers_present(GetConnectedZlevels(z))) + return + //Health + if(stat == DEAD) + if(health > 0) + icon_state = icon_living + switch_from_dead_to_living_mob_list() + set_stat(CONSCIOUS) + set_density(1) + return 0 + + handle_atmos() + + if(health <= 0) + death() + return + + if(health > maxHealth) + health = maxHealth + + handle_stunned() + handle_weakened() + handle_paralysed() + handle_confused() + handle_supernatural() + handle_impaired_vision() + + handle_special() + + if(can_bleed && bleed_ticks > 0) + handle_bleeding() + + if(buckled && can_escape) + if(istype(buckled, /obj/effect/energy_net)) + var/obj/effect/energy_net/Net = buckled + Net.escape_net(src) + else if(prob(50)) + escape(src, buckled) + else if(prob(50)) + visible_message("\The [src] struggles against \the [buckled]!") + + return 1 + + +/mob/living/simple_animal/proc/handle_atmos(var/atmos_suitable = 1) + //Atmos + + if(!loc) + return + + var/datum/gas_mixture/environment = loc.return_air() + if(!(MUTATION_SPACERES in mutations) && environment) + + if(abs(environment.temperature - bodytemperature) > 40 ) + bodytemperature += ((environment.temperature - bodytemperature) / 5) + + // don't bother checking it twice if we got a supplied 0 val. + if(atmos_suitable) + if(LAZYLEN(min_gas)) + for(var/gas in min_gas) + if(environment.gas[gas] < min_gas[gas]) + atmos_suitable = FALSE + break + if(atmos_suitable && LAZYLEN(max_gas)) + for(var/gas in max_gas) + if(environment.gas[gas] > max_gas[gas]) + atmos_suitable = FALSE + break + + //Atmos effect + if(bodytemperature < minbodytemp) + fire_alert = 2 + adjustBruteLoss(cold_damage_per_tick) + else if(bodytemperature > maxbodytemp) + fire_alert = 1 + adjustBruteLoss(heat_damage_per_tick) + else + fire_alert = 0 + + if(!atmos_suitable) + adjustBruteLoss(unsuitable_atmos_damage) + +/mob/living/simple_animal/proc/escape(mob/living/M, obj/O) + O.unbuckle_mob(M) + visible_message("\The [M] escapes from \the [O]!") + +/mob/living/simple_animal/proc/handle_supernatural() + if(purge) + purge -= 1 + +/mob/living/simple_animal/gib() + ..(icon_gib,1) + +/mob/living/simple_animal/proc/visible_emote(var/act_desc) + custom_emote(1, act_desc) + +/mob/living/simple_animal/proc/audible_emote(var/act_desc) + custom_emote(2, act_desc) + +/mob/living/simple_animal/proc/handle_special() + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 26fb46a0ddc..bdf7e9b56c4 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -10,7 +10,6 @@ health = 50 universal_speak = TRUE speak_emote = list("hisses") - emote_hear = list("wails","screeches") response_help = "puts their hand through" response_disarm = "flails at" response_harm = "punches" @@ -20,7 +19,6 @@ min_gas = null max_gas = null speed = -1 - stop_automated_movement = 1 status_flags = 0 faction = "cult" supernatural = 1 @@ -35,6 +33,9 @@ skin_material = null skin_amount = 0 + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/shade + say_list_type = /datum/say_list/shade + /obj/item/natural_weapon/shade name = "foul touch" attack_verb = list("drained") @@ -57,3 +58,10 @@ ghostize() qdel(src) return + + +/datum/ai_holder/simple_animal/retaliate/shade + hostile = FALSE + +/datum/say_list/shade + emote_hear = list("wails","screeches") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index f96c84ee3f7..c64baa33fe4 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -21,23 +21,14 @@ var/icon_living = "" var/icon_dead = "" var/icon_gib = null //We only try to show a gibbing animation if this exists. - - var/list/speak = list("...") - var/speak_chance = 0 - var/list/emote_hear = list() //Hearable emotes - var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + var/icon_rest = null // The iconstate for resting, optional var/turns_per_move = 1 var/turns_since_move = 0 - var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. - var/wander = 1 // Does the mob wander around when idle? - var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. - //Interaction var/response_help = "tries to help" var/response_disarm = "tries to disarm" var/response_harm = "tries to hurt" - var/harm_intent_damage = 3 var/can_escape = FALSE // 'smart' simple animals such as human enemies, or things small, big, sharp or strong enough to power out of a net //Temperature effect @@ -47,6 +38,55 @@ var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp var/fire_alert = 0 + var/min_oxy = 5 // Oxygen in moles, minimum, 0 is 'no minimum' + var/max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' + var/min_tox = 0 // Phoron min + var/max_tox = 1 // Phoron max + var/min_co2 = 0 // CO2 min + var/max_co2 = 5 // CO2 max + var/min_n2 = 0 // N2 min + var/max_n2 = 0 // N2 max + var/unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above + + //Attack ranged settings + var/projectiletype // The projectiles I shoot + var/projectilesound // The sound I make when I do it + var/projectile_accuracy = 0 // Accuracy modifier to add onto the bullet when its fired. + var/projectile_dispersion = 0 // How many degrees to vary when I do it. + var/casingtype + + // Reloading settings, part of ranged code + var/needs_reload = FALSE // If TRUE, mob needs to reload occasionally + var/reload_max = 1 // How many shots the mob gets before it has to reload, will not be used if needs_reload is FALSE + var/reload_count = 0 // A counter to keep track of how many shots the mob has fired so far. Reloads when it hits reload_max. + var/reload_time = 4 SECONDS // How long it takes for a mob to reload. This is to buy a player a bit of time to run or fight. + var/reload_sound = 'sound/weapons/flipblade.ogg' // What sound gets played when the mob successfully reloads. Defaults to the same sound as reloading guns. Can be null. + + //Hostility settings + var/taser_kill = 1 // Is the mob weak to tasers + + //Mob melee settings + var/list/attacktext = list("attacked") // "You are [attacktext] by the mob!" + var/attack_sound = null // Sound to play when I attack + var/attack_armor_pen = 0 // How much armor pen this attack has. + + var/melee_attack_delay = 4 // If set, the mob will do a windup animation and can miss if the target moves out of the way. + var/ranged_attack_delay = null + var/special_attack_delay = null + + //Mob interaction + var/list/friends = list() // Mobs on this list wont get attacked regardless of faction status. + var/harm_intent_damage = 3 // How much an unarmed harm click does to this mob. + var/list/loot_list = list() // The list of lootable objects to drop, with "/path = prob%" structure + var/obj/item/card/id/myid// An ID card if they have one to give them access to stuff. + + //Movement things. + var/movement_cooldown = 5 // Lower is faster. + var/movement_sound = null // If set, will play this sound when it moves on its own will. + var/turn_sound = null // If set, plays the sound when the mob's dir changes in most cases. + var/movement_shake_radius = 0 // If set, moving will shake the camera of all living mobs within this radius slightly. + var/aquatic_movement = 0 // If set, the mob will move through fluids with no hinderance. + //Atmos effect - Yes, you can make creatures that require phoron or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage var/list/min_gas = list(GAS_OXYGEN = 5) var/list/max_gas = list(GAS_PHORON = 1, GAS_CO2 = 5) @@ -63,9 +103,48 @@ var/list/natural_armor //what armor animal has var/flash_vulnerability = 1 // whether or not the mob can be flashed; 0 = no, 1 = yes, 2 = very yes - //Null rod stuff - var/supernatural = 0 - var/purge = 0 + var/can_pry = TRUE + var/pry_time = 7 SECONDS //time it takes for mob to pry open a door + var/pry_desc = "prying" //"X begins pry_desc the door!" + + //Special attacks + var/special_attack_min_range = null // The minimum distance required for an attempt to be made. + var/special_attack_max_range = null // The maximum for an attempt. + var/special_attack_charges = null // If set, special attacks will work off of a charge system, and won't be usable if all charges are expended. Good for grenades. + var/special_attack_cooldown = null // If set, special attacks will have a cooldown between uses. + var/last_special_attack = null // world.time when a special attack occured last, for cooldown calculations. + + //Damage resistances + var/grab_resist = 0 // Chance for a grab attempt to fail. Note that this is not a true resist and is just a prob() of failure. + var/list/armor = list( // Values for normal getarmor() checks + "melee" = 0, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + var/list/armor_soak = list( // Values for getsoak() checks. + "melee" = 0, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + // Protection against heat/cold/electric/water effects. + // 0 is no protection, 1 is total protection. Negative numbers increase vulnerability. + var/heat_resist = 0.0 + var/cold_resist = 0.0 + var/shock_resist = 0.0 + var/water_resist = 1.0 + var/poison_resist = 0.0 + var/thick_armor = FALSE // Stops injections and "injections". + var/purge = 0 // Cult stuff. + var/supernatural = FALSE // Ditto. var/bleed_ticks = 0 var/bleed_colour = COLOR_BLOOD_HUMAN @@ -90,255 +169,8 @@ /mob/living/simple_animal/Destroy() if(istype(natural_weapon)) QDEL_NULL(natural_weapon) - . = ..() -/mob/living/simple_animal/Life() . = ..() - if(!.) - return FALSE - if(!living_observers_present(GetConnectedZlevels(z))) - return - //Health - if(stat == DEAD) - if(health > 0) - icon_state = icon_living - switch_from_dead_to_living_mob_list() - set_stat(CONSCIOUS) - set_density(1) - return 0 - - handle_atmos() - - if(health <= 0) - death() - return - - if(health > maxHealth) - health = maxHealth - - handle_stunned() - handle_weakened() - handle_paralysed() - handle_confused() - handle_supernatural() - handle_impaired_vision() - - if(can_bleed && bleed_ticks > 0) - handle_bleeding() - - if(buckled && can_escape) - if(istype(buckled, /obj/effect/energy_net)) - var/obj/effect/energy_net/Net = buckled - Net.escape_net(src) - else if(prob(50)) - escape(src, buckled) - else if(prob(50)) - visible_message("\The [src] struggles against \the [buckled]!") - - //Movement - if(!client && !stop_automated_movement && wander && !anchored) - if(isturf(src.loc) && !resting) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled - SelfMove(pick(GLOB.cardinal)) - turns_since_move = 0 - - //Speaking - if(!client && speak_chance) - if(rand(0,200) < speak_chance) - var/action = pick( - speak.len; "speak", - emote_hear.len; "emote_hear", - emote_see.len; "emote_see" - ) - - switch(action) - if("speak") - say(pick(speak)) - if("emote_hear") - audible_emote("[pick(emote_hear)].") - if("emote_see") - visible_emote("[pick(emote_see)].") - return 1 - -/mob/living/simple_animal/proc/handle_atmos(var/atmos_suitable = 1) - //Atmos - - if(!loc) - return - - var/datum/gas_mixture/environment = loc.return_air() - if(!(MUTATION_SPACERES in mutations) && environment) - - if(abs(environment.temperature - bodytemperature) > 40 ) - bodytemperature += ((environment.temperature - bodytemperature) / 5) - - // don't bother checking it twice if we got a supplied 0 val. - if(atmos_suitable) - if(LAZYLEN(min_gas)) - for(var/gas in min_gas) - if(environment.gas[gas] < min_gas[gas]) - atmos_suitable = FALSE - break - if(atmos_suitable && LAZYLEN(max_gas)) - for(var/gas in max_gas) - if(environment.gas[gas] > max_gas[gas]) - atmos_suitable = FALSE - break - - //Atmos effect - if(bodytemperature < minbodytemp) - fire_alert = 2 - adjustBruteLoss(cold_damage_per_tick) - else if(bodytemperature > maxbodytemp) - fire_alert = 1 - adjustBruteLoss(heat_damage_per_tick) - else - fire_alert = 0 - - if(!atmos_suitable) - adjustBruteLoss(unsuitable_atmos_damage) - -/mob/living/simple_animal/proc/escape(mob/living/M, obj/O) - O.unbuckle_mob(M) - visible_message("\The [M] escapes from \the [O]!") - -/mob/living/simple_animal/proc/handle_supernatural() - if(purge) - purge -= 1 - -/mob/living/simple_animal/gib() - ..(icon_gib,1) - -/mob/living/simple_animal/proc/visible_emote(var/act_desc) - custom_emote(1, act_desc) - -/mob/living/simple_animal/proc/audible_emote(var/act_desc) - custom_emote(2, act_desc) - -/mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj) - if(!Proj || Proj.nodamage) - return - - var/damage = Proj.damage - if(Proj.damtype == STUN) - damage = Proj.damage / 6 - if(Proj.damtype == BRUTE) - damage = Proj.damage / 2 - if(Proj.damtype == BURN) - damage = Proj.damage / 1.5 - if(Proj.agony) - damage += Proj.agony / 6 - if(health < Proj.agony * 3) - Paralyse(Proj.agony / 20) - visible_message("[src] is stunned momentarily!") - - bullet_impact_visuals(Proj) - adjustBruteLoss(damage) - Proj.on_hit(src) - return 0 - -/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M as mob) - ..() - - switch(M.a_intent) - - if(I_HELP) - if (health > 0) - M.visible_message("[M] [response_help] \the [src].") - M.update_personal_goal(/datum/goal/achievement/specific_object/pet, type) - - if(I_DISARM) - M.visible_message("[M] [response_disarm] \the [src].") - M.do_attack_animation(src) - //TODO: Push the mob away or something - - if(I_HURT) - var/dealt_damage = harm_intent_damage - var/harm_verb = response_harm - if(ishuman(M) && M.species) - var/datum/unarmed_attack/attack = M.get_unarmed_attack(src) - dealt_damage = attack.damage <= dealt_damage ? dealt_damage : attack.damage - harm_verb = pick(attack.attack_verb) - if(attack.sharp || attack.edge) - adjustBleedTicks(dealt_damage) - - adjustBruteLoss(dealt_damage) - M.visible_message("[M] [harm_verb] \the [src]!") - M.do_attack_animation(src) - - return - -/mob/living/simple_animal/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/stack/medical)) - if(stat != DEAD) - var/obj/item/stack/medical/MED = O - if(!MED.animal_heal) - to_chat(user, "That [MED] won't help \the [src] at all!") - return - if(health < maxHealth) - if(MED.can_use(1)) - adjustBruteLoss(-MED.animal_heal) - visible_message("[user] applies the [MED] on [src].") - MED.use(1) - else - to_chat(user, "\The [src] is dead, medical items won't bring \him back to life.") - return - - if(istype(O, /obj/item/device/flash)) - if(stat != DEAD) - O.attack(src, user, user.zone_sel ? user.zone_sel.selecting : ran_zone()) - return - - if(meat_type && (stat == DEAD) && meat_amount) - if(istype(O, /obj/item/material/knife/kitchen/cleaver)) - var/victim_turf = get_turf(src) - if(!locate(/obj/structure/table, victim_turf)) - to_chat(user, SPAN_NOTICE("You need to place \the [src] on a table to butcher it.")) - return - var/time_to_butcher = (mob_size) - to_chat(user, SPAN_NOTICE("You begin harvesting \the [src].")) - if(do_after(user, time_to_butcher, src, do_flags = DO_DEFAULT & ~DO_BOTH_CAN_TURN)) - if(prob(user.skill_fail_chance(SKILL_COOKING, 60, SKILL_ADEPT))) - to_chat(user, SPAN_NOTICE("You botch harvesting \the [src], and ruin some of the meat in the process.")) - subtract_meat(user) - return - else - harvest(user, user.get_skill_value(SKILL_COOKING)) - return - else - to_chat(user, SPAN_NOTICE("Your hand slips with your movement, and some of the meat is ruined.")) - subtract_meat(user) - return - - else - if(!O.force) - visible_message("[user] gently taps [src] with \the [O].") - else - O.attack(src, user, user.zone_sel ? user.zone_sel.selecting : ran_zone()) - -/mob/living/simple_animal/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) - - visible_message("\The [src] has been attacked with \the [O] by [user]!") - - if(O.force <= resistance) - to_chat(user, "This weapon is ineffective; it does no damage.") - return 0 - - var/damage = O.force - if (O.damtype == PAIN) - damage = 0 - if (O.damtype == STUN) - damage = (O.force / 8) - if(supernatural && istype(O,/obj/item/nullrod)) - damage *= 2 - purge = 3 - adjustBruteLoss(damage) - if(O.edge || O.sharp) - adjustBleedTicks(damage) - - return 1 /mob/living/simple_animal/movement_delay() var/tally = ..() //Incase I need to add stuff other than "speed" later @@ -398,18 +230,6 @@ ..() updatehealth() -/mob/living/simple_animal/proc/SA_attackable(target_mob) - if (isliving(target_mob)) - var/mob/living/L = target_mob - - if (L.status_flags & NOTARGET) - return TRUE - - if(!L.stat && L.health >= 0) - return FALSE - - return TRUE - /mob/living/simple_animal/say(var/message) var/verb = "says" if(speak_emote.len) @@ -505,18 +325,43 @@ else return FLASH_PROTECTION_MAJOR -/mob/living/simple_animal/proc/reflect_unarmed_damage(var/mob/living/carbon/human/attacker, var/damage_type, var/description) - if(attacker.a_intent == I_HURT) - var/hand_hurtie - if(attacker.hand) - hand_hurtie = BP_L_HAND - else - hand_hurtie = BP_R_HAND - attacker.apply_damage(rand(return_damage_min, return_damage_max), damage_type, hand_hurtie, used_weapon = description) - if(rand(25)) - to_chat(attacker, SPAN_WARNING("Your attack has no obvious effect on \the [src]'s [description]!")) - -/mob/living/simple_animal/proc/get_natural_weapon() - if(ispath(natural_weapon)) - natural_weapon = new natural_weapon(src) - return natural_weapon \ No newline at end of file +/mob/living/simple_animal/SelfMove(turf/n, direct, movetime) + var/turf/old_turf = get_turf(src) + var/old_dir = dir + . = ..() + if(. && movement_shake_radius) + for(var/mob/living/L in range(movement_shake_radius, src)) + shake_camera(L, 1, 1) + if(turn_sound && dir != old_dir) + playsound(src, turn_sound, 50, 1) + else if(movement_sound && old_turf != get_turf(src)) // Playing both sounds at the same time generally sounds bad. + playsound(src, movement_sound, 50, 1) + +/mob/living/simple_animal/movement_delay() + . = movement_cooldown + + // Turf related slowdown + var/turf/T = get_turf(src) + if(T && T.movement_delay && !is_floating) // Flying mobs ignore turf-based slowdown. Aquatic mobs ignore water slowdown, and can gain bonus speed in it. + . += T.movement_delay + + if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move. + if(. <= 0) + . = 1 + . *= purge + + if(a_intent == "walk") + . *= 1.5 + + . += ..() + +/mob/living/simple_animal/proc/pry_door(var/mob/user, var/delay, var/obj/machinery/door/pesky_door) + visible_message(SPAN_WARNING("\The [user] begins [pry_desc] at \the [pesky_door]!")) + set_AI_busy(TRUE) + if(do_after(user, delay, pesky_door)) + pesky_door.open(1) + ai_holder.prying = FALSE + set_AI_busy(FALSE) + else + visible_message(SPAN_NOTICE("\The [user] is interrupted.")) + set_AI_busy(FALSE) \ No newline at end of file diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 8bd1113b356..2649cd8f53e 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -26,6 +26,11 @@ if(delay) delay.AddDelay(timeout) +/mob/proc/checkMoveCooldown() + if(world.time < next_move) + return FALSE // Need to wait more. + return TRUE + /client/proc/client_dir(input, direction=-1) return turn(input, direction*dir2angle(dir)) diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index 666c60f7dd0..89e2509ae9d 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -370,7 +370,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp //find a viable mouse candidate - var/mob/living/simple_animal/mouse/host + var/mob/living/simple_animal/friendly/mouse/host var/obj/machinery/atmospherics/unary/vent_pump/vent_found var/list/found_vents = list() for(var/obj/machinery/atmospherics/unary/vent_pump/v in SSmachines.machinery) @@ -378,7 +378,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp found_vents.Add(v) if(found_vents.len) vent_found = pick(found_vents) - host = new /mob/living/simple_animal/mouse(vent_found.loc) + host = new /mob/living/simple_animal/friendly/mouse(vent_found.loc) else to_chat(src, "Unable to find any unwelded vents to spawn mice at.") if(host) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 101a7ec3726..497a62fce84 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -189,7 +189,7 @@ for(var/t in organs) //this really should not be necessary qdel(t) - var/mob/living/simple_animal/corgi/new_corgi = new /mob/living/simple_animal/corgi (loc) + var/mob/living/simple_animal/friendly/corgi/new_corgi = new /mob/living/simple_animal/friendly/corgi (loc) new_corgi.a_intent = I_HURT new_corgi.key = key @@ -271,21 +271,21 @@ return 0 //Verbs do not appear for players. These constructs should really have their own class simple_animal/construct/subtype //Good mobs! - if(ispath(MP, /mob/living/simple_animal/cat)) + if(ispath(MP, /mob/living/simple_animal/friendly/cat)) return 1 - if(ispath(MP, /mob/living/simple_animal/corgi)) + if(ispath(MP, /mob/living/simple_animal/friendly/corgi)) return 1 if(ispath(MP, /mob/living/simple_animal/crab)) return 1 if(ispath(MP, /mob/living/simple_animal/hostile/carp)) return 1 - if(ispath(MP, /mob/living/simple_animal/mushroom)) + if(ispath(MP, /mob/living/simple_animal/friendly/mushroom)) return 1 if(ispath(MP, /mob/living/simple_animal/shade)) return 1 - if(ispath(MP, /mob/living/simple_animal/tomato)) + if(ispath(MP, /mob/living/simple_animal/friendly/tomato)) return 1 - if(ispath(MP, /mob/living/simple_animal/mouse)) + if(ispath(MP, /mob/living/simple_animal/friendly/mouse)) return 1 //It is impossible to pull up the player panel for mice (Fixed! - Nodrak) if(ispath(MP, /mob/living/simple_animal/hostile/bear)) return 1 //Bears will auto-attack mobs, even if they're player controlled (Fixed! - Nodrak) diff --git a/code/modules/overmap/exoplanets/planet_types/grass.dm b/code/modules/overmap/exoplanets/planet_types/grass.dm index a184589a60d..192956cb2b1 100644 --- a/code/modules/overmap/exoplanets/planet_types/grass.dm +++ b/code/modules/overmap/exoplanets/planet_types/grass.dm @@ -81,17 +81,17 @@ lightlevel = 0.5 has_trees = TRUE flora_diversity = 8 - fauna_types = list(/mob/living/simple_animal/cat, /mob/living/simple_animal/chicken, /mob/living/simple_animal/mouse, /mob/living/simple_animal/opossum, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/hostile/retaliate/goose, /mob/living/simple_animal/cow) + fauna_types = list(/mob/living/simple_animal/friendly/cat, /mob/living/simple_animal/friendly/chicken, /mob/living/simple_animal/friendly/mouse, /mob/living/simple_animal/friendly/opossum, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/hostile/retaliate/goose, /mob/living/simple_animal/friendly/cow) megafauna_types = list(/mob/living/simple_animal/hostile/retaliate/parrot/space/megafauna, /mob/living/simple_animal/hostile/retaliate/goose/dire) //Animals being named alien creature is a bit odd as these would just be earth transplants - species = list( /mob/living/simple_animal/cat = "wild cat", - /mob/living/simple_animal/chicken = "wild chicken", - /mob/living/simple_animal/mouse = "mouse", - /mob/living/simple_animal/opossum = "opossum", + species = list( /mob/living/simple_animal/friendly/cat = "wild cat", + /mob/living/simple_animal/friendly/chicken = "wild chicken", + /mob/living/simple_animal/friendly/mouse = "mouse", + /mob/living/simple_animal/friendly/opossum = "opossum", /mob/living/simple_animal/hostile/retaliate/goat = "wild goat", /mob/living/simple_animal/hostile/retaliate/goose = "goose", - /mob/living/simple_animal/cow = "wild cow") + /mob/living/simple_animal/friendly/cow = "wild cow") /obj/effect/overmap/visitable/sector/exoplanet/grass/terraformed/generate_habitability() habitability_class = HABITABILITY_IDEAL diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 76c49758d0c..fb5964cd4c0 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -433,6 +433,7 @@ yo = null xo = null var/result = 0 //To pass the message back to the gun. + var/atom/hit_thing /obj/item/projectile/test/Bump(atom/A as mob|obj|turf|area, forced=0) if(A == firer) @@ -442,6 +443,7 @@ return if(istype(A, /mob/living) || istype(A, /obj/vehicle)) result = 2 //We hit someone, return 1! + hit_thing = A return result = 1 return @@ -460,7 +462,9 @@ /obj/item/projectile/test/Process(var/turf/targloc) while(src) //Loop on through! - if(result) + if(result > 1) + return hit_thing + else if (result) return (result - 1) if((!( targloc ) || loc == targloc)) targloc = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge @@ -520,4 +524,4 @@ var/obj/item/SP = new shrapnel_type() SP.SetName((name != "shrapnel")? "[name] shrapnel" : "shrapnel") SP.desc += " It looks like it was fired from [shot_from]." - return SP + return SP \ No newline at end of file diff --git a/code/modules/random_map/dungeon/predefined.dm b/code/modules/random_map/dungeon/predefined.dm index 9a8fe52097d..019c0b8f382 100644 --- a/code/modules/random_map/dungeon/predefined.dm +++ b/code/modules/random_map/dungeon/predefined.dm @@ -5,7 +5,9 @@ room_theme_rare = list(/datum/room_theme/metal = 1, /datum/room_theme = 3, /datum/room_theme/metal/secure = 1) monsters_common = list(/mob/living/simple_animal/hostile/carp = 50, /mob/living/simple_animal/hostile/carp/pike = 1) - monsters_uncommon = list(/mob/living/simple_animal/hostile/hivebot = 10, /mob/living/simple_animal/hostile/hivebot/strong = 1) + monsters_uncommon = list(/mob/living/simple_animal/hostile/hivebot = 10, + /mob/living/simple_animal/hostile/hivebot/strong = 1 + ) /datum/random_map/winding_dungeon/premade/New() loot_common += subtypesof(/obj/item/reagent_containers/food) + subtypesof(/obj/item/material) + subtypesof(/obj/item/melee) diff --git a/code/modules/random_map/noise/desert.dm b/code/modules/random_map/noise/desert.dm index 1c7ca229a41..b6b12024abc 100644 --- a/code/modules/random_map/noise/desert.dm +++ b/code/modules/random_map/noise/desert.dm @@ -27,7 +27,7 @@ var/grass_path = pick(typesof(/obj/structure/flora/grass)-/obj/structure/flora/grass) new grass_path(T) if(prob(5)) - var/mob_type = pick(list(/mob/living/simple_animal/lizard, /mob/living/simple_animal/mouse)) + var/mob_type = pick(list(/mob/living/simple_animal/friendly/lizard, /mob/living/simple_animal/friendly/mouse)) new mob_type(T) if(5 to 6) if(prob(20)) diff --git a/code/modules/random_map/noise/tundra.dm b/code/modules/random_map/noise/tundra.dm index 6dfa408d7d5..e011d60ec30 100644 --- a/code/modules/random_map/noise/tundra.dm +++ b/code/modules/random_map/noise/tundra.dm @@ -52,7 +52,7 @@ var/grass_path = pick(typesof(/obj/structure/flora/grass)-/obj/structure/flora/grass) new grass_path(T) if(prob(5)) - var/mob_type = pick(list(/mob/living/simple_animal/lizard, /mob/living/simple_animal/mouse)) + var/mob_type = pick(list(/mob/living/simple_animal/friendly/lizard, /mob/living/simple_animal/friendly/mouse)) new mob_type(T) if(7) if(prob(60)) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 48a15b0d495..f388ee0b561 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -991,13 +991,13 @@ result_amount = 1 required = /obj/item/slime_extract/gold var/list/possible_mobs = list( - /mob/living/simple_animal/cat, - /mob/living/simple_animal/cat/kitten, - /mob/living/simple_animal/corgi, - /mob/living/simple_animal/corgi/puppy, - /mob/living/simple_animal/cow, - /mob/living/simple_animal/chick, - /mob/living/simple_animal/chicken + /mob/living/simple_animal/friendly/cat, + /mob/living/simple_animal/friendly/cat/kitten, + /mob/living/simple_animal/friendly/corgi, + /mob/living/simple_animal/friendly/corgi/puppy, + /mob/living/simple_animal/friendly/cow, + /mob/living/simple_animal/friendly/chick, + /mob/living/simple_animal/friendly/chicken ) /datum/chemical_reaction/slime/crit/on_reaction(var/datum/reagents/holder) diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index fc5f1646026..c09dacdc1dd 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -32,7 +32,7 @@ /obj/item/storage/secure/safe, /obj/structure/iv_drip, /obj/machinery/disposal, - /mob/living/simple_animal/cow, + /mob/living/simple_animal/friendly/cow, /mob/living/simple_animal/hostile/retaliate/goat, /obj/machinery/sleeper, /obj/machinery/smartfridge/, diff --git a/code/modules/spells/aoe_turf/conjure/druidic_spells.dm b/code/modules/spells/aoe_turf/conjure/druidic_spells.dm index 70a37798f3f..e118d71a1f4 100644 --- a/code/modules/spells/aoe_turf/conjure/druidic_spells.dm +++ b/code/modules/spells/aoe_turf/conjure/druidic_spells.dm @@ -43,29 +43,29 @@ return "Your bats are now stronger." -/spell/aoe_turf/conjure/summon/bear - name = "Summon Bear" - desc = "This spell summons a permanent bear companion that will follow your orders." - feedback = "BR" - charge_max = 3000 //5 minutes because this is a REALLY powerful spell. May tone it down/up. - spell_flags = NEEDSCLOTHES - invocation = "REA'YO GOR DAYA!" - invocation_type = SpI_SHOUT - level_max = list(Sp_TOTAL = 4, Sp_SPEED = 0, Sp_POWER = 4) - - range = 0 - - name_summon = 1 - - summon_amt = 1 - summon_type = list(/mob/living/simple_animal/hostile/commanded/bear) - newVars = list("maxHealth" = 15, - "health" = 15, - "melee_damage_lower" = 10, - "melee_damage_upper" = 10, - ) - - hud_state = "wiz_bear" +// /spell/aoe_turf/conjure/summon/bear +// name = "Summon Bear" +// desc = "This spell summons a permanent bear companion that will follow your orders." +// feedback = "BR" +// charge_max = 3000 //5 minutes because this is a REALLY powerful spell. May tone it down/up. +// spell_flags = NEEDSCLOTHES +// invocation = "REA'YO GOR DAYA!" +// invocation_type = SpI_SHOUT +// level_max = list(Sp_TOTAL = 4, Sp_SPEED = 0, Sp_POWER = 4) + +// range = 0 + +// name_summon = 1 + +// summon_amt = 1 +// summon_type = list(/mob/living/simple_animal/hostile/commanded/bear) +// newVars = list("maxHealth" = 15, +// "health" = 15, +// "melee_damage_lower" = 10, +// "melee_damage_upper" = 10, +// ) + +// hud_state = "wiz_bear" /spell/aoe_turf/conjure/summon/bear/before_cast() ..() diff --git a/code/modules/spells/artifacts/spellbound_servants.dm b/code/modules/spells/artifacts/spellbound_servants.dm index fab116dcf5f..e82fa5672b0 100644 --- a/code/modules/spells/artifacts/spellbound_servants.dm +++ b/code/modules/spells/artifacts/spellbound_servants.dm @@ -104,10 +104,10 @@ familiar_type = /mob/living/simple_animal/hostile/carp/pike if("Mouse") H.verbs |= /mob/living/proc/ventcrawl - familiar_type = /mob/living/simple_animal/mouse + familiar_type = /mob/living/simple_animal/friendly/mouse if("Cat") H.mutations |= mRun - familiar_type = /mob/living/simple_animal/cat + familiar_type = /mob/living/simple_animal/friendly/cat if("Bear") var/obj/item/clothing/under/under = locate() in equipment var/obj/item/clothing/head/head = locate() in equipment @@ -115,17 +115,17 @@ var/datum/extension/armor/A = get_extension(under, /datum/extension/armor) if(A) A.armor_values = list( - melee = ARMOR_MELEE_VERY_HIGH, - bullet = ARMOR_BALLISTIC_PISTOL, - laser = ARMOR_LASER_SMALL, + melee = ARMOR_MELEE_VERY_HIGH, + bullet = ARMOR_BALLISTIC_PISTOL, + laser = ARMOR_LASER_SMALL, energy = ARMOR_ENERGY_SMALL ) //More armor A = get_extension(head, /datum/extension/armor) if(A) A.armor_values = list( - melee = ARMOR_MELEE_RESISTANT, - bullet = ARMOR_BALLISTIC_MINOR, - laser = ARMOR_LASER_MINOR, + melee = ARMOR_MELEE_RESISTANT, + bullet = ARMOR_BALLISTIC_MINOR, + laser = ARMOR_LASER_MINOR, energy = ARMOR_ENERGY_MINOR ) familiar_type = /mob/living/simple_animal/hostile/bear diff --git a/code/modules/spells/targeted/shapeshift.dm b/code/modules/spells/targeted/shapeshift.dm index 9c1de7270be..04838593b1f 100644 --- a/code/modules/spells/targeted/shapeshift.dm +++ b/code/modules/spells/targeted/shapeshift.dm @@ -99,7 +99,7 @@ name = "Baleful Polymorth" desc = "This spell transforms its target into a small, furry animal." feedback = "BP" - possible_transformations = list(/mob/living/simple_animal/lizard,/mob/living/simple_animal/mouse,/mob/living/simple_animal/corgi) + possible_transformations = list(/mob/living/simple_animal/friendly/lizard,/mob/living/simple_animal/friendly/mouse,/mob/living/simple_animal/friendly/corgi) share_damage = 0 invocation = "Yo'balada!" diff --git a/code/modules/tension/tension.dm b/code/modules/tension/tension.dm new file mode 100644 index 00000000000..0e89e307164 --- /dev/null +++ b/code/modules/tension/tension.dm @@ -0,0 +1,245 @@ +// This code is used to give a very rough estimate of how screwed an individual player might be at any given moment when fighting monsters. +// You could use this to have an effect trigger when someone is in serious danger, or as a means for an AI to guess which mob needs to die first. +// The idea and the code structure was taken from Dungeon Crawl Stone Soup. + +/atom/movable/proc/get_threat(var/mob/living/threatened) + return 0 + + +/atom/movable/proc/guess_threat_level(var/mob/living/threatened) + return 0 + +/mob/living/simple_animal + var/threat_level = null // Set this if you want an explicit danger rating. + +/mob/living/simple_animal/guess_threat_level(var/mob/living/threatened) + if(threat_level) // If they have a predefined number, use it. + return threat_level + // Otherwise we need to guess how scary this thing is. + var/threat_guess = 0 + + // First lets consider their attack ability. + var/will_point_blank = FALSE + if(has_AI()) + will_point_blank = ai_holder.pointblank + + var/potential_damage = 0 + if(!projectiletype || ( ( get_dist(src, threatened) >= 1) && !will_point_blank ) ) // Melee damage. + potential_damage = (natural_weapon.force) / 2 + + // Treat potential_damage as estimated DPS. If the enemy attacks twice as fast as usual, it will double the number. + potential_damage *= 1 SECOND / (base_attack_cooldown + melee_attack_delay) + else + var/obj/item/projectile/P = new projectiletype(src) + if(P.nodamage) // Tasers are somewhat less scary. + potential_damage = P.agony / 2 + else + potential_damage = P.damage + if(P.damage_type == PAIN) // Not sure if any projectiles do this, but can't be too safe. TODO: i dont think pain is a dmg type + potential_damage /= 2 + // Rubber bullets, I guess. + potential_damage += P.agony / 2 + qdel(P) + + potential_damage *= 1 SECOND / (base_attack_cooldown + ranged_attack_delay) + + // Special attacks are ultra-specific to the mob so a generic threat assessment isn't really possible. + + threat_guess += potential_damage + + // Then consider their defense. + threat_guess += getMaxHealth() / 5 // 100 health translates to 20 threat. + + return threat_guess + +/mob/living/get_threat(var/mob/living/threatened) + if(stat) + return 0 + + +/mob/living/simple_animal/get_threat(var/mob/living/threatened) + . = ..() + + if(has_AI()) + if(!ai_holder.hostile) + return 0 // Can't hurt anyone. + + if(incapacitated(INCAPACITATION_DISABLED)) + return 0 // Can't currently hurt you if it's stunned. + + var/friendly = threatened.faction == faction + + var/threat = guess_threat_level(threatened) + + // Hurt entities contribute less tension. + threat *= health + threat /= getMaxHealth() + + // Allies reduce tension instead of adding. + if(friendly) + threat = -threat + + else + if(threatened.invisibility > see_invisible) + threat /= 2 // Target cannot be seen by src. + if(invisibility > threatened.see_invisible) + threat *= 2 // Target cannot see src. + + // Handle statuses. + if(confused) + threat /= 2 + + // if(has_modifier_of_type(/datum/modifier/berserk)) + // threat *= 2 + + // Handle ability to harm. + // Being five tiles away from some spiders is a lot less scary than being in melee range of five spiders at once. + if(!projectiletype) + threat /= max(get_dist(src, threatened), 1) + + return threat + +// Carbon / mostly Human threat check. +/mob/living/carbon/get_threat(var/mob/living/threatened) + . = ..() + + if(has_AI()) + if(!ai_holder.hostile) + return 0 + + if(incapacitated(INCAPACITATION_DISABLED)) + return 0 + + var/friendly = (IIsAlly(threatened) && a_intent == I_HELP) + + var/threat = guess_threat_level(threatened) + + threat *= health + threat /= getMaxHealth() + + // Allies reduce tension instead of adding. + if(friendly) + threat = -threat + + else + if(threatened.invisibility > see_invisible) + threat /= 2 // Target cannot be seen by src. + if(invisibility > threatened.see_invisible) + threat *= 2 // Target cannot see src. + + // Handle statuses. + if(confused) + threat /= 2 + + // if(has_modifier_of_type(/datum/modifier/berserk)) + // threat *= 2 + + return threat + +/mob/living/carbon/guess_threat_level(var/mob/living/threatened) + var/threat_guess = 0 + + // First lets consider their attack ability. + var/will_point_blank = FALSE + if(has_AI()) + will_point_blank = ai_holder.pointblank + + . = ..() + + var/obj/item/I = get_active_hand() + if(!I || !istype(I)) + var/damage_guess = 0 + if(ishuman(src) && ishuman(threatened)) + var/mob/living/carbon/human/H = src + var/datum/unarmed_attack/attack = H.get_unarmed_attack(threatened, BP_CHEST) + if(!attack) + damage_guess += 5 + + var/punch_damage = attack.get_unarmed_damage(H) + if(H.gloves) + if(istype(H.gloves, /obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = H.gloves + punch_damage += G.force + + damage_guess += punch_damage + + else + damage_guess += 5 + + threat_guess += damage_guess + + else + var/weapon_attack_speed = DEFAULT_ATTACK_COOLDOWN / (1 SECOND) + var/weapon_damage = I.force + + if(istype(I, /obj/item/gun)) + will_point_blank = TRUE + var/obj/item/gun/projectile/G = I + + if (istype(G)) + var/obj/item/ammo_casing/A = G.ammo_type + var/obj/item/projectile/P = new A.projectile_type + + if(P) // Does the gun even have a projectile type? + weapon_damage = P.damage + if(will_point_blank && a_intent == I_HURT) + weapon_damage *= 1.5 + weapon_attack_speed = G.fire_delay / (1 SECOND) + qdel(P) + + var/average_damage = weapon_damage / weapon_attack_speed + + threat_guess += average_damage + + // Consider intent. + switch(a_intent) + if(I_HELP) // Not likely to fight us. + threat_guess *= 0.4 + if(I_DISARM) // Might engage us, but unlikely to be with the intent to kill. + threat_guess *= 0.8 + if(I_GRAB) // May try to restrain us. This is here for reference, or later tweaking if needed. + threat_guess *= 1 + if(I_HURT) // May try to hurt us. + threat_guess *= 1.25 + + // Then consider their defense. + threat_guess += getMaxHealth() / 5 // 100 health translates to 20 threat. + + return threat_guess + +// Gives a rough idea of how much danger someone is in. Meant to be used for PvE things since PvP has too many unknown variables. +/mob/living/proc/get_tension() + var/tension = 0 + var/list/potential_threats = list() + + // First, get everything threatening to us. + for(var/thing in view(src)) + if(isliving(thing)) + potential_threats += thing + if(istype(thing, /obj/machinery/porta_turret)) + potential_threats += thing + + var/danger = FALSE + // Now to get all the threats. + for(var/atom/movable/AM in potential_threats) + var/tension_from_AM = AM.get_threat(src) + tension += tension_from_AM + if(tension_from_AM > 0) + danger = TRUE + + if(!danger) + return 0 + + // Tension is roughly doubled when about to fall into crit. + var/max_health = getMaxHealth() + tension *= 2 * max_health / (health + max_health) + + // Being unable to act is really tense. + if(incapacitated(INCAPACITATION_DISABLED) && !lying) + tension *= 10 + return tension + + if(confused) + tension *= 2 + + return tension diff --git a/code/modules/xenoarcheaology/artifacts/autocloner.dm b/code/modules/xenoarcheaology/artifacts/autocloner.dm index dcb44be3fdf..56f9410257c 100644 --- a/code/modules/xenoarcheaology/artifacts/autocloner.dm +++ b/code/modules/xenoarcheaology/artifacts/autocloner.dm @@ -26,15 +26,15 @@ /mob/living/simple_animal/hostile/creature) else spawn_type = pick(\ - /mob/living/simple_animal/cat, - /mob/living/simple_animal/corgi, - /mob/living/simple_animal/corgi/puppy, - /mob/living/simple_animal/chicken, - /mob/living/simple_animal/cow, + /mob/living/simple_animal/friendly/cat, + /mob/living/simple_animal/friendly/corgi, + /mob/living/simple_animal/friendly/corgi/puppy, + /mob/living/simple_animal/friendly/chicken, + /mob/living/simple_animal/friendly/cow, /mob/living/simple_animal/hostile/retaliate/parrot, /mob/living/simple_animal/slime, /mob/living/simple_animal/crab, - /mob/living/simple_animal/mouse, + /mob/living/simple_animal/friendly/mouse, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/hostile/retaliate/goose) diff --git a/icons/misc/buildmode.dmi b/icons/misc/buildmode.dmi index 8608310384dac0b3128c6947375cf49acea17c98..b360388784699cd2e391f4f5f7bd7b4bd9a90745 100644 GIT binary patch literal 2786 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 literal 2349 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 literal 0 HcmV?d00001 diff --git a/maps/away/ascent_caulship/ascent_props.dm b/maps/away/ascent_caulship/ascent_props.dm index 7d6e71d054b..60d8e2e9546 100644 --- a/maps/away/ascent_caulship/ascent_props.dm +++ b/maps/away/ascent_caulship/ascent_props.dm @@ -48,15 +48,17 @@ natural_weapon = /obj/item/natural_weapon/bite faction = "kharmaani" var/global/list/friendly_species = list(SPECIES_MANTID_ALATE, SPECIES_MANTID_GYNE, SPECIES_MONARCH_QUEEN, SPECIES_MONARCH_WORKER) + ai_holder_type = /datum/ai_holder/simple_animal/retaliate/alate_nymph -/mob/living/simple_animal/hostile/retaliate/alate_nymph/ListTargets(var/dist = 7) +/datum/ai_holder/simple_animal/retaliate/alate_nymph/list_targets() . = list() - for(var/mob/living/M in hearers(src, dist)) - if(M.faction == faction) + var/mob/living/simple_animal/hostile/retaliate/alate_nymph/A = holder + for(var/mob/living/M in hearers(src, vision_range)) + if(M.faction == A.faction) continue if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M - if(H.species.get_bodytype() in friendly_species) + if(H.species.get_bodytype() in A.friendly_species) continue . += M diff --git a/maps/away/blueriver/blueriver.dm b/maps/away/blueriver/blueriver.dm index a2c12ea3fc6..a76214db0fb 100644 --- a/maps/away/blueriver/blueriver.dm +++ b/maps/away/blueriver/blueriver.dm @@ -47,11 +47,37 @@ harm_intent_damage = 8 natural_weapon = /obj/item/natural_weapon/defender_blades + ai_holder_type = /datum/ai_holder/simple_animal/melee/defender var/attack_mode = FALSE var/transformation_delay_min = 4 var/transformation_delay_max = 8 +/datum/ai_holder/simple_animal/melee/defender/lose_target() + . = ..() + var/mob/living/simple_animal/hostile/hive_alien/defender/D = holder + if(D.attack_mode && !find_target()) //If we don't immediately find another target, switch to movement mode + D.mode_movement() + + return ..() + +/datum/ai_holder/simple_animal/melee/defender/lose_target() + . = ..() + var/mob/living/simple_animal/hostile/hive_alien/defender/D = holder + if(D.attack_mode && !find_target()) //If we don't immediately find another target, switch to movement mode + D.mode_movement() + + return ..() + +/datum/ai_holder/simple_animal/melee/defender/engage_target() + . = ..() + var/mob/living/simple_animal/hostile/hive_alien/defender/D = holder + if(!D.attack_mode) + return D.mode_attack() + + flick("hive_executioner_attacking", src) + + return ..() /obj/item/natural_weapon/defender_blades name = "blades" attack_verb = list("eviscerated") @@ -67,10 +93,10 @@ anchored = FALSE speed = -1 move_to_delay = 8 - attack_mode = FALSE + . = FALSE //Immediately find a target so that we're not useless for 1 Life() tick! - FindTarget() + ai_holder.find_target() /mob/living/simple_animal/hostile/hive_alien/defender/proc/mode_attack() set waitfor = 0 @@ -82,26 +108,6 @@ attack_mode = TRUE walk(src, 0) -/mob/living/simple_animal/hostile/hive_alien/defender/LostTarget() - if(attack_mode && !FindTarget()) //If we don't immediately find another target, switch to movement mode - mode_movement() - - return ..() - -/mob/living/simple_animal/hostile/hive_alien/defender/LoseTarget() - if(attack_mode && !FindTarget()) //If we don't immediately find another target, switch to movement mode - mode_movement() - - return ..() - -/mob/living/simple_animal/hostile/hive_alien/defender/AttackingTarget() - if(!attack_mode) - return mode_attack() - - flick("hive_executioner_attacking", src) - - return ..() - /mob/living/simple_animal/hostile/hive_alien/defender/wounded name = "wounded hive defender" health = 80 diff --git a/maps/away/errant_pisces/errant_pisces.dm b/maps/away/errant_pisces/errant_pisces.dm index 23646a02807..50b6f430c5a 100644 --- a/maps/away/errant_pisces/errant_pisces.dm +++ b/maps/away/errant_pisces/errant_pisces.dm @@ -34,6 +34,27 @@ break_stuff_probability = 35 faction = "shark" + ai_holder_type = /datum/ai_holder/simple_animal/melee/carp/shark + +/datum/ai_holder/simple_animal/melee/carp/shark/engage_target() + set waitfor = 0//to deal with sleep() possibly stalling other procs + . = ..() + var/mob/living/simple_animal/hostile/carp/shark/S = holder + var/mob/living/L = . + if(istype(L)) + if(prob(25))//if one is unlucky enough, they get tackled few tiles away + L.visible_message("\The [src] tackles [L]!") + var/tackle_length = rand(3,5) + for (var/i = 1 to tackle_length) + var/turf/T = get_step(L.loc, S.dir)//on a first step of tackling standing mob would block movement so let's check if there's something behind it. Works for consequent moves too + if (T.density || LinkBlocked(L.loc, T) || TurfBlockedNonWindow(T) || DirBlocked(T, GLOB.flip_dir[S.dir])) + break + sleep(2) + S.forceMove(T)//maybe there's better manner then just forceMove() them + L.forceMove(T) + S.visible_message("\The [S] releases [L].") + + /mob/living/simple_animal/hostile/carp/shark/carp_randomify() return @@ -49,23 +70,6 @@ environment.merge(sharkmaw_phoron) visible_message("\The [src]'s body releases some gas from the gills with a quiet fizz!") -/mob/living/simple_animal/hostile/carp/shark/AttackingTarget() - set waitfor = 0//to deal with sleep() possibly stalling other procs - . =..() - var/mob/living/L = . - if(istype(L)) - if(prob(25))//if one is unlucky enough, they get tackled few tiles away - L.visible_message("\The [src] tackles [L]!") - var/tackle_length = rand(3,5) - for (var/i = 1 to tackle_length) - var/turf/T = get_step(L.loc, dir)//on a first step of tackling standing mob would block movement so let's check if there's something behind it. Works for consequent moves too - if (T.density || LinkBlocked(L.loc, T) || TurfBlockedNonWindow(T) || DirBlocked(T, GLOB.flip_dir[dir])) - break - sleep(2) - forceMove(T)//maybe there's better manner then just forceMove() them - L.forceMove(T) - visible_message("\The [src] releases [L].") - /obj/item/reagent_containers/food/snacks/sharkmeat name = "cosmoshark fillet" desc = "A fillet of cosmoshark meat." diff --git a/maps/away/lar_maria/lar_maria.dm b/maps/away/lar_maria/lar_maria.dm index d4c875d9090..de0b1c22ac8 100644 --- a/maps/away/lar_maria/lar_maria.dm +++ b/maps/away/lar_maria/lar_maria.dm @@ -29,21 +29,19 @@ environment_smash = 1 faction = "lar_maria" status_flags = CANPUSH - speak = list("Die!", "Fresh meat!", "Hurr!", "You said help will come!", "I did nothing!", "Eat my fist!", "One for the road!") - speak_chance = 50 - emote_hear = list("roars", "giggles", "breathes loudly", "mumbles", "yells something unintelligible") - emote_see = list("cries", "grins insanely", "itches fiercly", "scratches his face", "shakes his fists above his head") turns_per_move = 5 response_help = "pokes" response_disarm = "shoves" response_harm = "hits" speed = 8 can_escape = TRUE - stop_automated_movement_when_pulled = 0 natural_weapon = /obj/item/natural_weapon/punch var/obj/effect/landmark/corpse/lar_maria/corpse = null var/weapon = null + ai_holder_type = /datum/ai_holder/simple_animal/lar_maria + say_list_type = /datum/say_list/lar_maria + /mob/living/simple_animal/hostile/lar_maria/death(gibbed, deathmessage, show_dead_message) ..(gibbed, deathmessage, show_dead_message) if(corpse) @@ -228,3 +226,11 @@ /obj/item/paper/lar_maria/note_9 name = "paper note" info = "can we get some fresh carp sometime? Or freshish? Or frozen? I just really want carp, ok? I'm willing to pay for it if so." + +/datum/ai_holder/simple_animal/lar_maria + speak_chance = 50 + +/datum/say_list/lar_maria + speak = list("Die!", "Fresh meat!", "Hurr!", "You said help will come!", "I did nothing!", "Eat my fist!", "One for the road!") + emote_see = list("cries", "grins insanely", "itches fiercly", "scratches his face", "shakes his fists above his head") + emote_hear = list("roars", "giggles", "breathes loudly", "mumbles", "yells something unintelligible") \ No newline at end of file diff --git a/maps/away/meatstation/meatstation.dm b/maps/away/meatstation/meatstation.dm index 7d871596c82..b5490db3b1c 100644 --- a/maps/away/meatstation/meatstation.dm +++ b/maps/away/meatstation/meatstation.dm @@ -66,7 +66,6 @@ response_harm = "stomps" flash_vulnerability = 0 //eyeless turns_per_move = 5 - wander = 1 natural_weapon = /obj/item/natural_weapon/bite/weak faction = "meat" min_gas = null @@ -75,39 +74,40 @@ meat_amount = 1 can_escape = TRUE + ai_holder_type = /datum/ai_holder/simple_animal/melee/meatstation + /mob/living/simple_animal/hostile/meatstation/meatworm name = "flesh worm" desc = "A toothy little thing. It won't stop gnashing and thrashing!" icon_state = "meatworm" icon_living = "meatworm" icon_dead = "meatworm_dead" - speak_chance = 2 turns_per_move = 3 speed = -2 maxHealth = 20 health = 20 natural_weapon = /obj/item/natural_weapon/bite/weak - emote_see = list("gnashes", "thrashes about", "chomps the air") mob_size = MOB_SMALL meat_amount = 2 can_escape = FALSE + say_list_type = /datum/say_list/meatstation/meatworm + /mob/living/simple_animal/hostile/meatstation/meatball name = "animated meat" desc = "A crude creature of meat and teeth." icon_state = "meatball" icon_living = "meatball" icon_dead = "meatball_dead" - speak_chance = 2 speed = 2 maxHealth = 50 health = 50 natural_weapon = /obj/item/natural_weapon/meatball - emote_hear = list("cackles","gibbers") - emote_see = list("gnashes", "writhes", "gurgles","clicks its teeth") meat_amount = 2 can_escape = FALSE + say_list_type = /datum/say_list/meatstation/meatball + /obj/item/natural_weapon/meatball force = 12 attack_verb = list("thwacked") @@ -118,15 +118,14 @@ icon_state = "wormscientist" icon_living = "wormscientist" icon_dead = "wormscientist_dead" - speak_chance = 2 speed = 7 maxHealth = 90 health = 90 natural_weapon = /obj/item/natural_weapon/wormscience - emote_hear = list("gurgles","moans") - emote_see = list("wobbles", "writhes", "twitches","shudders", "trembles") meat_amount = 3 + say_list_type = /datum/say_list/meatstation/meat_human + /obj/item/natural_weapon/wormscience force = 15 attack_verb = list("whacked") @@ -137,18 +136,16 @@ icon_state = "wormguard" icon_living = "wormguard" icon_dead = "wormguard_dead" - speak_chance = 2 speed = 7 maxHealth = 60 health = 60 natural_weapon = /obj/item/natural_weapon/wormguard - emote_hear = list("gurgles","moans") - emote_see = list("wobbles", "writhes", "twitches","shudders", "trembles") meat_amount = 3 projectilesound = 'sound/weapons/laser.ogg' ranged = 1 projectiletype = /obj/item/projectile/beam/meatstation + say_list_type = /datum/say_list/meatstation/meat_human /obj/item/natural_weapon/wormguard force = 17 attack_verb = list("slammed") @@ -160,16 +157,15 @@ icon_living = "meatmound" icon_dead = "meatmound_dead" flash_vulnerability = 1 - speak_chance = 2 speed = 10 maxHealth = 160 health = 160 natural_weapon = /obj/item/natural_weapon/meatmound - emote_hear = list("roars","moans","growls") - emote_see = list("gnashes", "undulates", "gurgles","chomps") meat_amount = 4 mob_size = MOB_LARGE + say_list_type = /datum/say_list/meatstation/meatmound + /obj/item/natural_weapon/meatmound force = 25 sharp = TRUE @@ -311,4 +307,21 @@ obj/item/paper/meatstation/weapon_note//and this one's here to give players some /obj/random/single/meatstation/low/wormscientist icon_state = "wormscientist10" - spawn_object = /mob/living/simple_animal/hostile/meatstation/wormscientist \ No newline at end of file + spawn_object = /mob/living/simple_animal/hostile/meatstation/wormscientist + +/datum/ai_holder/simple_animal/melee/meatstation + +/datum/say_list/meatstation/meatworm + emote_see = list("gnashes", "thrashes about", "chomps the air") + +/datum/say_list/meatstation/meatball + emote_hear = list("cackles","gibbers") + emote_see = list("gnashes", "writhes", "gurgles","clicks its teeth") + +/datum/say_list/meatstation/meat_human + emote_hear = list("gurgles","moans") + emote_see = list("wobbles", "writhes", "twitches","shudders", "trembles") + +/datum/say_list/meatstation/meatmound + emote_hear = list("roars","moans","growls") + emote_see = list("gnashes", "undulates", "gurgles","chomps") \ No newline at end of file diff --git a/maps/away/scavver/scavver_gantry-2.dmm b/maps/away/scavver/scavver_gantry-2.dmm index 7bd57fcfde0..c82d692d499 100644 --- a/maps/away/scavver/scavver_gantry-2.dmm +++ b/maps/away/scavver/scavver_gantry-2.dmm @@ -3026,7 +3026,7 @@ /obj/item/storage/box/donkpockets, /obj/item/material/kitchen/utensil/spork, /obj/random/loot, -/mob/living/simple_animal/mouse/brown/Tom, +/mob/living/simple_animal/friendly/mouse/brown/Tom, /turf/simulated/floor/wood/yew, /area/scavver/hab) "wQ" = ( diff --git a/maps/away/slavers/slavers_base.dm b/maps/away/slavers/slavers_base.dm index ebcf3a9ee26..367af8b6e12 100644 --- a/maps/away/slavers/slavers_base.dm +++ b/maps/away/slavers/slavers_base.dm @@ -139,13 +139,11 @@ icon_state = "extremist" icon_living = "extremist" icon_dead = "extremist_dead" - speak_chance = 0 turns_per_move = 5 response_help = "pushes" response_disarm = "shoves" response_harm = "hits" speed = 4 - stop_automated_movement_when_pulled = 0 maxHealth = 100 health = 100 natural_weapon = /obj/item/natural_weapon/punch @@ -158,6 +156,8 @@ projectiletype = /obj/item/projectile/beam faction = "extremist abolitionists" + ai_holder_type = /datum/ai_holder/simple_animal/ranged + /mob/living/simple_animal/hostile/abolition_extremist/death(gibbed, deathmessage, show_dead_message) . = ..(gibbed, deathmessage, show_dead_message) if(corpse) diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm index c6b61c0fcb0..7f007872e6d 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm @@ -545,7 +545,7 @@ dir = 1; icon_state = "railing0-1" }, -/mob/living/simple_animal/mouse/brown/Tom, +/mob/living/simple_animal/friendly/mouse/brown/Tom, /turf/simulated/floor/tiled/techfloor/grid, /area/map_template/crashed_pod) "fn" = ( diff --git a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm index f89f9dc0cbb..def08a5a2fb 100644 --- a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm +++ b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm @@ -97,8 +97,6 @@ name = "Farmbot" desc = "The botanist's best friend. There's something slightly odd about the way it moves." icon = 'maps/random_ruins/exoplanet_ruins/hydrobase/hydro.dmi' - speak = list("Initiating harvesting subrout-ine-ine.", "Connection timed out.", "Connection with master AI syst-tem-tem lost.", "Core systems override enab-...") - emote_see = list("beeps repeatedly", "whirrs violently", "flashes its indicator lights", "emits a ping sound") icon_state = "farmbot" icon_living = "farmbot" icon_dead = "farmbot_dead" @@ -107,6 +105,10 @@ maxHealth = 225 malfunctioning = 0 + ai_holder_type = /datum/ai_holder/simple_animal/passive + + say_list_type = /datum/say_list/malf_drone/hydro + /mob/living/simple_animal/hostile/retaliate/malf_drone/hydro/Initialize() . = ..() if(prob(15)) @@ -120,3 +122,7 @@ destroy_surroundings = 1 projectiletype = initial(projectiletype) walk(src,0) + +/datum/say_list/malf_drone/hydro + speak = list("Initiating harvesting subrout-ine-ine.", "Connection timed out.", "Connection with master AI syst-tem-tem lost.", "Core systems override enab-...") + emote_see = list("beeps repeatedly", "whirrs violently", "flashes its indicator lights", "emits a ping sound") \ No newline at end of file diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index aa137c35d0a..81e748044f0 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -6393,7 +6393,7 @@ dir = 8 }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/mob/living/simple_animal/cow, +/mob/living/simple_animal/friendly/cow, /turf/simulated/floor/grass, /area/map_template/colony/hydroponics) "lf" = ( @@ -6493,7 +6493,7 @@ /turf/simulated/floor/exoplanet/concrete, /area/map_template/colony/mineralprocessing) "lo" = ( -/mob/living/simple_animal/cow, +/mob/living/simple_animal/friendly/cow, /turf/simulated/floor/grass, /area/map_template/colony/hydroponics) "lp" = ( @@ -7685,7 +7685,7 @@ /obj/machinery/light{ dir = 8 }, -/mob/living/simple_animal/chicken, +/mob/living/simple_animal/friendly/chicken, /turf/simulated/floor/grass, /area/map_template/colony/hydroponics) "nm" = ( @@ -8708,7 +8708,7 @@ /turf/simulated/floor/exoplanet/concrete, /area/template_noop) "vt" = ( -/mob/living/simple_animal/chicken, +/mob/living/simple_animal/friendly/chicken, /turf/simulated/floor/grass, /area/map_template/colony/hydroponics) "vJ" = ( diff --git a/maps/random_ruins/space_ruins/multi_zas_test.dmm b/maps/random_ruins/space_ruins/multi_zas_test.dmm index 7a0282fb1b1..575e9d08969 100644 --- a/maps/random_ruins/space_ruins/multi_zas_test.dmm +++ b/maps/random_ruins/space_ruins/multi_zas_test.dmm @@ -6,7 +6,7 @@ /turf/simulated/floor, /area/template_noop) "c" = ( -/mob/living/simple_animal/corgi, +/mob/living/simple_animal/friendly/corgi, /turf/simulated/floor, /area/template_noop) "d" = ( diff --git a/maps/torch/torch6_bridge.dmm b/maps/torch/torch6_bridge.dmm index 6ec8dacf569..4e901ea5df3 100644 --- a/maps/torch/torch6_bridge.dmm +++ b/maps/torch/torch6_bridge.dmm @@ -16333,7 +16333,7 @@ dir = 9 }, /obj/random/plushie, -/mob/living/simple_animal/corgi/Ian, +/mob/living/simple_animal/friendly/corgi/Ian, /turf/simulated/floor/tiled/dark, /area/crew_quarters/heads/office/xo) "ZX" = ( diff --git a/maps/torch/torch_npcs.dm b/maps/torch/torch_npcs.dm index 78e41b68046..ebe0a6fcb70 100644 --- a/maps/torch/torch_npcs.dm +++ b/maps/torch/torch_npcs.dm @@ -19,9 +19,9 @@ /obj/random_multi/single_item/runtime name = "Multi Point - Runtime" id = "Runtime" - item_path = /mob/living/simple_animal/cat/fluff/Runtime + item_path = /mob/living/simple_animal/friendly/cat/fluff/Runtime /obj/random_multi/single_item/poppy name = "Multi Point - Poppy" id = "Poppy" - item_path = /mob/living/simple_animal/opossum/poppy \ No newline at end of file + item_path = /mob/living/simple_animal/friendly/opossum/poppy \ No newline at end of file diff --git a/test/check-paths.sh b/test/check-paths.sh index d2e779e62db..e10fbc66d55 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -34,13 +34,13 @@ exactly 117 "to_world uses" '\sto_world\(' exactly 65 "to_world_log uses" '\sto_world_log\(' exactly 0 "world<< uses" 'world<<|world[[:space:]]<<' exactly 0 "world.log<< uses" 'world.log<<|world.log[[:space:]]<<' -exactly 120 "<< uses" '(?&1 | tee log.txt" From 8879f630204aa4378b91b7a19321da7d53b38d9d Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 24 May 2021 01:21:08 +0000 Subject: [PATCH 023/246] Automatic changelog generation --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-30714.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30714.yml diff --git a/html/changelog.html b/html/changelog.html index dcf3a0dd0c9..2c5f062997d 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,12 @@ -->
    +

    24 May 2021

    +

    The-Spanish-Inquisition updated:

    +
      +
    • Added two new projectile firearms; Added one ammo speed loader to support new firearm.
    • +
    +

    23 May 2021

    BlueNexus updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 8fd3562d458..3b88e8f6fd1 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16873,3 +16873,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: Engineer Trainees now get access to material goggles SierraKomodo: - rscdel: The Executive Assistant/Asset Protection Agent role has been removed. +2021-05-24: + The-Spanish-Inquisition: + - rscadd: Added two new projectile firearms; Added one ammo speed loader to support + new firearm. diff --git a/html/changelogs/AutoChangeLog-pr-30714.yml b/html/changelogs/AutoChangeLog-pr-30714.yml deleted file mode 100644 index c53ebda2082..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30714.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: The-Spanish-Inquisition -delete-after: true -changes: - - rscadd: Added two new projectile firearms; Added one ammo speed loader to support - new firearm. From 3c884fd1b246a9db86eb827014301f3c4a7848e6 Mon Sep 17 00:00:00 2001 From: Cody Brittain Date: Wed, 19 May 2021 18:31:57 -0400 Subject: [PATCH 024/246] Reverts "Changes Pharmacist to Lab Tech" --- maps/torch/icons/assignment_hud.dmi | Bin 2841 -> 2840 bytes maps/torch/job/medical_jobs.dm | 15 +++++++++------ maps/torch/job/outfits/medical_outfits.dm | 8 ++++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/maps/torch/icons/assignment_hud.dmi b/maps/torch/icons/assignment_hud.dmi index a6b325ca917d75c548ef2449db6018e5470651b2..19627f5ecf4d0c10e2656aabd1da0ab2fa9b75ed 100644 GIT binary patch delta 232 zcmV0paHG1q2K|3lGPe~10qdlbo9`-{ZT;%MD&pM&IUBH&UllDwmt+g z_~?NNOoec~?#N5ItA4HK%Y1s1Ef=!k7VDCv1jhT8(^W9@_d4 z#NeX`rZN@6LAxU_WwH9TnlJO|O}1Rf#$2pRl2RG(TTY|A`3#jfAd_pA+~(QM>7}sG zBCz-|lfXAYf-if;*mFZnhKw({1;oaMmPLP1`PR3q)m+4-0`*p0@Eg3dRs+HZ)Twi( diff --git a/maps/torch/job/medical_jobs.dm b/maps/torch/job/medical_jobs.dm index 803418ae935..ffb5750ba85 100644 --- a/maps/torch/job/medical_jobs.dm +++ b/maps/torch/job/medical_jobs.dm @@ -53,7 +53,7 @@ ideal_character_age = 45 total_positions = 1 spawn_positions = 1 - supervisors = "Physicians and the Chief Medical Officer" + supervisors = "physicians and the Chief Medical Officer" selection_color = "#013d3b" economic_power = 6 outfit_type = /decl/hierarchy/outfit/job/torch/crew/medical/senior @@ -91,7 +91,7 @@ title = "Medical Technician" total_positions = 3 spawn_positions = 3 - supervisors = "Physicians and the Chief Medical Officer" + supervisors = "physicians and the Chief Medical Officer" economic_power = 7 minimum_character_age = list(SPECIES_HUMAN = 19) ideal_character_age = 40 @@ -140,7 +140,7 @@ department_flag = MED total_positions = 1 spawn_positions = 1 - supervisors = "Medical personnel, and the Chief Medical Officer" + supervisors = "medical personnel and the Chief Medical Officer" selection_color = "#013d3b" minimum_character_age = list(SPECIES_HUMAN = 18) ideal_character_age = 20 @@ -183,17 +183,20 @@ return "You are a Trainee Medical Technician. You are learning how to treat and recover wounded crew from the more experienced medical personnel aboard. You are subordinate to the rest of the medical team." /datum/job/chemist - title = "Laboratory Technician" + title = "Pharmacist" department = "Medical" department_flag = MED total_positions = 1 spawn_positions = 1 - supervisors = "the Chief Medical Officer, the Corporate Liaison and Medical Personnel" + supervisors = "medical personnel, the Corporate Liaison, and the Chief Medical Officer" selection_color = "#013d3b" economic_power = 4 minimum_character_age = list(SPECIES_HUMAN = 25) ideal_character_age = 30 minimal_player_age = 7 + alt_titles = list( + "Chemist" + ) outfit_type = /decl/hierarchy/outfit/job/torch/crew/medical/contractor/chemist allowed_branches = list(/datum/mil_branch/civilian) allowed_ranks = list(/datum/mil_rank/civ/contractor) @@ -214,7 +217,7 @@ minimal_access = list() /datum/job/chemist/get_description_blurb() - return "You are a Laboratory Technician. You make medicine. You are not a doctor or medic, but have surface level knowledge in those fields. You should not be treating patients, but rather providing the the medicine to do so. You are subordinate to Physicians and Medical Techncians." + return "You are the Pharmacist. You make medicine and other useful substances. You are not a doctor or medic; you should not be treating patients, but rather providing the medicine to do so. You are subordinate to Physicians and Medical Technicians." /datum/job/psychiatrist title = "Counselor" diff --git a/maps/torch/job/outfits/medical_outfits.dm b/maps/torch/job/outfits/medical_outfits.dm index 0b904857aff..f2223b09f24 100644 --- a/maps/torch/job/outfits/medical_outfits.dm +++ b/maps/torch/job/outfits/medical_outfits.dm @@ -70,11 +70,15 @@ /decl/hierarchy/outfit/job/torch/crew/medical/contractor/chemist name = OUTFIT_JOB_NAME("Chemist - Torch") - uniform = /obj/item/clothing/under/rank/medical + uniform = /obj/item/clothing/under/rank/chemist shoes = /obj/item/clothing/shoes/white - pda_type = /obj/item/modular_computer/pda/medical + pda_type = /obj/item/modular_computer/pda/chemistry id_types = list(/obj/item/card/id/torch/contractor/chemist) +/decl/hierarchy/outfit/job/torch/crew/medical/contractor/chemist/New() + ..() + BACKPACK_OVERRIDE_CHEMISTRY + /decl/hierarchy/outfit/job/torch/crew/medical/counselor name = OUTFIT_JOB_NAME("Counselor") uniform = /obj/item/clothing/under/rank/psych/turtleneck From 3cacb76eefd2fafbaa4012c342c20432ab1a04dc Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 24 May 2021 18:36:08 +0200 Subject: [PATCH 025/246] Automatic changelog generation for PR #30727 [ci skip] --- html/changelogs/AutoChangeLog-pr-30727.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30727.yml diff --git a/html/changelogs/AutoChangeLog-pr-30727.yml b/html/changelogs/AutoChangeLog-pr-30727.yml new file mode 100644 index 00000000000..11532bef6e4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30727.yml @@ -0,0 +1,5 @@ +author: Jux +delete-after: true +changes: + - tweak: Fleet CAs and CNs now don't get the dress beret, and have to wear the full + sailor getup. From 32c574624c1132a52de686c194133230d27abd81 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Tue, 25 May 2021 01:17:36 +0000 Subject: [PATCH 026/246] Automatic changelog generation --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-30727.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30727.yml diff --git a/html/changelog.html b/html/changelog.html index 2c5f062997d..4bbd7ad9644 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,12 @@ -->
      +

      25 May 2021

      +

      Jux updated:

      +
        +
      • Fleet CAs and CNs now don't get the dress beret, and have to wear the full sailor getup.
      • +
      +

      24 May 2021

      The-Spanish-Inquisition updated:

        diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 3b88e8f6fd1..33843be3c9d 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16877,3 +16877,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. The-Spanish-Inquisition: - rscadd: Added two new projectile firearms; Added one ammo speed loader to support new firearm. +2021-05-25: + Jux: + - tweak: Fleet CAs and CNs now don't get the dress beret, and have to wear the full + sailor getup. diff --git a/html/changelogs/AutoChangeLog-pr-30727.yml b/html/changelogs/AutoChangeLog-pr-30727.yml deleted file mode 100644 index 11532bef6e4..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30727.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Jux -delete-after: true -changes: - - tweak: Fleet CAs and CNs now don't get the dress beret, and have to wear the full - sailor getup. From 2477015e4ecbf02961e1bfdf0fe6c44572bb7b40 Mon Sep 17 00:00:00 2001 From: Hubblenaut Date: Tue, 25 May 2021 20:06:16 +0200 Subject: [PATCH 027/246] Fixes armor penetration for melee weapons --- code/modules/mob/living/carbon/carbon_defense.dm | 2 +- code/modules/mob/living/living_defense.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 5e4ccc76dc1..7c9cd0a684f 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -5,7 +5,7 @@ //Apply weapon damage var/damage_flags = I.damage_flags() - var/datum/wound/created_wound = apply_damage(effective_force, I.damtype, hit_zone, damage_flags, used_weapon=I) + var/datum/wound/created_wound = apply_damage(effective_force, I.damtype, hit_zone, damage_flags, used_weapon=I, armor_pen=I.armor_penetration) //Melee weapon embedded object code. if(istype(created_wound) && I && I.can_embed() && I.damtype == BRUTE && !I.anchored && !is_robot_module(I)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 0f857394333..2f65c3d1fa1 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -121,7 +121,7 @@ //Apply weapon damage var/damage_flags = I.damage_flags() - return apply_damage(effective_force, I.damtype, hit_zone, damage_flags, used_weapon=I) + return apply_damage(effective_force, I.damtype, hit_zone, damage_flags, used_weapon=I, armor_pen=I.armor_penetration) //this proc handles being hit by a thrown atom /mob/living/hitby(var/atom/movable/AM, var/datum/thrownthing/TT) From 68415f8bc7d15312ee74698409265d41b0fc14d3 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Wed, 26 May 2021 01:10:54 +0200 Subject: [PATCH 028/246] Automatic changelog generation for PR #30740 [ci skip] --- html/changelogs/AutoChangeLog-pr-30740.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30740.yml diff --git a/html/changelogs/AutoChangeLog-pr-30740.yml b/html/changelogs/AutoChangeLog-pr-30740.yml new file mode 100644 index 00000000000..7de9fd42b90 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30740.yml @@ -0,0 +1,5 @@ +author: Hubblenaut +delete-after: true +changes: + - bugfix: Fixes melee weapons like the energy sword effectively not having armor + penetration. From 7953fdb17043400c00c5d1eb7cdc401eaea18928 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Wed, 26 May 2021 01:36:11 +0000 Subject: [PATCH 029/246] Automatic changelog generation --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-30740.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30740.yml diff --git a/html/changelog.html b/html/changelog.html index 4bbd7ad9644..c22735bc881 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,12 @@ -->
        +

        26 May 2021

        +

        Hubblenaut updated:

        +
          +
        • Fixes melee weapons like the energy sword effectively not having armor penetration.
        • +
        +

        25 May 2021

        Jux updated:

          diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 33843be3c9d..3b7ded54c18 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16881,3 +16881,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Jux: - tweak: Fleet CAs and CNs now don't get the dress beret, and have to wear the full sailor getup. +2021-05-26: + Hubblenaut: + - bugfix: Fixes melee weapons like the energy sword effectively not having armor + penetration. diff --git a/html/changelogs/AutoChangeLog-pr-30740.yml b/html/changelogs/AutoChangeLog-pr-30740.yml deleted file mode 100644 index 7de9fd42b90..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30740.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Hubblenaut -delete-after: true -changes: - - bugfix: Fixes melee weapons like the energy sword effectively not having armor - penetration. From c74b27c007b121aacc393f30a5f628e03ded79a9 Mon Sep 17 00:00:00 2001 From: Jocelynn Date: Tue, 25 May 2021 22:13:36 -0400 Subject: [PATCH 030/246] Moves the fleet wheel cover to SNCOs and up --- maps/torch/datums/uniforms.dm | 2 +- maps/torch/datums/uniforms_fleet.dm | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/maps/torch/datums/uniforms.dm b/maps/torch/datums/uniforms.dm index a5817d0db96..21f108bf574 100644 --- a/maps/torch/datums/uniforms.dm +++ b/maps/torch/datums/uniforms.dm @@ -78,7 +78,7 @@ dress_skirt = /obj/item/clothing/under/solgov/service/fleet/skirt dress_over = /obj/item/clothing/suit/dress/solgov/fleet/sailor dress_shoes = /obj/item/clothing/shoes/dress - dress_hat = /obj/item/clothing/head/solgov/dress/fleet + dress_hat = /obj/item/clothing/head/solgov/dress/fleet/garrison dress_gloves = /obj/item/clothing/gloves/white /decl/hierarchy/mil_uniform/civilian diff --git a/maps/torch/datums/uniforms_fleet.dm b/maps/torch/datums/uniforms_fleet.dm index cf640bfa3e6..f8d7dd32115 100644 --- a/maps/torch/datums/uniforms_fleet.dm +++ b/maps/torch/datums/uniforms_fleet.dm @@ -69,6 +69,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list( /obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress @@ -152,6 +153,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list( /obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress @@ -228,6 +230,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) /decl/hierarchy/mil_uniform/fleet/med/officer @@ -301,6 +304,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) /decl/hierarchy/mil_uniform/fleet/sup/officer @@ -379,6 +383,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) /decl/hierarchy/mil_uniform/fleet/srv/officer @@ -435,6 +440,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) /decl/hierarchy/mil_uniform/fleet/exp/officer @@ -491,6 +497,7 @@ service_over = /obj/item/clothing/suit/storage/solgov/service/fleet/snco dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet/snco + dress_hat = /obj/item/clothing/head/solgov/dress/fleet dress_extra = list(/obj/item/material/sword/replica/officersword/pettyofficer, /obj/item/clothing/head/beret/solgov/fleet/dress) /decl/hierarchy/mil_uniform/fleet/spt/officer From 79a76a08584bd4aa0a038684dacac20c56128563 Mon Sep 17 00:00:00 2001 From: CSCMe <19313511+CSCMe@users.noreply.github.com> Date: Wed, 26 May 2021 04:13:34 +0200 Subject: [PATCH 031/246] Runtime fixes Also fixes diona dusting and gibbing Thanks to ChaosAlpha Fixes dusted mobs not having a ghost sprite --- code/modules/mob/death.dm | 4 ++++ code/modules/mob/living/carbon/alien/diona/_nymph.dm | 5 +++-- code/modules/mob/living/carbon/carbon.dm | 4 +++- code/modules/mob/living/silicon/robot/death.dm | 2 +- code/modules/species/station/station.dm | 2 +- maps/torch/job/medical_jobs.dm | 4 ++-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index 51c2e8ede4f..cb4d5ebf2bf 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -26,6 +26,10 @@ //Dusting robots does not eject the MMI, so it's a bit more powerful than gib() /N /mob/proc/dust(anim="dust-m",remains=/obj/effect/decal/cleanable/ash) death(1) + + if(stat == DEAD) + ghostize(FALSE) //Ghosts the mob here so it keeps its sprite + var/atom/movable/overlay/animation = null ADD_TRANSFORMATION_MOVEMENT_HANDLER(src) icon = null diff --git a/code/modules/mob/living/carbon/alien/diona/_nymph.dm b/code/modules/mob/living/carbon/alien/diona/_nymph.dm index 75ff2416047..b519bef1fb1 100644 --- a/code/modules/mob/living/carbon/alien/diona/_nymph.dm +++ b/code/modules/mob/living/carbon/alien/diona/_nymph.dm @@ -102,7 +102,7 @@ if(prob(emote_prob)) D.emote(pick("scratch","jump","chirp","tail")) -/proc/split_into_nymphs(var/mob/living/carbon/human/donor) +/proc/split_into_nymphs(var/mob/living/carbon/human/donor, dying) if(!donor || donor.species.name != SPECIES_DIONA) return @@ -149,4 +149,5 @@ donor.drop_from_inventory(W) donor.visible_message("\The [donor] quivers slightly, then splits apart with a wet slithering noise.") - qdel(donor) + if (!dying) + qdel(donor) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 0e0b19482bf..49bc9ae8f88 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -77,6 +77,8 @@ /mob/living/carbon/gib() for(var/mob/M in contents) + if(isspecies(src, SPECIES_DIONA) && istype(M, /mob/living/carbon/alien/diona) && (M.stat != DEAD)) + continue M.dropInto(loc) visible_message(SPAN_DANGER("\The [M] bursts out of \the [src]!")) ..() @@ -508,4 +510,4 @@ if(old_internal && !internal) to_chat(src, "You are no longer running on internals.") if(internals) - internals.icon_state = "internal[!!internal]" \ No newline at end of file + internals.icon_state = "internal[!!internal]" diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 0f78282fc7a..0ef350bcfe4 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,7 +1,7 @@ /mob/living/silicon/robot/dust() //Delete the MMI first so that it won't go popping out. if(mmi) - qdel(mmi) + QDEL_NULL(mmi) ..() /mob/living/silicon/robot/death(gibbed,deathmessage, show_dead_message) diff --git a/code/modules/species/station/station.dm b/code/modules/species/station/station.dm index 9f92cf1bbf3..0fddb809596 100644 --- a/code/modules/species/station/station.dm +++ b/code/modules/species/station/station.dm @@ -428,7 +428,7 @@ H.visible_message("\The [H] collapses into parts, revealing a solitary diona nymph at the core.") return else - split_into_nymphs(H) + split_into_nymphs(H, TRUE) /datum/species/diona/get_blood_name() return "sap" diff --git a/maps/torch/job/medical_jobs.dm b/maps/torch/job/medical_jobs.dm index 803418ae935..45fcc4df2b2 100644 --- a/maps/torch/job/medical_jobs.dm +++ b/maps/torch/job/medical_jobs.dm @@ -262,9 +262,9 @@ give_psionic_implant_on_join = FALSE /datum/job/psychiatrist/equip(var/mob/living/carbon/human/H) - if(H.mind.role_alt_title == "Psionic Counselor") + if(H.mind?.role_alt_title == "Psionic Counselor") psi_faculties = list("[PSI_REDACTION]" = PSI_RANK_OPERANT) - if(H.mind.role_alt_title == "Mentalist") + if(H.mind?.role_alt_title == "Mentalist") psi_faculties = list("[PSI_COERCION]" = PSI_RANK_OPERANT) return ..() From 188bd3caf82418d8f8f9a4b4d4d380a0eeef8c95 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Wed, 26 May 2021 05:03:13 +0200 Subject: [PATCH 032/246] Automatic changelog generation for PR #30734 [ci skip] --- html/changelogs/AutoChangeLog-pr-30734.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30734.yml diff --git a/html/changelogs/AutoChangeLog-pr-30734.yml b/html/changelogs/AutoChangeLog-pr-30734.yml new file mode 100644 index 00000000000..5abd790c453 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30734.yml @@ -0,0 +1,5 @@ +author: Spookerton +delete-after: true +changes: + - tweak: The peacekeeper armor and helmet sets in the emergency armory are black + medium with sol tags instead. From e563aa9872ea1ecb776ab2c7dbbbf3c715084f96 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Wed, 26 May 2021 05:03:22 +0200 Subject: [PATCH 033/246] Automatic changelog generation for PR #30521 [ci skip] --- html/changelogs/AutoChangeLog-pr-30521.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30521.yml diff --git a/html/changelogs/AutoChangeLog-pr-30521.yml b/html/changelogs/AutoChangeLog-pr-30521.yml new file mode 100644 index 00000000000..25810c2da3e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30521.yml @@ -0,0 +1,9 @@ +author: Mucker +delete-after: true +changes: + - rscadd: Ported Polaris mob AI and integrated existing simple mobs into it. + - admin: Added a buildmode for controlling mobs with AI (sort of like an RTS). + - bugfix: Fixed ranged mobs rapid firing when being melee'd. + - bugfix: Fixed mobs being able to circumvent barriers via diagonal movement. + - rscdel: Nanomachines and Commanded have been disabled until they can be integrated + with the new mob AI. From ef99e15b4d4c6f792950acb210c841887c71e059 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Wed, 26 May 2021 03:08:18 +0000 Subject: [PATCH 034/246] Automatic changelog generation --- html/changelog.html | 12 ++++++++++++ html/changelogs/.all_changelog.yml | 10 ++++++++++ html/changelogs/AutoChangeLog-pr-30521.yml | 9 --------- html/changelogs/AutoChangeLog-pr-30734.yml | 5 ----- 4 files changed, 22 insertions(+), 14 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30521.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30734.yml diff --git a/html/changelog.html b/html/changelog.html index c22735bc881..10565b69737 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -62,6 +62,18 @@

          Hubblenaut updated:

          • Fixes melee weapons like the energy sword effectively not having armor penetration.
          +

          Mucker updated:

          +
            +
          • Ported Polaris mob AI and integrated existing simple mobs into it.
          • +
          • Added a buildmode for controlling mobs with AI (sort of like an RTS).
          • +
          • Fixed ranged mobs rapid firing when being melee'd.
          • +
          • Fixed mobs being able to circumvent barriers via diagonal movement.
          • +
          • Nanomachines and Commanded have been disabled until they can be integrated with the new mob AI.
          • +
          +

          Spookerton updated:

          +
            +
          • The peacekeeper armor and helmet sets in the emergency armory are black medium with sol tags instead.
          • +

          25 May 2021

          Jux updated:

          diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 3b7ded54c18..333777fa945 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16885,3 +16885,13 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Hubblenaut: - bugfix: Fixes melee weapons like the energy sword effectively not having armor penetration. + Mucker: + - rscadd: Ported Polaris mob AI and integrated existing simple mobs into it. + - admin: Added a buildmode for controlling mobs with AI (sort of like an RTS). + - bugfix: Fixed ranged mobs rapid firing when being melee'd. + - bugfix: Fixed mobs being able to circumvent barriers via diagonal movement. + - rscdel: Nanomachines and Commanded have been disabled until they can be integrated + with the new mob AI. + Spookerton: + - tweak: The peacekeeper armor and helmet sets in the emergency armory are black + medium with sol tags instead. diff --git a/html/changelogs/AutoChangeLog-pr-30521.yml b/html/changelogs/AutoChangeLog-pr-30521.yml deleted file mode 100644 index 25810c2da3e..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30521.yml +++ /dev/null @@ -1,9 +0,0 @@ -author: Mucker -delete-after: true -changes: - - rscadd: Ported Polaris mob AI and integrated existing simple mobs into it. - - admin: Added a buildmode for controlling mobs with AI (sort of like an RTS). - - bugfix: Fixed ranged mobs rapid firing when being melee'd. - - bugfix: Fixed mobs being able to circumvent barriers via diagonal movement. - - rscdel: Nanomachines and Commanded have been disabled until they can be integrated - with the new mob AI. diff --git a/html/changelogs/AutoChangeLog-pr-30734.yml b/html/changelogs/AutoChangeLog-pr-30734.yml deleted file mode 100644 index 5abd790c453..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30734.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Spookerton -delete-after: true -changes: - - tweak: The peacekeeper armor and helmet sets in the emergency armory are black - medium with sol tags instead. From f18ea7f4578515a2685ab0658c7f895b267af1f5 Mon Sep 17 00:00:00 2001 From: Cody Brittain Date: Wed, 26 May 2021 12:34:59 -0400 Subject: [PATCH 035/246] Survival Pod now spawns with plastic, and the correct tape roll. --- maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm index c6b61c0fcb0..5cc3ae86bcb 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm @@ -482,7 +482,7 @@ dir = 1; icon_state = "railing0-1" }, -/obj/item/tape/engineering, +/obj/item/taperoll/engineering, /turf/simulated/floor/tiled/techfloor/grid, /area/map_template/crashed_pod) "fj" = ( @@ -665,6 +665,7 @@ /obj/item/stack/material/glass/fifty, /obj/item/stack/material/glass/fifty, /obj/item/stack/material/plasteel/fifty, +/obj/item/stack/material/plastic/fifty, /obj/item/device/flashlight/lantern, /obj/item/device/flashlight/lantern, /obj/item/storage/box/glowsticks, From 0b0b9454efbede869665a7333b1018a60ad99357 Mon Sep 17 00:00:00 2001 From: CrimsonShrike Date: Thu, 27 May 2021 01:47:02 +0100 Subject: [PATCH 036/246] Unlocked rig panels now won't check access --- code/modules/clothing/spacesuits/rig/rig.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 43cce4fc3b6..9926a1324ba 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -596,7 +596,7 @@ return ret /obj/item/rig/get_req_access() - if(!security_check_enabled) + if(!security_check_enabled || !locked) return list() return ..() From 4a4aab7bd8fe8e87a6c6bbe969d384690dd9a407 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 27 May 2021 04:43:51 +0200 Subject: [PATCH 037/246] Automatic changelog generation for PR #30738 [ci skip] --- html/changelogs/AutoChangeLog-pr-30738.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30738.yml diff --git a/html/changelogs/AutoChangeLog-pr-30738.yml b/html/changelogs/AutoChangeLog-pr-30738.yml new file mode 100644 index 00000000000..d73f12ba984 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30738.yml @@ -0,0 +1,5 @@ +author: CSCMe, ChaosAlpha +delete-after: true +changes: + - bugfix: Gibbed Dionaea couldn't jump between their nymphs. + - bugfix: Dusted non-humans didn't have ghost sprites From e2a2e3b9fae1ea4234a2384cdc78048f1fda02c9 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 27 May 2021 04:43:59 +0200 Subject: [PATCH 038/246] Automatic changelog generation for PR #30742 [ci skip] --- html/changelogs/AutoChangeLog-pr-30742.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30742.yml diff --git a/html/changelogs/AutoChangeLog-pr-30742.yml b/html/changelogs/AutoChangeLog-pr-30742.yml new file mode 100644 index 00000000000..e68a83118af --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30742.yml @@ -0,0 +1,5 @@ +author: Jux +delete-after: true +changes: + - tweak: The fleet wheel cover is now exclusive to SNCOs and Officers. Tout your + headgear over the lower enlisted and laugh. From bbdb25dcef5ae13294dbf3151d1759b34a09bf21 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 27 May 2021 02:54:03 +0000 Subject: [PATCH 039/246] Automatic changelog generation --- html/changelog.html | 11 +++++++++++ html/changelogs/.all_changelog.yml | 7 +++++++ html/changelogs/AutoChangeLog-pr-30738.yml | 5 ----- html/changelogs/AutoChangeLog-pr-30742.yml | 5 ----- 4 files changed, 18 insertions(+), 10 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30738.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30742.yml diff --git a/html/changelog.html b/html/changelog.html index 10565b69737..47d8cbbab69 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,17 @@ -->
          +

          27 May 2021

          +

          CSCMe, ChaosAlpha updated:

          +
            +
          • Gibbed Dionaea couldn't jump between their nymphs.
          • +
          • Dusted non-humans didn't have ghost sprites
          • +
          +

          Jux updated:

          +
            +
          • The fleet wheel cover is now exclusive to SNCOs and Officers. Tout your headgear over the lower enlisted and laugh.
          • +
          +

          26 May 2021

          Hubblenaut updated:

            diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 333777fa945..5d94692a46a 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16895,3 +16895,10 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Spookerton: - tweak: The peacekeeper armor and helmet sets in the emergency armory are black medium with sol tags instead. +2021-05-27: + CSCMe, ChaosAlpha: + - bugfix: Gibbed Dionaea couldn't jump between their nymphs. + - bugfix: Dusted non-humans didn't have ghost sprites + Jux: + - tweak: The fleet wheel cover is now exclusive to SNCOs and Officers. Tout your + headgear over the lower enlisted and laugh. diff --git a/html/changelogs/AutoChangeLog-pr-30738.yml b/html/changelogs/AutoChangeLog-pr-30738.yml deleted file mode 100644 index d73f12ba984..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30738.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: CSCMe, ChaosAlpha -delete-after: true -changes: - - bugfix: Gibbed Dionaea couldn't jump between their nymphs. - - bugfix: Dusted non-humans didn't have ghost sprites diff --git a/html/changelogs/AutoChangeLog-pr-30742.yml b/html/changelogs/AutoChangeLog-pr-30742.yml deleted file mode 100644 index e68a83118af..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30742.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Jux -delete-after: true -changes: - - tweak: The fleet wheel cover is now exclusive to SNCOs and Officers. Tout your - headgear over the lower enlisted and laugh. From ee806c8104ffe57d6da0dd11adeeaf639428db9f Mon Sep 17 00:00:00 2001 From: Jaydno Date: Thu, 27 May 2021 18:25:46 +1200 Subject: [PATCH 040/246] Skrellship tweaks e --- maps/away/skrellscoutship/skrellscoutship.dm | 1 + maps/away/skrellscoutship/skrellscoutship_revamp.dmm | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/maps/away/skrellscoutship/skrellscoutship.dm b/maps/away/skrellscoutship/skrellscoutship.dm index ff9a0e6a911..55853b5ba34 100644 --- a/maps/away/skrellscoutship/skrellscoutship.dm +++ b/maps/away/skrellscoutship/skrellscoutship.dm @@ -306,3 +306,4 @@ color = "#40e0d0" name = "thermal induction generator" desc = "Made by Krri'gli Corp using thermal induction technology, this heater is guaranteed not to set anything, or anyone, on fire." + set_temperature = T0C+40 diff --git a/maps/away/skrellscoutship/skrellscoutship_revamp.dmm b/maps/away/skrellscoutship/skrellscoutship_revamp.dmm index 40b1235ce58..d5f4a31e258 100644 --- a/maps/away/skrellscoutship/skrellscoutship_revamp.dmm +++ b/maps/away/skrellscoutship/skrellscoutship_revamp.dmm @@ -1134,6 +1134,10 @@ /obj/machinery/light/skrell{ dir = 8 }, +/obj/item/reagent_containers/hypospray, +/obj/item/reagent_containers/hypospray{ + pixel_x = 4 + }, /turf/simulated/floor/tiled/skrell/white, /area/ship/skrellscoutship/crew/medbay) "dS" = ( @@ -4966,6 +4970,10 @@ dir = 1; health = 1e+006 }, +/obj/item/disk/integrated_circuit/upgrade/clone{ + pixel_x = 7 + }, +/obj/item/disk/integrated_circuit/upgrade/advanced, /turf/simulated/floor/tiled/skrell/orange, /area/ship/skrellscoutship/wings/starboard) "JT" = ( From 4e422c3033ea1c1d87be645e3e9d95fc296c82e9 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 27 May 2021 22:49:47 +0200 Subject: [PATCH 041/246] Automatic changelog generation for PR #30716 [ci skip] --- html/changelogs/AutoChangeLog-pr-30716.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30716.yml diff --git a/html/changelogs/AutoChangeLog-pr-30716.yml b/html/changelogs/AutoChangeLog-pr-30716.yml new file mode 100644 index 00000000000..84fac4f0036 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30716.yml @@ -0,0 +1,6 @@ +author: GeneralCamo +delete-after: true +changes: + - tweak: Renamed Laboratory Technician to Pharmacist + - tweak: Pharmacists now get their job-specific packs and PDA + - rscadd: Re-added "Chemist" alt-title to the Pharmacist From 75911f0161ef59acd9712c64e4bf5d4ae894345c Mon Sep 17 00:00:00 2001 From: CSCMe <19313511+CSCMe@users.noreply.github.com> Date: Fri, 28 May 2021 02:42:08 +0200 Subject: [PATCH 042/246] Fixes errors when non-humans blueswitch The bluespace clone steals their daddy's inventory instead of getting job gear so they always look like an exact copy --- .../endgame/bluespace_jump/bluespace_jump.dm | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm b/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm index 9245389fdd9..beebe87597d 100644 --- a/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm +++ b/code/game/gamemodes/endgame/bluespace_jump/bluespace_jump.dm @@ -119,15 +119,21 @@ return daddy.examine(arglist(args)) /obj/effect/bluegoast/proc/blueswitch() - var/mob/living/carbon/human/H = new(get_turf(src), daddy.species.name) + var/mob/living/carbon/human/H + if(ishuman(daddy)) + H = new(get_turf(src), daddy.species.name) + H.dna = daddy.dna.Clone() + H.sync_organ_dna() + H.UpdateAppearance() + for(var/obj/item/entry in daddy.get_equipped_items(TRUE)) + daddy.remove_from_mob(entry) //steals instead of copies so we don't end up with duplicates + H.equip_to_appropriate_slot(entry) + else + H = new daddy.type(get_turf(src)) + H.appearance = daddy.appearance + H.real_name = daddy.real_name - H.dna = daddy.dna.Clone() - H.sync_organ_dna() H.flavor_text = daddy.flavor_text - H.UpdateAppearance() - var/datum/job/job = SSjobs.get_by_title(daddy.job) - if(job) - job.equip(H) daddy.dust() qdel(src) @@ -138,4 +144,4 @@ color = "#ff9900" alpha = 100 blend_mode = BLEND_SUBTRACT - layer = FULLSCREEN_LAYER \ No newline at end of file + layer = FULLSCREEN_LAYER From bf3d73f18cd675c8e3b3a74c7aa328e09532eb03 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Fri, 28 May 2021 01:56:42 +0000 Subject: [PATCH 043/246] Automatic changelog generation --- html/changelog.html | 66 +++------------------- html/changelogs/.all_changelog.yml | 5 ++ html/changelogs/AutoChangeLog-pr-30716.yml | 6 -- 3 files changed, 13 insertions(+), 64 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30716.yml diff --git a/html/changelog.html b/html/changelog.html index 47d8cbbab69..812c6f9dc18 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,14 @@ -->
            +

            28 May 2021

            +

            GeneralCamo updated:

            +
              +
            • Renamed Laboratory Technician to Pharmacist
            • +
            • Pharmacists now get their job-specific packs and PDA
            • +
            • Re-added "Chemist" alt-title to the Pharmacist
            • +
            +

            27 May 2021

            CSCMe, ChaosAlpha updated:

              @@ -573,64 +581,6 @@

              SammasC123 updated:

              • Pain multiplier for hits to the groin (and theoretically other genitals) lowered from x2 to x1.5.
              - -

              26 March 2021

              -

              CrimsonShrike updated:

              -
                -
              • Mech light legs now prevent fall damage.
              • -
              • Exosuit manouvering units added. Space awaits! Added one to the exploration exosuit to make it usable for away sites. Added another to salvage gantry mech so it stops floating into space and dying.
              • -
              • Mechetes have been adjusted to be more powerful but slower.
              • -
              • Exosuits now have power states. Remember to turn them on using power button!
              • -
              • Adjusts some exosuit buttons to better reflect real state of suit
              • -
              • Some exosuit modules will now consume power passively too.
              • -
              • Flaregun impacts now have a chance to set victim on fire
              • -
              • Slide projectors. Maps one to the briefing room on Deck 1
              • -
              -

              EvenInDeathIStillServe updated:

              -
                -
              • Speech bubbles are animated now.
              • -
              • Matter cartridges can be partially loaded to refill RCDs, both of the construction and crossbow varieties.
              • -
              -

              Ilysen updated:

              -
                -
              • Added some digital logs on modular computers on the Ahab's Harpoon away site that elucidates more about what happened. Keep an eye out for new digital logs going forward!
              • -
              • Cultist pylons are now destroyed permanently when they shatter, instead of being placed into a weird "broken" state where have the exact same sprite but can be passed over.
              • -
              • Cult pylons now have a proper health system, instead of using random chance to determine if they're damaged.
              • -
              • Wraiths now float, preventing them from falling down z-levels and letting them counteract gravity. They'll still drift in space, though.
              • -
              • Artificer abilities no longer use a super loud sound.
              • -
              • Finally fixed wraiths being unable to use their Phase Shift.
              • -
              • Fixed player-controlled simple animals (like constructs) being unable to hurt things.
              • -
              • Natural weapons can no longer embed into wounds.
              • -
              • Artificers now correctly repair constructs on an attack instead of bashing them.
              • -
              -

              Lorwp updated:

              -
                -
              • New changelog generation method
              • -
              -

              Mucker updated:

              -
                -
              • Removed the 'teleport_far' artifact effect.
              • -
              • Adds a buildmode for editing an area, turf, and container atmospheres.
              • -
              -

              Ryan180602 updated:

              -
                -
              • Drinking glasses can now be used to splash mobs
              • -
              • Beakers can now break when thrown, spilling its contents
              • -
              • Add a harm-intent check when clicking on self with glasses/beakers
              • -
              • FBPs are no longer affected by the effects of smoke
              • -
              • Fixes an oversight with unintentionally pouring reagents into dispensers
              • -
              -

              SealCure updated:

              -
                -
              • Adds user feedback to toggling a HUDimplant.
              • -
              • Nymphs engulfing drinking glasses will now obtain a glass shard instead of deleting the container.
              • -
              • Punitelli will now sometimes spawn with a fashionable pair of sandals for his poor feet.
              • -
              -

              Spookerton updated:

              -
                -
              • The appearance editor from antag appearance selection has languages on it.
              • -
              • The vox mirror creates vox mobs appropriately equipped for their antag type.
              • -
            GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 5d94692a46a..27c31856548 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16902,3 +16902,8 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Jux: - tweak: The fleet wheel cover is now exclusive to SNCOs and Officers. Tout your headgear over the lower enlisted and laugh. +2021-05-28: + GeneralCamo: + - tweak: Renamed Laboratory Technician to Pharmacist + - tweak: Pharmacists now get their job-specific packs and PDA + - rscadd: Re-added "Chemist" alt-title to the Pharmacist diff --git a/html/changelogs/AutoChangeLog-pr-30716.yml b/html/changelogs/AutoChangeLog-pr-30716.yml deleted file mode 100644 index 84fac4f0036..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30716.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: GeneralCamo -delete-after: true -changes: - - tweak: Renamed Laboratory Technician to Pharmacist - - tweak: Pharmacists now get their job-specific packs and PDA - - rscadd: Re-added "Chemist" alt-title to the Pharmacist From dc8a749361d6d1b997bf4d21d35c900f9d967e9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 May 2021 05:39:38 +0000 Subject: [PATCH 044/246] Bump actions/cache from 2.1.5 to 2.1.6 Bumps [actions/cache](https://github.com/actions/cache) from 2.1.5 to 2.1.6. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/1a9e2138d905efd099035b49d8b7a3888c653ca8...c64c572235d810460d0d6876e9c705ad5002b353) Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5163a859f2e..2acbf9f155f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f - name: Setup Cache - uses: actions/cache@1a9e2138d905efd099035b49d8b7a3888c653ca8 + uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 with: path: $HOME/spaceman_dmm/$SPACEMAN_DMM_VERSION key: ${{ runner.os }}-spacemandmm-${{ env.SPACEMAN_DMM_VERSION }} @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f - name: Setup Cache - uses: actions/cache@1a9e2138d905efd099035b49d8b7a3888c653ca8 + uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 with: path: $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} @@ -71,7 +71,7 @@ jobs: steps: - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f - name: Setup Cache - uses: actions/cache@1a9e2138d905efd099035b49d8b7a3888c653ca8 + uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 with: path: $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} From ce83e535b972f6e45bcc0affe2280a01afb0c04a Mon Sep 17 00:00:00 2001 From: Vi Date: Fri, 28 May 2021 18:01:46 -0400 Subject: [PATCH 045/246] Change Skills On-Station traitors now get two trained, one experienced, and one master skill choices. --- code/modules/mob/skills/skill_ui.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/skills/skill_ui.dm b/code/modules/mob/skills/skill_ui.dm index 3977c51b2c6..ac0e2c7cfb7 100644 --- a/code/modules/mob/skills/skill_ui.dm +++ b/code/modules/mob/skills/skill_ui.dm @@ -228,7 +228,7 @@ The generic antag version. Similar, but for station antags that have jobs. */ /datum/nano_module/skill_ui/antag/station - max_choices = list(0, 0, 3, 1, 0) + max_choices = list(0, 0, 2, 1, 1) /* Similar, but for off-station jobs (Bearcat, Verne, survivor etc.). */ From 598409ffced83361a948a4d0544058640072bb9d Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sat, 29 May 2021 02:13:42 +0000 Subject: [PATCH 046/246] Automatic changelog generation --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 812c6f9dc18..0c7f7154430 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -575,12 +575,6 @@

            Ilysen updated:

          • You can now examine many circuit boards to learn some short information about the machine they're used to build. Your Construction skill affects the amount of info you glean, with Trained construction getting the most results.
          • Examining a machine with at least Basic construction skill will tell you about its function.
          - -

          27 March 2021

          -

          SammasC123 updated:

          -
            -
          • Pain multiplier for hits to the groin (and theoretically other genitals) lowered from x2 to x1.5.
          • -
          GoonStation 13 Development Team From 6f052b8da75c30a70691b483537f512508553b0b Mon Sep 17 00:00:00 2001 From: Archemagus <32466328+Archemagus@users.noreply.github.com> Date: Sat, 29 May 2021 13:14:01 +0300 Subject: [PATCH 047/246] Stop CLcompiler from throwing errors --- .github/workflows/changelog_generation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/changelog_generation.yml b/.github/workflows/changelog_generation.yml index c5e27896f1c..0d5c44c5182 100644 --- a/.github/workflows/changelog_generation.yml +++ b/.github/workflows/changelog_generation.yml @@ -31,5 +31,6 @@ jobs: run: | git config --global user.email "${{ secrets.BOT_EMAIL }}" git config --global user.name "${{ secrets.BOT_NAME }}" + git diff --quiet --exit-code && echo "No changes found, abort." && exit 0 git commit -m "Automatic changelog generation" -a git push From f310b01f3d10e5baabbe0ae27bc02ba7de20d304 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sat, 29 May 2021 18:32:36 +0200 Subject: [PATCH 048/246] Automatic changelog generation for PR #30744 [ci skip] --- html/changelogs/AutoChangeLog-pr-30744.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30744.yml diff --git a/html/changelogs/AutoChangeLog-pr-30744.yml b/html/changelogs/AutoChangeLog-pr-30744.yml new file mode 100644 index 00000000000..71a7223cd49 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30744.yml @@ -0,0 +1,6 @@ +author: GeneralCamo +delete-after: true +changes: + - maptweak: The crashed survival pod now spawns with plastic. + - bugfix: The crashed survival pod now spawns the correct engineering tape roll + (rather than pre-placed engineering tape) From a8617cf3a8acacff67209feb5e13d9114f84ae09 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sat, 29 May 2021 18:32:59 +0200 Subject: [PATCH 049/246] Automatic changelog generation for PR #30746 [ci skip] --- html/changelogs/AutoChangeLog-pr-30746.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30746.yml diff --git a/html/changelogs/AutoChangeLog-pr-30746.yml b/html/changelogs/AutoChangeLog-pr-30746.yml new file mode 100644 index 00000000000..11a202563b1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30746.yml @@ -0,0 +1,5 @@ +author: CrimsonShrike +delete-after: true +changes: + - tweak: Rigs with unlocked panels require no access to use. Sharing rigs with department + members no longer requires cutting wires. From 2e8f76b08acb28eb9a3e477e590fe129e0a254c6 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sat, 29 May 2021 18:33:56 +0200 Subject: [PATCH 050/246] Automatic changelog generation for PR #30748 [ci skip] --- html/changelogs/AutoChangeLog-pr-30748.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30748.yml diff --git a/html/changelogs/AutoChangeLog-pr-30748.yml b/html/changelogs/AutoChangeLog-pr-30748.yml new file mode 100644 index 00000000000..b41e489129f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30748.yml @@ -0,0 +1,5 @@ +author: 0sj/Jaydn +delete-after: true +changes: + - tweak: skrellian space heaters now have a default temp of 40 + - maptweak: Added circuit printer upgrades and two hyposprays to skrellship From 8655ba100f0326067be05183673d668152dbb511 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sat, 29 May 2021 19:39:27 +0200 Subject: [PATCH 051/246] Automatic changelog generation for PR #30756 [ci skip] --- html/changelogs/AutoChangeLog-pr-30756.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30756.yml diff --git a/html/changelogs/AutoChangeLog-pr-30756.yml b/html/changelogs/AutoChangeLog-pr-30756.yml new file mode 100644 index 00000000000..3b7836254a0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30756.yml @@ -0,0 +1,5 @@ +author: Viim +delete-after: true +changes: + - tweak: Changes traitor skills from three trained, one experienced, to two trained, + one experienced, and one master. From 59b7f5454029360bac94fa7425241893e829556e Mon Sep 17 00:00:00 2001 From: Ilysen Date: Thu, 27 May 2021 08:06:59 -0400 Subject: [PATCH 052/246] beeg augment expansion --- baystation12.dme | 14 ++ code/_helpers/medical_scans.dm | 4 + code/_onclick/hud/screen_objects.dm | 7 +- code/datums/uplink/augments.dm | 44 ++++ code/datums/uplink/uplink_categories.dm | 3 + code/game/objects/items.dm | 10 +- .../objects/items/weapons/tanks/tank_types.dm | 8 + .../augment/active/adaptive_binoculars.dm | 20 ++ code/modules/augment/active/armblades.dm | 32 ++- .../augment/active/corrective_lenses.dm | 13 ++ .../modules/augment/active/glare_dampeners.dm | 10 + code/modules/augment/active/iatric_monitor.dm | 23 +++ .../augment/active/internal_air_system.dm | 58 ++++++ .../augment/active/leukocyte_breeder.dm | 67 +++++++ .../modules/augment/active/nerve_dampeners.dm | 42 ++++ code/modules/augment/active/popout_shotgun.dm | 33 +++ code/modules/augment/augment.dm | 22 +- code/modules/augment/cbm.dm | 188 ++++++++++++++++++ code/modules/augment/equip.dm | 18 ++ code/modules/augment/passive/armor.dm | 3 +- code/modules/augment/passive/boost/muscle.dm | 1 + code/modules/augment/passive/fluff.dm | 86 ++++++++ code/modules/augment/simple.dm | 21 +- .../loadout/lists/augments.dm | 47 +++++ .../preference_setup/loadout/loadout.dm | 43 +++- code/modules/clothing/glasses/glasses.dm | 26 +++ code/modules/clothing/glasses/prescription.dm | 8 +- code/modules/codex/entries/augments.dm | 40 ++++ .../mob/living/carbon/human/examine.dm | 7 +- .../file_system/reports/crew_record.dm | 13 +- code/modules/organs/external/diagnostics.dm | 5 + .../research/designs/designs_mechfab.dm | 57 ++++++ icons/obj/augment.dmi | Bin 2302 -> 7372 bytes icons/obj/surgery.dmi | Bin 36994 -> 37246 bytes icons/obj/tank.dmi | Bin 4206 -> 10659 bytes maps/torch/loadout/loadout_augments.dm | 11 + maps/torch/torch.dm | 2 +- 37 files changed, 951 insertions(+), 35 deletions(-) create mode 100644 code/datums/uplink/augments.dm create mode 100644 code/modules/augment/active/adaptive_binoculars.dm create mode 100644 code/modules/augment/active/corrective_lenses.dm create mode 100644 code/modules/augment/active/glare_dampeners.dm create mode 100644 code/modules/augment/active/iatric_monitor.dm create mode 100644 code/modules/augment/active/internal_air_system.dm create mode 100644 code/modules/augment/active/leukocyte_breeder.dm create mode 100644 code/modules/augment/active/nerve_dampeners.dm create mode 100644 code/modules/augment/active/popout_shotgun.dm create mode 100644 code/modules/augment/cbm.dm create mode 100644 code/modules/augment/equip.dm create mode 100644 code/modules/augment/passive/fluff.dm create mode 100644 code/modules/client/preference_setup/loadout/lists/augments.dm create mode 100644 code/modules/codex/entries/augments.dm create mode 100644 maps/torch/loadout/loadout_augments.dm diff --git a/baystation12.dme b/baystation12.dme index 1ad954194da..5824acaa5d0 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -440,6 +440,7 @@ #include "code\datums\underwear\undershirt.dm" #include "code\datums\underwear\underwear.dm" #include "code\datums\uplink\ammunition.dm" +#include "code\datums\uplink\augments.dm" #include "code\datums\uplink\badassery.dm" #include "code\datums\uplink\devices and tools.dm" #include "code\datums\uplink\grenades.dm" @@ -1373,15 +1374,26 @@ #include "code\modules\atmospherics\components\unary\vent_scrubber.dm" #include "code\modules\augment\active.dm" #include "code\modules\augment\augment.dm" +#include "code\modules\augment\cbm.dm" +#include "code\modules\augment\equip.dm" #include "code\modules\augment\simple.dm" +#include "code\modules\augment\active\adaptive_binoculars.dm" #include "code\modules\augment\active\armblades.dm" #include "code\modules\augment\active\circuit.dm" +#include "code\modules\augment\active\corrective_lenses.dm" +#include "code\modules\augment\active\glare_dampeners.dm" #include "code\modules\augment\active\hudimplants.dm" +#include "code\modules\augment\active\iatric_monitor.dm" +#include "code\modules\augment\active\internal_air_system.dm" +#include "code\modules\augment\active\leukocyte_breeder.dm" +#include "code\modules\augment\active\nerve_dampeners.dm" #include "code\modules\augment\active\polytool.dm" +#include "code\modules\augment\active\popout_shotgun.dm" #include "code\modules\augment\active\tool\engineering.dm" #include "code\modules\augment\active\tool\surgical.dm" #include "code\modules\augment\passive\armor.dm" #include "code\modules\augment\passive\boost.dm" +#include "code\modules\augment\passive\fluff.dm" #include "code\modules\augment\passive\nanoaura.dm" #include "code\modules\augment\passive\boost\muscle.dm" #include "code\modules\augment\passive\boost\reflex.dm" @@ -1433,6 +1445,7 @@ #include "code\modules\client\preference_setup\loadout\gear_tweaks.dm" #include "code\modules\client\preference_setup\loadout\loadout.dm" #include "code\modules\client\preference_setup\loadout\lists\accessories.dm" +#include "code\modules\client\preference_setup\loadout\lists\augments.dm" #include "code\modules\client\preference_setup\loadout\lists\clothing.dm" #include "code\modules\client\preference_setup\loadout\lists\earwear.dm" #include "code\modules\client\preference_setup\loadout\lists\eyegear.dm" @@ -1569,6 +1582,7 @@ #include "code\modules\codex\entries\_codex_entry.dm" #include "code\modules\codex\entries\ascent.dm" #include "code\modules\codex\entries\atmospherics.dm" +#include "code\modules\codex\entries\augments.dm" #include "code\modules\codex\entries\clothing.dm" #include "code\modules\codex\entries\codex.dm" #include "code\modules\codex\entries\engineering.dm" diff --git a/code/_helpers/medical_scans.dm b/code/_helpers/medical_scans.dm index 1f74454de9a..2b66997ea2a 100644 --- a/code/_helpers/medical_scans.dm +++ b/code/_helpers/medical_scans.dm @@ -76,6 +76,10 @@ scan["internal_organs"] = list() for(var/obj/item/organ/internal/I in H.internal_organs) + if (istype(I, /obj/item/organ/internal/augment)) + var/obj/item/organ/internal/augment/A = I + if (!A.known) // Hidden augments don't appear on scans + continue var/list/O = list() O["name"] = I.name O["is_broken"] = I.is_broken() diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 35e050f6593..dd20c6b373e 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -280,7 +280,12 @@ C.set_internals(tankcheck[best], "\the [tankcheck[best]] [from] your [nicename[best]]") if(!C.internal) - to_chat(C, "You don't have \a [breathes] tank.") + // Finally, check for an internal air system. + // We use this as an absolute last resort, so we don't include it in the above logic + // There's no need to check that the gas contents are safe, because its internal logic always make sure it is + var/obj/item/organ/internal/augment/active/internal_air_system/IAS = locate() in C.internal_organs + if (!IAS?.activate()) + to_chat(C, SPAN_WARNING("You don't have \a [breathes] tank.")) if("act_intent") usr.a_intent_change("right") diff --git a/code/datums/uplink/augments.dm b/code/datums/uplink/augments.dm new file mode 100644 index 00000000000..8193160833e --- /dev/null +++ b/code/datums/uplink/augments.dm @@ -0,0 +1,44 @@ +/*********** +* Augments * +***********/ + +/datum/uplink_item/item/augment + category = /datum/uplink_category/augments + +/datum/uplink_item/item/augment/aug_internal_air_system + name = "Internal Air System CBM (chest, active)" + desc = "This flexible air sack housed in your torso slowly fills with safe air as you breathe, and can be used as a low-capacity internals source if nothing else is available. \ + It will automatically filter out the safest air for your species. It has been configured to be undetectable on body scanners. \ + NOTE: This augment is incompatible with synthetic biologies." + item_cost = 24 + path = /obj/item/device/compact_bionic_module/internal_air_system + +/datum/uplink_item/item/augment/aug_adaptive_binoculars + name = "Adaptive Binoculars CBM (head)" + desc = "A pair of ultrathin lenses can be deployed or retracted at will from your eye sockets. They have powerful zoom capabilities, allowing you to see into the distance. \ + They have been configured to be undetectable on body scanners." + item_cost = 30 + path = /obj/item/device/compact_bionic_module/adaptive_binoculars + +/datum/uplink_item/item/augment/aug_iatric_monitor + name = "Iatric Monitor CBM (head)" + desc = "A small computer system attached to the brain stem that monitors your life signs. It has been configured to be undetectable on body scanners. \ + It can be activated to gain a simple readout of your current physical state that can be understood regardless of your medical skill. \ + NOTE: This augment is incompatible with synthetic biologies." + item_cost = 20 + path = /obj/item/device/compact_bionic_module/iatric_monitor + +/datum/uplink_item/item/augment/aug_wrist_blade + name = "Wrist Blade CBM (arm)" + desc = "This concealed housing is mounted inside your lower arm, and can be activated to extend a vicious, lightweight blade. Useful for assassinations or self-defense. \ + Developed especially for concealment, its presence will not be revealed by body scanners." + item_cost = 32 // Identical to an energy sword - much less damage, but it has its own benefits, so consider it a sidegrade + path = /obj/item/device/compact_bionic_module/wrist_blade + +/datum/uplink_item/item/augment/aug_popout_shotgun + name = "Pop-out Shotgun CBM (arm)" + desc = "Replacing a hefty part of your forearm, this mechanism can be controlled with a flicking motion to reveal a long 12-gauge barrel that can fit a single shell. \ + The ultimate trump card when you're out of options. It comes pre-loaded with a single buckshot shell. \ + Due to its bulk, it is impossible to conceal from body scanners, and will be discovered by anyone feeling your bones - install with caution!" + item_cost = 60 + path = /obj/item/device/compact_bionic_module/popout_shotgun diff --git a/code/datums/uplink/uplink_categories.dm b/code/datums/uplink/uplink_categories.dm index fc00fac157c..3e907ce86c5 100644 --- a/code/datums/uplink/uplink_categories.dm +++ b/code/datums/uplink/uplink_categories.dm @@ -33,6 +33,9 @@ /datum/uplink_category/implants name = "Implants" +/datum/uplink_category/augments + name = "Augments" + /datum/uplink_category/medical name = "Medical & Food" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index fa812072213..ccd59166368 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -706,8 +706,14 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. else if(!zoom && istype(H) && H.equipment_tint_total >= TINT_MODERATE) to_chat(user, "Your visor gets in the way of looking through the [devicename].") return - else if(!zoom && user.get_active_hand() != src) - to_chat(user, "You are too distracted to look through the [devicename], perhaps if it was in your active hand this might work better.") + var/is_distracted + if (H) // Humans can zoom through items they wear on their eyes + is_distracted = !zoom && H.get_active_hand() != src && H.get_equipped_item(slot_glasses) != src + else + is_distracted = !zoom && user.get_active_hand() != src + + if(is_distracted) + to_chat(user, SPAN_WARNING("You are too distracted to look through the [devicename]. Perhaps if it was in your active hand this might work better.")) return if(user.hud_used.hud_shown) diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 0c71bc9f867..b4ef0457822 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -133,6 +133,14 @@ gauge_icon = "indicator_emergency_double" volume = 60 +/obj/item/tank/emergency/air_sac + name = "air sac" + desc = "A small, compressed air sac that fills with breathable air, to be used in emergencies." + icon_state = "air_sac" + gauge_icon = "indicator_emergency" + volume = 20 + unacidable = TRUE + /* * Nitrogen */ diff --git a/code/modules/augment/active/adaptive_binoculars.dm b/code/modules/augment/active/adaptive_binoculars.dm new file mode 100644 index 00000000000..d2d5fae743f --- /dev/null +++ b/code/modules/augment/active/adaptive_binoculars.dm @@ -0,0 +1,20 @@ +/obj/item/organ/internal/augment/active/simple/equip/adaptive_binoculars + name = "adaptive binoculars" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "adaptive_binoculars" + desc = "Digital glass 'screens' can be deployed over the eyes. At the user's control, their image can be greatly enhanced, providing a view of distant areas." + action_button_name = "Deploy lenses" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 2) + equip_slot = slot_glasses + holding_type = /obj/item/clothing/glasses/augment_binoculars + +/obj/item/organ/internal/augment/active/simple/equip/adaptive_binoculars/hidden + known = FALSE + +/obj/item/organ/internal/augment/active/simple/equip/adaptive_binoculars/emp_act(severity) + . = ..() + if (holding?.zoom) + to_chat(owner, SPAN_WARNING("Your eyes fill with static as your [holding.name] malfunction.")) + owner.eye_blind += 10 + owner.eye_blurry += 20 + holding.unzoom() diff --git a/code/modules/augment/active/armblades.dm b/code/modules/augment/active/armblades.dm index f00b30649f2..f7f8b73ddc1 100644 --- a/code/modules/augment/active/armblades.dm +++ b/code/modules/augment/active/armblades.dm @@ -6,7 +6,8 @@ applies_material_colour = 0 desc = "A handy utility blade for the discerning augmentee. Warranty void if used for cutting." base_parry_chance = 30 - unbreakable = 1 + unbreakable = TRUE + unacidable = TRUE force_multiplier = 0.2 sharp = TRUE edge = TRUE @@ -39,4 +40,31 @@ allowed_organs = list(BP_AUGMENT_R_HAND, BP_AUGMENT_L_HAND) holding_type = /obj/item/material/armblade/claws //Limited to robolimbs - augment_flags = AUGMENTATION_MECHANIC \ No newline at end of file + augment_flags = AUGMENTATION_MECHANIC + +/// Traitor version - no parry chance but good damage, and compatible with organic limbs +/obj/item/organ/internal/augment/active/simple/wrist_blade + name = "concealed wrist blade" + desc = "A concealed sheath made from bio-compatible cloth, shaped for a thin blade." + action_button_name = "Deploy blade" + icon_state = "armblade" + allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM) + holding_type = /obj/item/material/armblade/wrist + known = FALSE // Specially designed for concealment + origin_tech = list(TECH_COMBAT = 3, TECH_ESOTERIC = 4) + deploy_sound = 'sound/effects/holster/sheathout.ogg' + retract_sound = 'sound/effects/holster/sheathin.ogg' + +/obj/item/material/armblade/wrist + name = "wrist blade" + desc = "A thin and very sharp folding blade specially made for combat, made from a specialized alloy that prevents all that nasty blood and viscera from sticking to it. Its light weight allows for rapid slashing attacks." + icon_state = "armblade" + item_state = "wolverine" + base_parry_chance = 0 + force_multiplier = 0.2 + attack_cooldown_modifier = -1 + default_material = MATERIAL_PLASTEEL // Steel is so unsophisticated + w_class = ITEM_SIZE_SMALL // Can't dismember limbs - only hands/feet + +/obj/item/material/armblade/wrist/add_blood(mob/living/carbon/human/M) + return FALSE diff --git a/code/modules/augment/active/corrective_lenses.dm b/code/modules/augment/active/corrective_lenses.dm new file mode 100644 index 00000000000..6fd2ae50377 --- /dev/null +++ b/code/modules/augment/active/corrective_lenses.dm @@ -0,0 +1,13 @@ +/obj/item/organ/internal/augment/active/simple/equip/corrective_lenses + name = "corrective lenses" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "corrective_lenses" + desc = "A pair of retractable, ultrathin corrective lenses are installed into the eye sockets. They can be deployed or retracted at will and serve as prescription glasses." + action_button_name = "Deploy lenses" + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + equip_slot = slot_glasses + holding_type = /obj/item/clothing/glasses/prescription/augment + +/obj/item/organ/internal/augment/active/simple/equip/corrective_lenses/onRoundstart() + deploy() diff --git a/code/modules/augment/active/glare_dampeners.dm b/code/modules/augment/active/glare_dampeners.dm new file mode 100644 index 00000000000..4d6555ccf56 --- /dev/null +++ b/code/modules/augment/active/glare_dampeners.dm @@ -0,0 +1,10 @@ +/obj/item/organ/internal/augment/active/simple/equip/glare_dampeners + name = "glare dampeners" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "glare_dampeners" + desc = "Thick, tinted lenses installed in your head can deploy over your eyes, reducing visibility but providing protection from welding glare and bright lights." + action_button_name = "Deploy dampeners" + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + equip_slot = slot_glasses + holding_type = /obj/item/clothing/glasses/glare_dampeners diff --git a/code/modules/augment/active/iatric_monitor.dm b/code/modules/augment/active/iatric_monitor.dm new file mode 100644 index 00000000000..7d3dcfc6f5d --- /dev/null +++ b/code/modules/augment/active/iatric_monitor.dm @@ -0,0 +1,23 @@ +/obj/item/organ/internal/augment/active/iatric_monitor + name = "iatric monitor" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "iatric_monitor" + desc = "A small computer system constantly tracks your physiological state and vital signs. A muscle gesture can be used to receive a simple diagnostic report, not unlike that from a handheld scanner." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 3, TECH_BIO = 2) + +/obj/item/organ/internal/augment/active/iatric_monitor/emp_act(severity) + . = ..() + if (severity) + var/scan_results = medical_scan_results(owner, TRUE, SKILL_NONE) + owner.playsound_local(null, 'sound/effects/fastbeep.ogg', 20, is_global = TRUE) + to_chat(owner, "
          [scan_results]
          ") + to_chat(owner, SPAN_WARNING("Your [name] cheerily outputs a bogus report as it malfunctions.")) + +/obj/item/organ/internal/augment/active/iatric_monitor/activate() + var/scan_results = medical_scan_results(owner, TRUE, SKILL_PROF) + owner.playsound_local(null, 'sound/effects/fastbeep.ogg', 20, is_global = TRUE) + to_chat(owner, "
          [scan_results]
          ") + +/obj/item/organ/internal/augment/active/iatric_monitor/hidden + known = FALSE diff --git a/code/modules/augment/active/internal_air_system.dm b/code/modules/augment/active/internal_air_system.dm new file mode 100644 index 00000000000..247ccf94bd7 --- /dev/null +++ b/code/modules/augment/active/internal_air_system.dm @@ -0,0 +1,58 @@ +/obj/item/organ/internal/augment/active/internal_air_system + name = "internal air system" + allowed_organs = list(BP_AUGMENT_CHEST_ACTIVE) + icon_state = "internal_air_system" + desc = "A flexible air sac, made from a complex, bio-compatible polymer, is installed into the respiratory system. It gradually replenishes itself with breathable gas from the surrounding environment as you breathe, and you can later use it as a source of internals." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_MATERIAL = 3, TECH_BIO = 4) + var/obj/item/tank/emergency/air_sac/sac + +/obj/item/organ/internal/augment/active/internal_air_system/hidden + known = FALSE + +/obj/item/organ/internal/augment/active/internal_air_system/onInstall() + sac = new(owner) + sac.air_contents.adjust_gas(owner.species.breath_type, (STD_BREATH_VOLUME * 3) * sac.air_contents.volume / (R_IDEAL_GAS_EQUATION * T20C), 0) // Give us a few free breaths + to_chat(owner, SPAN_NOTICE("You feel a curious sensation as your [sac.name] starts puffing up inside your body.\n\ + Remember that you'll still need an airtight mask or helmet to use it!")) + +/obj/item/organ/internal/augment/active/internal_air_system/onRemove() + QDEL_NULL(sac) + +/obj/item/organ/internal/augment/active/internal_air_system/Process() + if (!owner || !owner.species || !sac) + return + var/safe_gas = owner.species.breath_type + if (!sac.air_contents.gas[safe_gas] && sac.air_contents.return_pressure()) // Fallback in case of species switch or etc - purge all air if the gas type is no longer safe + sac.air_contents.remove_volume(sac.air_contents.volume) + to_chat(owner, SPAN_WARNING("You feel your [sac.name] rapidly deflate as it purges unsafe air.")) + return + sac.distribute_pressure = owner.species.breath_pressure ? owner.species.breath_pressure : ONE_ATMOSPHERE + if (owner.internal || sac.air_contents.return_pressure() >= 1013) // Don't refill unless we're breathing normally + return + var/turf/T = get_turf(owner) + var/datum/gas_mixture/E = T.return_air_for_internal_lifeform() + if (E.get_gas(safe_gas)) + var/datum/gas_mixture/breath = E.remove_volume(owner.species.breath_pressure * 0.25) + sac.air_contents.adjust_gas(safe_gas, breath.get_gas(safe_gas)) // "Inhale" a breath of gas for the sac + if (sac.air_contents.return_pressure() >= 1013) + to_chat(owner, SPAN_NOTICE("Your [sac.name] stops filling as it reaches maximum pressure.")) + +/obj/item/organ/internal/augment/active/internal_air_system/activate() + if (!owner || !owner.species || (owner.internal && owner.internal != sac)) + return + if (!sac) + to_chat(owner, SPAN_WARNING("Your [name]'s sac is missing or punctured!")) + return + if (sac.air_contents.return_pressure() < owner.species.breath_pressure) + to_chat(owner, SPAN_WARNING("Your [name]'s [sac.name] is empty!")) + return + if(!(owner.wear_mask && owner.wear_mask.item_flags & ITEM_FLAG_AIRTIGHT)) + if(!(owner.head && owner.head.item_flags & ITEM_FLAG_AIRTIGHT)) + to_chat(owner, SPAN_WARNING("You need a valid mask or helmet equipped. You still need to exhale!")) + return + if (!owner.internal) + owner.set_internals(sac, "\the [sac] in your [name]") + else + owner.set_internals(null) + return TRUE diff --git a/code/modules/augment/active/leukocyte_breeder.dm b/code/modules/augment/active/leukocyte_breeder.dm new file mode 100644 index 00000000000..b2d77ad3d61 --- /dev/null +++ b/code/modules/augment/active/leukocyte_breeder.dm @@ -0,0 +1,67 @@ +/obj/item/organ/internal/augment/active/leukocyte_breeder + name = "leukocyte breeder" + allowed_organs = list(BP_AUGMENT_CHEST_ACTIVE) + icon_state = "booster" + desc = "These stimulators augment the immune system and promote the growth of hunter-killer cells in the presence of a foreign invader, effectively boosting the body's immunity to parasites and disease." + action_button_name = "Toggle leukocyte breeder" + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 4) + + var/active = FALSE + /// How many processing ticks the augment has been enabled for + var/ticks_active = 0 + /// After this many ticks, the owner has "broken in" the augment, and will benefit more but suffer drawbacks if it's disabled + var/ticks_to_acclimate = 120 + +/obj/item/organ/internal/augment/active/leukocyte_breeder/emp_act(severity) + . = ..() + if (owner && active) + if (prob(100 - (20 * severity))) // 40% chance for 3, 60% chance for 2, and 80% chance for 1 severity, respectively + to_chat(owner, SPAN_WARNING("You feel a wave of nausea as your [name] deactivates.")) + active = FALSE + +/obj/item/organ/internal/augment/active/leukocyte_breeder/onRoundstart() + active = TRUE // We can safely assume that someone starting off with the breeder will have it active + ticks_active = ticks_to_acclimate + to_chat(owner, SPAN_INFO("Your [name] has started the shift active, granting you its full benefits without needing to break it in.")) + +/obj/item/organ/internal/augment/active/leukocyte_breeder/onInstall() + if (prob(10)) + ticks_active = ticks_to_acclimate // Some folks are just lucky and don't get any side effects + +/obj/item/organ/internal/augment/active/leukocyte_breeder/activate() + if (!can_activate()) + return + if (active && ticks_active >= ticks_to_acclimate) + // Give an alert if trying to deactivate while acclimated, so roundstart takers don't accidentally turn it off by learning the buttons + if (alert(owner, "Deactivate \the [src]?", name, "Yes", "No") != "Yes" || !can_activate()) + return + active = !active + owner.playsound_local(null, 'sound/effects/fastbeep.ogg', 20, is_global = TRUE) + if (active) + to_chat(owner, SPAN_NOTICE("Leukocyte breeder engaged and improving immune response.")) + else + to_chat(owner, SPAN_WARNING("Leukocyte breeder disengaged. Short-term health may suffer.")) + if (owner.immunity >= (owner.immunity_norm * 0.9)) // Reduce short-term immunity, but only if it's at around normal levels + owner.immunity -= 10 + ticks_active = 0 + +/obj/item/organ/internal/augment/active/leukocyte_breeder/Process() + if (!owner) + return + if (active) + ticks_active++ + if (owner.immunity < owner.immunity_norm * 1.1) // Slightly boosts above baseline + owner.immunity += 0.05 // Not as effective as immune boosters, but constant + ticks_active++ + + if (ticks_active < ticks_to_acclimate) + // The body needs time to break in the augment, and may have minor side effects until it does + // This is primarily for roleplay purposes, and not any actual gameplay impact - its benefits kick in immediately + // The immune system just plays a huge role in the body, even if it doesn't in-game, and reflecting that could help with immersion! + if (ticks_active < ticks_to_acclimate) + if (prob(5)) + owner.emote(pick("cough", "sneeze")) + if (prob(3)) + to_chat(owner, SPAN_WARNING(pick("You feel uncomfortably hot.", "Your head aches.", "You feel lightheaded."))) + owner.dizziness += rand(3, 5) diff --git a/code/modules/augment/active/nerve_dampeners.dm b/code/modules/augment/active/nerve_dampeners.dm new file mode 100644 index 00000000000..376fc3ff69a --- /dev/null +++ b/code/modules/augment/active/nerve_dampeners.dm @@ -0,0 +1,42 @@ +/obj/item/organ/internal/augment/active/nerve_dampeners + name = "nerve dampeners" + allowed_organs = list(BP_AUGMENT_CHEST_ACTIVE) + icon_state = "muscule" + desc = "Each activation of this augment provides a strong painkilling effect for around thirty seconds, but will be followed by a powerful comedown. Excessive short-term use may cause brain damage." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 4, TECH_BIO = 4) + var/ticks_remaining = 0 + +/obj/item/organ/internal/augment/active/nerve_dampeners/can_activate() + if (ticks_remaining) + to_chat(owner, SPAN_WARNING("Your [name] is already active.")) + return + . = ..() + +/obj/item/organ/internal/augment/active/nerve_dampeners/activate() + if (!can_activate()) + return + to_chat(owner, SPAN_NOTICE("You activate your [name], and feel a wave of numbness wash over you!")) + ticks_remaining = 15 + if (owner.drowsyness) + to_chat(owner, SPAN_DANGER("Your body slackens as you lose sensation.")) + if (prob(owner.getBrainLoss())) + to_chat(owner, SPAN_DANGER("You slump to the ground and black out.")) + owner.Paralyse(10) + owner.adjustBrainLoss(owner.drowsyness) + +/obj/item/organ/internal/augment/active/nerve_dampeners/Process() + if (!owner) + return + if (ticks_remaining) + ticks_remaining-- + owner.add_chemical_effect(CE_PAINKILLER, 160) // About twice as strong as tramadol at full strength + if (!ticks_remaining) // ...but comes at a price. Brief short term benefit for a long-term comedown + to_chat(owner, SPAN_WARNING("You abruptly feel intensely exhausted as sensation returns.")) + owner.drowsyness = max(owner.drowsyness, 15) + owner.confused = max(owner.confused, 15) + owner.slurring = max(owner.slurring, 30) + owner.chem_effects[CE_PAINKILLER] = 0 + owner.stamina = 0 + if(MOVING_QUICKLY(owner)) + owner.set_moving_slowly() diff --git a/code/modules/augment/active/popout_shotgun.dm b/code/modules/augment/active/popout_shotgun.dm new file mode 100644 index 00000000000..9cc8840ccd6 --- /dev/null +++ b/code/modules/augment/active/popout_shotgun.dm @@ -0,0 +1,33 @@ +/obj/item/organ/internal/augment/active/simple/popout_shotgun + name = "pop-out shotgun" + desc = "A galvanized steel mechanism that replaces most of the flesh below the elbow. Using the arm's natural range of motion as a hinge, it can be flicked open to reveal a 12-gauge shotgun with room for a single shell." + action_button_name = "Deploy shotgun" + icon_state = "circuit" + allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM) + holding_type = /obj/item/gun/projectile/shotgun/popout + origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3, TECH_ESOTERIC = 4) + deploy_sound = 'sound/weapons/guns/interaction/rifle_boltback.ogg' + retract_sound = 'sound/weapons/guns/interaction/rifle_boltforward.ogg' + discoverable = TRUE + +/obj/item/gun/projectile/shotgun/popout + name = "pop-out shotgun" + desc = "A specialized 12-gauge shotgun concealed in the forearm. A deadly surprise." + icon = 'icons/obj/augment.dmi' + icon_state = "circuit" + item_state = "coilgun" + max_shells = 1 + w_class = ITEM_SIZE_HUGE + force = 5 + obj_flags = OBJ_FLAG_CONDUCTIBLE + caliber = CALIBER_SHOTGUN + load_method = SINGLE_CASING + ammo_type = /obj/item/ammo_casing/shotgun/pellet + handle_casings = EJECT_CASINGS + load_sound = 'sound/weapons/guns/interaction/shotgun_instert.ogg' + has_safety = FALSE // No brakes on this train baby + unacidable = TRUE + +/obj/item/gun/projectile/shotgun/popout/check_accidents(mob/living/user, message, skill_path, fail_chance, no_more_fail, factor) + // The pop-out shotgun is a very unique type of gun and doesn't function like a normal one. We do this to prevent people blowing themselves away if they're not weapons-trained + return FALSE diff --git a/code/modules/augment/augment.dm b/code/modules/augment/augment.dm index 08e920d7ae2..a592fdd9871 100644 --- a/code/modules/augment/augment.dm +++ b/code/modules/augment/augment.dm @@ -8,7 +8,8 @@ var/list/allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM) default_action_type = /datum/action/item_action/organ/augment var/descriptor = "" - var/known = TRUE + var/known = TRUE // Whether or not this augment displays on body scanners + var/discoverable = FALSE // Whether or not this augment will be discovered when checking a body part for wounds /obj/item/organ/internal/augment/Initialize() . = ..() @@ -23,6 +24,9 @@ /obj/item/organ/internal/augment/proc/onInstall() return +/obj/item/organ/internal/augment/proc/onRoundstart() + return + /obj/item/organ/internal/augment/removed(var/mob/living/user, var/drop_organ=1) onRemove() ..() @@ -46,28 +50,28 @@ if(organ_tag == BP_AUGMENT_L_LEG) parent_organ = BP_L_LEG - descriptor = "left leg." + descriptor = "left leg" else if(organ_tag == BP_AUGMENT_R_LEG) parent_organ = BP_R_LEG - descriptor = "right leg." + descriptor = "right leg" else if(organ_tag == BP_AUGMENT_L_HAND) parent_organ = BP_L_HAND - descriptor = "left hand." + descriptor = "left hand" else if(organ_tag == BP_AUGMENT_R_HAND) parent_organ = BP_R_HAND - descriptor = "right hand." + descriptor = "right hand" else if(organ_tag == BP_AUGMENT_L_ARM) parent_organ = BP_L_ARM - descriptor = "left arm." + descriptor = "left arm" else if(organ_tag == BP_AUGMENT_R_ARM) parent_organ = BP_R_ARM - descriptor = "right arm." + descriptor = "right arm" else if(organ_tag == BP_AUGMENT_HEAD) parent_organ = BP_HEAD - descriptor = "head." + descriptor = "head" else if(organ_tag == BP_AUGMENT_CHEST_ACTIVE || organ_tag == BP_AUGMENT_CHEST_ARMOUR) parent_organ = BP_CHEST - descriptor = "chest." + descriptor = "chest" /obj/item/organ/internal/augment/examine(mob/user, distance) diff --git a/code/modules/augment/cbm.dm b/code/modules/augment/cbm.dm new file mode 100644 index 00000000000..d07d7f7eea4 --- /dev/null +++ b/code/modules/augment/cbm.dm @@ -0,0 +1,188 @@ +#define CBM_STEP_CUT_OPEN "cut open" +#define CBM_STEP_FRACTURE "fracture" +#define CBM_STEP_INSTALL_AUGMENT "install augment" +#define CBM_STEP_SEAL "seal" + +/obj/item/device/compact_bionic_module + name = "compact bionic module" + desc = "An oblong box with an irregular shape and a seam running down the center." + icon = 'icons/obj/surgery.dmi' + icon_state = "compact_bionic_module" + w_class = ITEM_SIZE_NORMAL + origin_tech = list(TECH_DATA = 3, TECH_ESOTERIC = 3) + var/obj/item/organ/internal/augment/loaded_augment + var/augment_type + var/working + /// If not false, then installation will be instant and cause no damage; useful for admins or debugging + var/cbm_debug = FALSE + +/obj/item/device/compact_bionic_module/Initialize() + . = ..() + if (!isnull(augment_type)) + loaded_augment = new augment_type(src) + +/obj/item/device/compact_bionic_module/examine(mob/user) + . = ..() + if (isobserver(user) || (user.mind && user.mind.special_role != null) || user.skill_check(SKILL_DEVICES, SKILL_PROF)) + to_chat(user, "This is a compact bionic module - an illicit, one-use augment installer. It can be used even by individuals with no medical knowledge.") + to_chat(user, SPAN_DANGER("This device does NOT come with its own painkillers or anesthetic. Installing without painkillers is theoretically possible, but dangerous and very traumatic.")) + // Label is keyed based on "is it safe from detection?" + // Two Y's means it's fully hidden + // One N means that either it can be scanned or that it can be discovered via limb inspection + // Two N's means that it can be scanned as well as discovered through limb inspection + if (!isnull(loaded_augment)) + to_chat(user, "A machine-printed label on the side reads \ + '[SPAN_NOTICE(uppertext(loaded_augment.name))] - [loaded_augment.known ? "N" : "Y"][loaded_augment.discoverable ? "N" : "Y"]'.") + else + to_chat(user, "It seems to be empty.") + if (is_admin(user) && !user.is_stealthed()) + to_chat(user, SPAN_NOTICE("ADMIN NOTICE: If you'd like to make installation instant, varedit the cbm_debug variable to anything but 0.")) + +/obj/item/device/compact_bionic_module/attackby(obj/item/W, mob/living/user) + if (isScrewdriver(W) && loaded_augment) + return loaded_augment.attackby(W, user) + else if (isCrowbar(W) && loaded_augment) + user.visible_message( + SPAN_WARNING("\The [user] starts prying out \the [loaded_augment] from \the [src]!"), + SPAN_WARNING("You start prying out \the [loaded_augment] from \the [src]..."), + SPAN_WARNING("You hear metal creaking.") + ) + playsound(user, 'sound/items/Crowbar.ogg', 50, TRUE) + if (!do_after(user, 10 SECONDS, src) || !loaded_augment) + return + user.visible_message( + SPAN_WARNING("\The [user] levers \the [loaded_augment] out of \the [src]."), + SPAN_WARNING("You permanently remove \the [src]'s [loaded_augment.name]."), + SPAN_WARNING("You hear a clunk.") + ) + playsound(user, 'sound/items/Deconstruct.ogg', 50, TRUE) + loaded_augment.forceMove(get_turf(user)) + loaded_augment = null + ..() + +/obj/item/device/compact_bionic_module/attack_self(mob/living/user) + . = ..() + if (working) + return + if (!loaded_augment) + to_chat(user, SPAN_WARNING("\The [src] has no augment, or has been used up.")) + return + if (!ishuman(user)) + to_chat(user, SPAN_WARNING("\The [src] detects an incompatible physiology and refuses to activate.")) + return + if (!user.buckled && !user.lying && !cbm_debug) + to_chat(user, SPAN_WARNING("You must be buckled or lying to use the CBM.")) + return + else + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/affected = H.get_organ(loaded_augment.parent_organ) + var/beep_boop = BP_IS_ROBOTIC(affected) + if (!affected) + to_chat (user, SPAN_WARNING("\The [src] detects no valid operation spot.")) + return + else if(beep_boop && !(loaded_augment.augment_flags != AUGMENTATION_MECHANIC)) + to_chat(user, SPAN_WARNING("The [loaded_augment.name] cannot function within a mechanical part.")) + return + else if (!beep_boop && !(loaded_augment.augment_flags & AUGMENTATION_ORGANIC)) + to_chat(user, SPAN_WARNING("The [loaded_augment.name] cannot function within an organic part.")) + return + else if (affected.get_damage() >= affected.max_damage * 0.25 && !cbm_debug) + to_chat(user, SPAN_WARNING("Your [affected.name] is too damaged to safely use a CBM on.")) + return + else + var/obj/item/organ/internal/I = H.internal_organs_by_name[loaded_augment.organ_tag] + if(I && (I.parent_organ == loaded_augment.parent_organ)) + to_chat(H, SPAN_WARNING("You can only have one [loaded_augment.organ_tag].")) + return + operate(user, affected) + +/obj/item/device/compact_bionic_module/proc/operate(mob/living/carbon/human/user, obj/item/organ/external/affected) + if (!cbm_debug) + working = TRUE + to_chat(user, SPAN_WARNING("\icon[src] Commencing operation. Please remain still.")) + user.visible_message( + SPAN_WARNING("\The [user] places \the [src] against \his [affected.name]..."), + SPAN_DANGER("You push \the [src] against your [affected.name] and activate it...") + ) + if (!do_after(user, 2 SECONDS)) + working = FALSE + return + var/robot_part = BP_IS_ROBOTIC(affected) + if (!robot_part) + user.visible_message( + SPAN_DANGER("\The [src] whirrs and begins slicing open \the [user]'s [affected.name]!"), + SPAN_DANGER("\The [src] viciously cuts open your [affected.name] and starts operating on it!") + ) + user.custom_pain("Your [affected.name] feels like it's being torn apart!", 160) // Massive pain because of the trauma of installation + else + user.visible_message( + SPAN_DANGER("\The [src] whirrs and begins operating on \the [user]'s [affected.name]!"), + SPAN_DANGER("\The [src] pries open your [affected.name] and starts rooting through its components.") + ) + playsound(user, 'sound/items/electronic_assembly_emptying.ogg', 50, TRUE) + affected.createwound(CUT, affected.min_broken_damage / 2, 1) + affected.clamp_organ() + affected.open_incision() + affected.fracture() + if (!do_after(user, 8 SECONDS)) + working = FALSE + to_chat(user, SPAN_WARNING("\The [src] falls away from your [affected.name], leaving behind a mangled mess.")) + return + user.visible_message( + SPAN_WARNING("\The [src] inserts something into \the [user]'s [affected.name]!"), + SPAN_WARNING("\The [src] starts installing \the [loaded_augment] into your [affected.name]!") + ) + if (!robot_part) + user.custom_pain("You feel something moving around inside your [affected.name]!", 160) + playsound(user, 'sound/effects/squelch1.ogg', 25, TRUE) + else + playsound(user, 'sound/items/jaws_pry.ogg', 50, TRUE) + if (!do_after(user, 8 SECONDS)) + working = FALSE + to_chat(user, SPAN_WARNING("\The [src] falls away from your [affected.name], leaving behind a mangled mess.")) + return + loaded_augment.forceMove(user) + loaded_augment.replaced(user, affected) + loaded_augment = null + if (!cbm_debug) + to_chat(user, SPAN_WARNING("\The [src] seals up your [affected.name] and powers down.")) + affected.status &= ~ORGAN_BROKEN + affected.stage = 0 + affected.update_wounds() + var/datum/wound/W = affected.get_incision() + if(istype(W)) + W.close() + if(affected.clamped()) + affected.remove_clamps() + affected.update_wounds() + to_chat(user, SPAN_WARNING("\icon[src] Operation complete.")) + playsound(src, 'sound/machines/ping.ogg', 50, FALSE) + working = FALSE + + + +/obj/item/device/compact_bionic_module/iatric_monitor + augment_type = /obj/item/organ/internal/augment/active/iatric_monitor/hidden + +/obj/item/device/compact_bionic_module/wrist_blade + augment_type = /obj/item/organ/internal/augment/active/simple/wrist_blade + +/obj/item/device/compact_bionic_module/popout_shotgun + augment_type = /obj/item/organ/internal/augment/active/simple/popout_shotgun + +/obj/item/device/compact_bionic_module/nerve_dampeners + augment_type = /obj/item/organ/internal/augment/active/nerve_dampeners + +/obj/item/device/compact_bionic_module/internal_air_system + augment_type = /obj/item/organ/internal/augment/active/internal_air_system/hidden + +/obj/item/device/compact_bionic_module/adaptive_binoculars + augment_type = /obj/item/organ/internal/augment/active/simple/equip/adaptive_binoculars/hidden + +/obj/item/device/compact_bionic_module/engineering_toolset + augment_type = /obj/item/organ/internal/augment/active/polytool/engineer + +#undef CBM_STEP_CUT_OPEN +#undef CBM_STEP_FRACTURE +#undef CBM_STEP_INSTALL_AUGMENT +#undef CBM_STEP_SEAL diff --git a/code/modules/augment/equip.dm b/code/modules/augment/equip.dm new file mode 100644 index 00000000000..341687df1dc --- /dev/null +++ b/code/modules/augment/equip.dm @@ -0,0 +1,18 @@ +/// Subtype of equipment modules that attempts to equip its item to a specified clothing slot. +/obj/item/organ/internal/augment/active/simple/equip + var/equip_slot + deploy_sound = 'sound/items/helmet_close.ogg' + retract_sound = 'sound/items/helmet_open.ogg' + +/obj/item/organ/internal/augment/active/simple/equip/deploy() + if(owner.equip_to_slot_if_possible(holding, equip_slot)) + GLOB.item_unequipped_event.register(holding, src, /obj/item/organ/internal/augment/active/simple/proc/holding_dropped ) + owner.visible_message( + SPAN_WARNING("\The [owner] extends \his [holding.name] from \his [limb.name]."), + SPAN_NOTICE("You extend your [holding.name] from your [limb.name].") + ) + if (deploy_sound) + playsound(owner, deploy_sound, 30) + return TRUE + else + to_chat(owner, SPAN_WARNING("Your [holding.name] fails to deploy.")) diff --git a/code/modules/augment/passive/armor.dm b/code/modules/augment/passive/armor.dm index dad640529b8..8902ec2bc8d 100644 --- a/code/modules/augment/passive/armor.dm +++ b/code/modules/augment/passive/armor.dm @@ -3,5 +3,6 @@ allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) icon_state = "armor-chest" desc = "A flexible composite mesh designed to prevent tearing and puncturing of underlying tissue." + discoverable = TRUE var/brute_mult = 0.8 - var/burn_mult = 1 \ No newline at end of file + var/burn_mult = 1 diff --git a/code/modules/augment/passive/boost/muscle.dm b/code/modules/augment/passive/boost/muscle.dm index 596e3fe93cf..a3245d77404 100644 --- a/code/modules/augment/passive/boost/muscle.dm +++ b/code/modules/augment/passive/boost/muscle.dm @@ -8,6 +8,7 @@ allowed_organs = list(BP_AUGMENT_R_LEG, BP_AUGMENT_L_LEG) icon_state = "muscule" desc = "Nanofiber tendons powered by an array of actuators increase the speed and agility of the user. You may want to install these in pairs to see a result." + discoverable = TRUE var/obj/item/organ/internal/augment/boost/muscle/other //we need two for these /obj/item/organ/internal/augment/boost/muscle/proc/get_acrobatics_modifier() diff --git a/code/modules/augment/passive/fluff.dm b/code/modules/augment/passive/fluff.dm new file mode 100644 index 00000000000..c489d3448a2 --- /dev/null +++ b/code/modules/augment/passive/fluff.dm @@ -0,0 +1,86 @@ +// These augments are purely for roleplay purposes. They either have a negligible in-game effect, or none at all. + +/obj/item/organ/internal/augment/fluff/head/circadian_conditioner + name = "circadian conditioner" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "booster" + desc = "A small brain implant that carefully regulates the output of certain hormones to assist in controlling the sleep-wake cycle of its owner. May be an effective counter to insomnia, jet lag, and late-night work shifts." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/organ/internal/augment/fluff/head/codex_access + name = "\improper Codex access chip" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "booster" + desc = "A neuro-memetic implant or retinal chip used to grant realtime access to the Codex - a distributed encyclopedia of sorts, with editorial offices based in Venusian orbit - either via a server connection or local backups of relevant information." + origin_tech = list(TECH_DATA = 2, TECH_DATA = 2) + +/obj/item/organ/internal/augment/fluff/head/neurostimulator_implant + name = "neurostimulator implant" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "booster" + desc = "An expensive implant attached to the brain's cortex, this network of signal relays sees mixed success as a treatment to lessen the impact of neurological problems such as Parkinson's disease, epilepsy, and paralysis. It can't prevent or heal brain damage on its own, and simply serves to make the life of its owner easier." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/organ/internal/augment/fluff/head/pain_assistant + name = "pain assistant" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "booster" + desc = "This brain implant blocks the impulses of certain nerves - usually tailored between individuals - and is used to lessen chronic pain from worn joints, headaches, and so on. It does nothing for pain that it isn't specifically tuned to handle, and is ineffective against anything stronger than a tummyache." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/organ/internal/augment/fluff/head/genetic_backup + name = "genetic backup" + allowed_organs = list(BP_AUGMENT_HEAD) + icon_state = "booster" + desc = "This implant is a compact and resilient solid-state drive. It does nothing on its own, but contains the complete DNA sequence of its owner - whether it be to aid in medical treatment, serve for research purposes, or even be used as a template for vat-grown humans in the future." + // This is deliberately kept available for full body prosthetic users; some FBPs may want to maintain a genetic backup of themselves for sentimental reasons + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/organ/internal/augment/fluff/head/data_chip + name = "data chip" + allowed_organs = list(BP_AUGMENT_HEAD) + icon = 'icons/obj/surgery.dmi' + icon_state = "cell" + desc = "These durable chips can contain nonspecific data for a variety of potential uses, such as record lookups, work portfolios, authentication codes, contact information, and more. They're useful for carrying information without needing extraneous hardware, and some even see use as high-tech dog tags for private security firms or mercenary coalitions." + origin_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) + + +// The augments below this line fit in the chest slot, rather than the head slot. We differentiate them in type to let people pick them independently in loadouts +/obj/item/organ/internal/augment/fluff/chest/ups_battery + name = "UPS battery" + allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + icon = 'icons/obj/surgery.dmi' + icon_state = "cell" + desc = "A UPS - or uninterruptible power supply - hooked into your brain or motherboard. It provides no protection from power loss on its own, but provides enough surge protection and emergency power to (hopefully) preserve your personality matrix in the event of a critical failure." + augment_flags = AUGMENTATION_MECHANIC + origin_tech = list(TECH_DATA = 2, TECH_POWER = 2) + + +/obj/item/organ/internal/augment/fluff/chest/skeletal_bracing + name = "skeletal bracing" + allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + icon_state = "armor-chest" + desc = "Mechanical hinges and springs made from titanium or some other bio-compatible metal reinforce your joints, generally making strenuous activity less painful for you and allowing you to carry weight that would normally be unbearable. It provides no increase on strength on its own, unless you have weak bones to begin with." + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2) + discoverable = TRUE // Physical objects around the joints + +/obj/item/organ/internal/augment/fluff/chest/ultraviolet_shielding + name = "ultraviolet shielding" + allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + icon_state = "armor-chest" + desc = "Some parts of your epidermis have been replaced with a bio-engineered substitute that's resistant to harmful solar radiation - a common factor in the lives of spacers or inhabitants of planets with a weak magnetosphere." + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + augment_flags = AUGMENTATION_ORGANIC + discoverable = TRUE // Skin would feel abnormal - maybe smooth/stiff or etc + +/obj/item/organ/internal/augment/fluff/chest/recycler_suite + name = "recycler suite" + allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + icon_state = "armor-chest" + desc = "In extreme environments where natural resources are limited or even nonexistent, it may be prudent to recycle nutrients and fluids the body would usually discard. This system reclaims any usable material in the digestive tract that would otherwise be lost to waste." + augment_flags = AUGMENTATION_ORGANIC + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + discoverable = TRUE // Intrusive - pipes and tubes etc. exist that wouldn't otherwise be there diff --git a/code/modules/augment/simple.dm b/code/modules/augment/simple.dm index a435bedced7..c8d3243f9ac 100644 --- a/code/modules/augment/simple.dm +++ b/code/modules/augment/simple.dm @@ -2,6 +2,8 @@ /obj/item/organ/internal/augment/active/simple var/obj/item/holding = null var/holding_type = null + var/deploy_sound + var/retract_sound /obj/item/organ/internal/augment/active/simple/Initialize() . = ..() @@ -36,9 +38,12 @@ if(owner.equip_to_slot_if_possible(holding, slot)) GLOB.item_unequipped_event.register(holding, src, /obj/item/organ/internal/augment/active/simple/proc/holding_dropped ) owner.visible_message( - SPAN_WARNING("[owner] extends \his [holding.name] from [limb]."), - SPAN_NOTICE("You extend your [holding.name] from [limb].") + SPAN_WARNING("\The [owner] extends \his [holding.name] from \his [limb.name]."), + SPAN_NOTICE("You extend your [holding.name] from your [limb.name].") ) + if (deploy_sound) + playsound(owner, deploy_sound, 30) + return TRUE /obj/item/organ/internal/augment/active/simple/proc/retract() if(holding.loc == src) @@ -47,12 +52,15 @@ if(ismob(holding.loc) && holding.loc == owner) var/mob/M = holding.loc if(!M.drop_from_inventory(holding, src)) - to_chat(owner, "\the [holding.name] fails to retract.") + to_chat(owner, "\The [holding.name] fails to retract.") return M.visible_message( - SPAN_WARNING("[M] retracts \his [holding.name] into [limb]."), - SPAN_NOTICE("You retract your [holding.name] into [limb].") + SPAN_WARNING("\The [M] retracts \his [holding.name] into \his [limb.name]."), + SPAN_NOTICE("You retract your [holding.name] into your [limb.name].") ) + if (retract_sound) + playsound(owner, retract_sound, 30) + return TRUE @@ -64,6 +72,7 @@ deploy() else //retract item retract() + owner.update_action_buttons() /obj/item/organ/internal/augment/active/simple/can_activate() if(..()) @@ -71,4 +80,4 @@ to_chat(owner, SPAN_WARNING("The device is damaged and fails to deploy")) return FALSE return TRUE - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/client/preference_setup/loadout/lists/augments.dm b/code/modules/client/preference_setup/loadout/lists/augments.dm new file mode 100644 index 00000000000..59f1dc1cb01 --- /dev/null +++ b/code/modules/client/preference_setup/loadout/lists/augments.dm @@ -0,0 +1,47 @@ +/datum/gear/augment + sort_category = "Augments" + category = /datum/gear/augment + +/datum/gear/augment/minor_head + display_name = "minor augments selection (head)" + description = "A minor augment with no in-game effects." + cost = 1 + path = /obj/item/organ/internal/augment/fluff/head + flags = GEAR_HAS_SUBTYPE_SELECTION + +/datum/gear/augment/minor_chest + display_name = "minor augments selection (chest)" + description = "A minor augment with no in-game effects." + cost = 1 + path = /obj/item/organ/internal/augment/fluff/chest + flags = GEAR_HAS_SUBTYPE_SELECTION + +/datum/gear/augment/corrective_lenses + display_name = "corrective lenses (head)" + cost = 2 + path = /obj/item/organ/internal/augment/active/simple/equip/corrective_lenses + +/datum/gear/augment/leukocyte_breeder + display_name = "leukocyte breeder (chest)" + cost = 4 + path = /obj/item/organ/internal/augment/active/leukocyte_breeder + +/datum/gear/augment/glare_dampeners + display_name = "glare dampeners (head)" + cost = 3 + path = /obj/item/organ/internal/augment/active/simple/equip/glare_dampeners + +/datum/gear/augment/integrated_health_hud + display_name = "integrated health HUD (head)" + cost = 4 + path = /obj/item/organ/internal/augment/active/hud/health + +/datum/gear/augment/integrated_security_hud + display_name = "integrated security HUD (head)" + cost = 5 + path = /obj/item/organ/internal/augment/active/hud/security + +/datum/gear/augment/integrated_janitor_hud + display_name = "integrated filth HUD (head)" + cost = 4 + path = /obj/item/organ/internal/augment/active/hud/janitor diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index 6f06dbc0a1d..350d1d397cb 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -378,12 +378,39 @@ var/list/gear_datums = list() var/obj/item/item = spawn_item(H, H, metadata) item.add_fingerprint(H) - var/atom/placed_in = H.equip_to_storage(item) - if(placed_in) - to_chat(H, "Placing \the [item] in your [placed_in.name]!") - else if(H.equip_to_appropriate_slot(item)) - to_chat(H, "Placing \the [item] in your inventory!") - else if(H.put_in_hands(item)) - to_chat(H, "Placing \the [item] in your hands!") + // Roundstart augments require special handling in order to properly install + // Putting this in "spawn_on_mob" requires overriding a bunch of logic, so we hook into here instead + if (istype(item, /obj/item/organ/internal/augment)) + var/obj/item/organ/internal/augment/A = item + var/obj/item/organ/external/affected = H.get_organ(A.parent_organ) + if (!affected) + to_chat(H, SPAN_WARNING("Failed to install \the [A]!")) + QDEL_NULL(A) + else + var/beep_boop = BP_IS_ROBOTIC(affected) + var/obj/item/organ/internal/I = H.internal_organs_by_name[A.organ_tag] + if (!(A.augment_flags & AUGMENTATION_MECHANIC) && beep_boop) + to_chat(H, SPAN_WARNING("\The [A] cannot be installed in a robotic part!")) + QDEL_NULL(A) + else if (!(A.augment_flags & AUGMENTATION_ORGANIC) && !beep_boop) + to_chat(H, SPAN_WARNING("\The [A] cannot be installed in an organic part!")) + QDEL_NULL(A) + else if(I && (I.parent_organ == A.parent_organ)) + to_chat(H, SPAN_WARNING("\The [A] could not be installed because you can only have one [A.organ_tag] at a time.")) + QDEL_NULL(A) + else + to_chat(H, SPAN_NOTICE("Installing \the [A] in your [affected.name]!")) + A.forceMove(H) + A.replaced(H, affected) + A.onRoundstart() + . = A else - to_chat(H, "Dropping \the [item] on the ground!") + var/atom/placed_in = H.equip_to_storage(item) + if(placed_in) + to_chat(H, SPAN_NOTICE("Placing \the [item] in your [placed_in.name]!")) + else if(H.equip_to_appropriate_slot(item)) + to_chat(H, SPAN_NOTICE("Placing \the [item] in your inventory!")) + else if(H.put_in_hands(item)) + to_chat(H, SPAN_NOTICE("Placing \the [item] in your hands!")) + else + to_chat(H, SPAN_DANGER("Dropping \the [item] on the ground!")) diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 2c31b49da08..e8ff13d313a 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -252,3 +252,29 @@ icon_state = "rwelding-g" item_state = "rwelding-g" tint = TINT_MODERATE + +/obj/item/clothing/glasses/glare_dampeners + name = "glare dampeners" + desc = "Synthetic lenses over the eyes, protecting from bright lights." + icon_state = "welding-g" + item_state = "welding-g" + use_alt_layer = TRUE + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY + +/obj/item/clothing/glasses/augment_binoculars + name = "adaptive binoculars" + desc = "Digital lenses covering the eyes, capable of zooming in on distant targets." + gender = PLURAL + icon_state = "thermal" + item_state = "glasses" + action_button_name = "Toggle zoom" + zoomdevicename = "lenses" + electric = TRUE + unacidable = TRUE + +/obj/item/clothing/glasses/augment_binoculars/attack_self(mob/user) + if(zoom) + unzoom(user) + else + zoom(user) diff --git a/code/modules/clothing/glasses/prescription.dm b/code/modules/clothing/glasses/prescription.dm index bfd9720d832..86fd5dd43cf 100644 --- a/code/modules/clothing/glasses/prescription.dm +++ b/code/modules/clothing/glasses/prescription.dm @@ -23,4 +23,10 @@ desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." icon_state = "gglasses" item_state = "gglasses" - body_parts_covered = 0 \ No newline at end of file + body_parts_covered = 0 + +/obj/item/clothing/glasses/prescription/augment + name = "corrective lenses" + desc = "The most expensive prescription on this side of Sol." + item_flags = ITEM_FLAG_PHORONGUARD + unacidable = TRUE diff --git a/code/modules/codex/entries/augments.dm b/code/modules/codex/entries/augments.dm new file mode 100644 index 00000000000..6196cab00c8 --- /dev/null +++ b/code/modules/codex/entries/augments.dm @@ -0,0 +1,40 @@ +/datum/codex_entry/cbm + associated_paths = list(/obj/item/device/compact_bionic_module) + display_name = "Compact Bionic Module/CBM" + antag_text = "

          Compact bionic modules are one-use installers pre-loaded with medical software and an augment. Due to safety concerns and their lack of licensing or registration, they are outlawed in most Sol Central Government territories, and are considered contraband. Activating it in-hand can be used to install an augment in the configured location by first tearing open an incision, then installing the augment, and finally sealing the wound.

          \ +\ +

          To use:

          \ +
            \ +
          1. Optional: Use a screwdriver on the CBM to configure the augment's installation location, if applicable.\ +
          2. Firmly buckle yourself to a solid object such as a bed or chair.\ +
          3. Ensure that you are firmly secured, then activate the CBM in your hand.\ +
          4. The CBM will perform a rapid surgical procedure on you. Be as still as possible during this time - it will deactivate if it is interrupted.\ +
          5. When the operation is complete, collect the spent CBM for disposal.\ +
          \ +\ +

          CBMs don't come with their own painkillers, and like any surgery operation, it will be extremely painful without them. It will do its best to seal the wounds it creates, but in the event of an interruption, you will likely be left with an open surgical incision.

          \ +\ +

          You can use a crowbar to lever the augment out of the CBM, but doing so will render the CBM itself permanently useless. This might be useful if you have a collaborator that can safely install the augment in a more desirable way.

          " + +/datum/codex_entry/leukocyte_breeder + associated_paths = list(/obj/item/organ/internal/augment/active/leukocyte_breeder) + display_name = "Leukocyte Breeder" + lore_text = "

          These stimulators were originally developed to serve as both a control hub and a booster for white blood cells. It regulates the production and release of activation proteins to allow for more accurate threat recognition and immune response, while also ensuring the body has enough leukocytes to go around.

          \ +\ +

          It sees the most use in those with autoimmune disorders, or for people with a naturally weak immune system that typically struggles to fight off infections and disease. It's also popular among boosters and bionic aficionados due to its ability to lessen the body's rejection of new additions to it.

          \ +\ +

          The body requires some time to 'break in' the augment due to rapid changes in the immune system, typically characterized by sneezing, coughing, fevers, and other symptoms of a minor cold. These side effects fade quickly, from anywhere between a few minutes to a day or two. Once the body has broken in the system, deactivating it will cause similar symptoms, as it has to adapt to the sudden lack of supplements it previously had.

          " + +/datum/codex_entry/internal_air_system + associated_paths = list(/obj/item/organ/internal/augment/active/internal_air_system) + display_name = "Internal Air System" + lore_text = "

          This augment features a sophisticated air filtration system that activates based on the respiratory activity of its host, to ensure that poisons are avoided. It slowly replenishes its internal supply based on breathable gas in the environment - for most species, that's oxygen - and can be used as an internals tank like any other source if nothing else is available.

          \ +\ +

          These systems are niche, but see use among miners, spacers, and other people that might suddenly find themselves requiring a source of clean, breathable air. The sensation of having your lungs fill without breathing can take some time to get used to, though.

          " + +/datum/codex_entry/nerve_dampeners + associated_paths = list(/obj/item/organ/internal/augment/active/nerve_dampeners) + display_name = "Nerve Dampeners" + lore_text = "

          Selective control of synaptic signals combined with adjustments in brain chemistry can be used to effectively mitigate pain under strenuous conditions. Its use is characterized by a tingling sensation across the entire body, which is unpleasant but much more manageable than whatever pain it might be canceling out.

          \ +\ +

          The body isn't made to go without these feelings for long without a chemical aid, and so after it finishes a cycle, it is followed by a wave of tiredness and fatigue. Using it too often may lead to minor brain damage, or even blacking out. Because of its haphazard methodology and unsafe mode of operation, it doesn't see much use in SolGov space.

          " diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 451dc222ab9..8279ccf2cab 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -326,8 +326,13 @@ var/mob/living/carbon/human/H = M var/obj/item/clothing/glasses/G = H.glasses var/obj/item/card/id/ID = M.GetIdCard() + var/obj/item/organ/internal/augment/active/hud/AUG + for (var/obj/item/organ/internal/augment/active/hud/A in H.internal_organs) // Check for installed and active HUD implants + if (A.hud_type & hudtype) + AUG = A + break - return (istype(G) && ((G.hud_type & hudtype) || (G.hud && (G.hud.hud_type & hudtype)))) && G.check_access(ID) + return ((istype(G) && ((G.hud_type & hudtype) || (G.hud && (G.hud.hud_type & hudtype)))) && G.check_access(ID)) || AUG?.active && AUG.check_access(ID) else if(istype(M, /mob/living/silicon/robot)) var/mob/living/silicon/robot/R = M for(var/obj/item/borg/sight/sight in list(R.module_state_1, R.module_state_2, R.module_state_3)) diff --git a/code/modules/modular_computers/file_system/reports/crew_record.dm b/code/modules/modular_computers/file_system/reports/crew_record.dm index 2dc53bc18a0..8b4d7bee614 100644 --- a/code/modules/modular_computers/file_system/reports/crew_record.dm +++ b/code/modules/modular_computers/file_system/reports/crew_record.dm @@ -67,7 +67,11 @@ GLOBAL_VAR_INIT(arrest_security_status, "Arrest") if(H) if(H.isSynthetic()) - set_implants("Fully synthetic body") + var/organ_data = list("Fully synthetic body") + for(var/obj/item/organ/internal/augment/A in H.internal_organs) + organ_data += "installed augment - [A.name]" + if (LAZYLEN(organ_data)) + set_implants(jointext(organ_data, "\[*\]")) else var/organ_data = list("\[*\]") for(var/obj/item/organ/external/E in H.organs) @@ -77,7 +81,10 @@ GLOBAL_VAR_INIT(arrest_security_status, "Arrest") if(BP_IS_ASSISTED(I)) organ_data += I.get_mechanical_assisted_descriptor() else if (BP_IS_ROBOTIC(I)) - organ_data += "robotic [I.name] prosthetic" + if (!istype(I, /obj/item/organ/internal/augment)) // Differentiate between augments and prosthetics + organ_data += "robotic [I.name] prosthetic" + else + organ_data += "installed augment - [I.name]" set_implants(jointext(organ_data, "\[*\]")) // Security record @@ -253,4 +260,4 @@ FIELD_LONG("Exploitable Information", antagRecord, access_syndicate, access_synd #undef FIELD_LONG #undef FIELD_NUM #undef FIELD_LIST -#undef FIELD_LIST_EDIT \ No newline at end of file +#undef FIELD_LIST_EDIT diff --git a/code/modules/organs/external/diagnostics.dm b/code/modules/organs/external/diagnostics.dm index 4c2547fec87..89390f0c9f3 100644 --- a/code/modules/organs/external/diagnostics.dm +++ b/code/modules/organs/external/diagnostics.dm @@ -151,6 +151,11 @@ else to_chat(user, "The [encased ? encased : "bones in the [name]"] seem to be fine.") + for (var/obj/item/organ/internal/augment/A in internal_organs) // Locate any non-concealed augments + if (A.discoverable) + to_chat(user, SPAN_WARNING("You feel a foreign object inside of \the [owner]'s [name]!")) + owner.custom_pain("Your [name] hurts as your [A.name] is jostled inside it.", 20, affecting = src) + break if(status & ORGAN_TENDON_CUT) to_chat(user, "The tendons in [name] are severed!") if(dislocated == 2) diff --git a/code/modules/research/designs/designs_mechfab.dm b/code/modules/research/designs/designs_mechfab.dm index 84d30ce5909..74564604ea8 100644 --- a/code/modules/research/designs/designs_mechfab.dm +++ b/code/modules/research/designs/designs_mechfab.dm @@ -517,6 +517,63 @@ req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4, TECH_MATERIAL = 4, TECH_BIO = 3) id = "augment_wolverine" +/datum/design/item/mechfab/augment/armblade/wrist_blade + name = "Wrist blade" + build_path = /obj/item/organ/internal/augment/active/simple/wrist_blade + materials = list(MATERIAL_TITANIUM = 4000, MATERIAL_DIAMOND = 250) + req_tech = list(TECH_ESOTERIC = 4, TECH_COMBAT = 5, TECH_BIO = 3) + id = "augment_wristblade" + +/datum/design/item/mechfab/augment/armblade/popout_shotgun + name = "Pop-out shotgun" + build_path = /obj/item/organ/internal/augment/active/simple/popout_shotgun + materials = list(DEFAULT_WALL_MATERIAL = 5000, MATERIAL_SILVER = 500) + req_tech = list(TECH_ESOTERIC = 5, TECH_COMBAT = 6, TECH_BIO = 4) + id = "augment_popout_shotgun" + +/datum/design/item/mechfab/augment/corrective_lenses + name = "Corrective lenses" + build_path = /obj/item/organ/internal/augment/active/simple/equip/corrective_lenses + materials = list(DEFAULT_WALL_MATERIAL = 500, MATERIAL_GLASS = 1000) + req_tech = list(TECH_MATERIAL = 2) + id = "augment_corrective_lenses" + +/datum/design/item/mechfab/augment/glare_dampeners + name = "Glare dampeners" + build_path = /obj/item/organ/internal/augment/active/simple/equip/glare_dampeners + materials = list(DEFAULT_WALL_MATERIAL = 100, MATERIAL_GLASS = 2000) + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + id = "augment_glare_dampeners" + +/datum/design/item/mechfab/augment/adaptive_binoculars + name = "Adaptive binoculars" + build_path = /obj/item/organ/internal/augment/active/simple/equip/adaptive_binoculars + materials = list(MATERIAL_DIAMOND = 100, MATERIAL_GOLD = 100, MATERIAL_GLASS = 2000) + // We use decent requirements for this, because it allows someone to zoom in and grab ammo from an open container at the same time + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4, TECH_COMBAT = 4) + id = "augment_adaptive_binoculars" + +/datum/design/item/mechfab/augment/iatric_monitor + name = "Iatric monitor" + build_path = /obj/item/organ/internal/augment/active/iatric_monitor + materials = list(DEFAULT_WALL_MATERIAL = 100, MATERIAL_GLASS = 2000) + req_tech = list(TECH_BIO = 3) + id = "augment_iatric_monitor" + +/datum/design/item/mechfab/augment/internal_air_system + name = "Internal air system" + build_path = /obj/item/organ/internal/augment/active/internal_air_system + materials = list(DEFAULT_WALL_MATERIAL = 500, MATERIAL_GLASS = 2000, MATERIAL_DIAMOND = 100) + req_tech = list(TECH_MATERIAL = 5, TECH_BIO = 5) + id = "augment_internal_air_system" + +/datum/design/item/mechfab/augment/leukocyte_breeder + name = "Leukocyte breeder" + build_path = /obj/item/organ/internal/augment/active/leukocyte_breeder + materials = list(DEFAULT_WALL_MATERIAL = 2000, MATERIAL_GLASS = 1000) + req_tech = list(TECH_MAGNET = 4, TECH_DATA = 4, TECH_BIO = 6) + id = "augment_leukocyte_breeder" + /datum/design/item/mechfab/augment/engineering name = "Engineering toolset" build_path = /obj/item/organ/internal/augment/active/polytool/engineer diff --git a/icons/obj/augment.dmi b/icons/obj/augment.dmi index 5088479ec257b77767fed8a2729ab14ec077498a..a93fca634d04f1798c2040c06f8ca7281ad1271f 100644 GIT binary patch literal 7372 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 WEz<~|4*_o}L8^+H3Keo@LH`48&P^Qv literal 2302 zcmVC0001`P)t-sz`(#8 z8yzz_K|guHqoboyQBh_zF~KQ6Dia$=At%Yn$$NWyH9mV|C^U~&JLCWW|0@fK03^7C zFmgpI0000&LPAwlRZB}tmzS4{ik?YFTs1W{N=izCK>)^r0548%T9&)(>+9yY5`x6= z!okTYFCslfG%Gkb(z8KEM@OT6C1Yn#g@k71%WR#Omm_wMBsyU{xTSgc=D@(fk6-}l zodBU{0PyN{DgXcg0d!JMQvg8b*k%9#0MmL@Sad{Xb7OL8aCB*JZU6vyoQ;yf4uUWY zhR^IN7P+bs_3Fid#)G^98C#W_jV0TV@b(r-j0erQwawQ*?cb*SSe!2<u1n1GV z{J{6?4NH1dp`m2AWk~~gZ^D%LAOcXh5YE(EC|IyWq4QMASaNg1z!ZPL-fGguHcpbH zbt~C`4^h^1UUoXJX*wM+SgYrgl>voP#5*K9*&2< z(SZCJ6F~{WwqDsRc=;LC1{1&~_b8Br>*K{~`?uBL;u823B!`JWjde2MYoTC9O9TIL zEh*Cj0h+VpQ}>%nwF;y(hF)Nng<@xCz9g`$Zhe)7{^fzy%>Waor@%<6xmt7-kY-*0 zdgy`+NG((19CG8CFcizOU=2WiEeX^BWKdV9jMu0K$+Kd3-c1 zp!Qry02BgdpqGWZ|ErOZ`Dy?z@u7Q7&ieu+l%_GhFE@jR2}PkB90@g0^}nbyk!9I% z9A3KYP*WPFq^<&F(F|sx!ANfffP#!sz-tLhM}h5k+xjdt57gwqZkk4S5c>&U9wdZP zpfXBuE@GDcq#Y*2mLBu~wjOFB$ZkwqeS9zsF0rd9&gfZR0n}%u00CwvB8Bv-J*6r= zs||;QpP#gq$GPtFW0hQDp@~ck6cE#DXv3v%%MgB$z*N{)Ge`CzeG{mFGS6<cbk| z<$_M>`G*0<2a{y46foaxREe;);*0(CBbp&Gj7yjh^vY<{TF zf&U(xUj(ZVmXq*($N(-W_ugw<2EhMA^?_aC#yb+FETCx?4-FG)ZGmEWffv|sLNo&U z^MES=9RnB+O*t%l*{}}~FY-b&#EU$TB8sa)aLKj-djUIw_SAd}6Y29p~m{(+=_Ww8r(3#ylJg7gr{Py}d20%l3_xM=)$^ZmB zt^ZorovBt^j8kcK!cwB5H9v35Mx$3rr7o7)ITWJ^sHZA&%SN9Fk>7e}`f0 z&8YAHJsP4o`d-^vxWmAlDjLxRdy=64PvdBND-egX(~YlL5k>s}D7;qJt)f@03EvyO z{512x;9f9T5&euXo($D9-oO|5)`U~U3Zwc7zuSZQ&javZL#&?duj|!vSszp%96|vS ztyarb_51mG)%oCH0$Scy$-xB7F~=Np95Pb-NAb-a5?gML!ac6G%kOn`?gfU=3}1sY zL85q3k}|r(WuG=hE|I|Td7ap7wh{g!kKnk!mSxq$?>FJ#NW^CM@)BYILz74G46lvP z-u$gh;kX#y?0_KHMpi^|iW_`z>D|jl`KqbGH!nLcj;8>@ZnxP6JE_LKb%1Yn;1f&r zvfT}z9y|DM`{KiGY(ZT_K!bY)NU9~HkmAj5lX-UPQn+oQ+HRoAMCj3AQCht8;?hV; zahoz@neaTiOyLk0=Spq2TOoVzCNy#TSeAg7eq7?4Ovu25_xdRuhvC^dSTYL!RQo7! zcL$6;3*h*23n7-mo8J^}J?Xi%WME{SW;&uEj(1N^oEO54BsylmQ+Rutr<2FwM#dlTr|{|=7-^By zqPQV(h7^t=hi}M?$1ll;=$BJ?bxyqhQ+SGC#7h{dGKu4Vh! zKbb$_@2BUF`}+g%NB#Zek@EKkP>}HV2T+jk_tQtp-%lMWe?N76{}K21QwJ0O{)qYe z$pifTkC?wdfCBoxKJxqb!$!*APcLBPKEOng^7oU|$Nl}}k@EMGN6Nn+AVzEh&GFmB Yf5-Qcx*fA96aWAK07*qoM6N<$f(4yk{Qv*} diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 882c2c7b842978c1510859a726b254695a61ba1d..da78c889ccc7c1b781151128ab9622cad7c1c976 100644 GIT binary patch delta 17069 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 delta 16830 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 +

          Ilysen updated:

          +
            +
          • Added a whole smorgasbord of new augment options, which you can obtain in a variety of ways.
          • +
          • Traitors can access an Augments section in their uplink to buy CBMs - compact bionic modules that can be used to self-install augments with no medical skill. They are, however, very painful to use.
          • +
          • Added an Augments section to character loadout for minor augments.
          • +
          • Added a selection of fluff augments that do nothing mechanically, and exist purely for roleplay purposes. You can get them from roundstart loadouts.
          • +
          • Integrated HUD augments can now be picked at roundstart by appropriate roles.
          • +
          • Some augments can now be discovered by grab-inspecting the body part they're on.
          • +
          • Medical records now differentiate between roundstart prosthetics and roundstart augments.
          • +
          • Hidden augments are now fully hidden on body scanners instead of showing on the "internal organs" list as a mechanical prosthetic.
          • +
          • HUD augments now let you control medical status/security status/etc when examining another person.
          • +
          +

          Viiim, Rootoo updated:

          +
            +
          • Changes the recipe of the fizzy orange, alexander, and iced frappe, so that they don't overlap with the kira special, iced coffee, and stinger.
          • +

          Viim updated:

          • Changes traitor skills from three trained, one experienced, to two trained, one experienced, and one master.
          • diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 67ad467a365..a88ba30b6ec 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16918,6 +16918,28 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - maptweak: The crashed survival pod now spawns with plastic. - bugfix: The crashed survival pod now spawns the correct engineering tape roll (rather than pre-placed engineering tape) + Ilysen: + - rscadd: Added a whole smorgasbord of new augment options, which you can obtain + in a variety of ways. + - rscadd: Traitors can access an Augments section in their uplink to buy CBMs - + compact bionic modules that can be used to self-install augments with no medical + skill. They are, however, very painful to use. + - rscadd: Added an Augments section to character loadout for minor augments. + - rscadd: Added a selection of fluff augments that do nothing mechanically, and + exist purely for roleplay purposes. You can get them from roundstart loadouts. + - tweak: Integrated HUD augments can now be picked at roundstart by appropriate + roles. + - tweak: Some augments can now be discovered by grab-inspecting the body part they're + on. + - tweak: Medical records now differentiate between roundstart prosthetics and roundstart + augments. + - bugfix: Hidden augments are now fully hidden on body scanners instead of showing + on the "internal organs" list as a mechanical prosthetic. + - bugfix: HUD augments now let you control medical status/security status/etc when + examining another person. + Viiim, Rootoo: + - tweak: Changes the recipe of the fizzy orange, alexander, and iced frappe, so + that they don't overlap with the kira special, iced coffee, and stinger. Viim: - tweak: Changes traitor skills from three trained, one experienced, to two trained, one experienced, and one master. diff --git a/html/changelogs/AutoChangeLog-pr-30752.yml b/html/changelogs/AutoChangeLog-pr-30752.yml deleted file mode 100644 index f544be8d6da..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30752.yml +++ /dev/null @@ -1,21 +0,0 @@ -author: Ilysen -delete-after: true -changes: - - rscadd: Added a whole smorgasbord of new augment options, which you can obtain - in a variety of ways. - - rscadd: Traitors can access an Augments section in their uplink to buy CBMs - - compact bionic modules that can be used to self-install augments with no medical - skill. They are, however, very painful to use. - - rscadd: Added an Augments section to character loadout for minor augments. - - rscadd: Added a selection of fluff augments that do nothing mechanically, and - exist purely for roleplay purposes. You can get them from roundstart loadouts. - - tweak: Integrated HUD augments can now be picked at roundstart by appropriate - roles. - - tweak: Some augments can now be discovered by grab-inspecting the body part they're - on. - - tweak: Medical records now differentiate between roundstart prosthetics and roundstart - augments. - - bugfix: Hidden augments are now fully hidden on body scanners instead of showing - on the "internal organs" list as a mechanical prosthetic. - - bugfix: HUD augments now let you control medical status/security status/etc when - examining another person. diff --git a/html/changelogs/AutoChangeLog-pr-30762.yml b/html/changelogs/AutoChangeLog-pr-30762.yml deleted file mode 100644 index 4c50b3ee353..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30762.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Viiim, Rootoo -delete-after: true -changes: - - tweak: Changes the recipe of the fizzy orange, alexander, and iced frappe, so - that they don't overlap with the kira special, iced coffee, and stinger. From d211ecae0cd59461308e41289c4958319cb467bd Mon Sep 17 00:00:00 2001 From: Ilysen Date: Sun, 30 May 2021 08:48:33 -0400 Subject: [PATCH 060/246] Fixes voice changer cham --- code/modules/clothing/chameleon.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 0cdcc61b512..484c7af5baf 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -81,7 +81,7 @@ /obj/item/clothing/mask/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src,/datum/extension/chameleon/clothing,/obj/item/clothing/mask) /obj/item/clothing/glasses/chameleon name = "goggles" From eec8ae58b994f1ea8cae2783a6c029c49312fa06 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 30 May 2021 21:33:43 +0200 Subject: [PATCH 061/246] Automatic changelog generation for PR #30764 [ci skip] --- html/changelogs/AutoChangeLog-pr-30764.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30764.yml diff --git a/html/changelogs/AutoChangeLog-pr-30764.yml b/html/changelogs/AutoChangeLog-pr-30764.yml new file mode 100644 index 00000000000..4e1193d9da9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30764.yml @@ -0,0 +1,4 @@ +author: Ilysen +delete-after: true +changes: + - bugfix: You can now properly change the appearance of the modified gas mask. From a2bc8b006181cdb2d595a737bd35a6517419710c Mon Sep 17 00:00:00 2001 From: Ilysen Date: Sun, 30 May 2021 12:08:51 -0400 Subject: [PATCH 062/246] Fixes molotov lighting bug --- code/modules/detectivework/tools/rag.dm | 17 +++---- .../reagent_containers/food/drinks/bottle.dm | 45 ++++++++++--------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 252f9b47ed6..3675738c655 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -46,14 +46,15 @@ remove_contents(user) /obj/item/reagent_containers/glass/rag/attackby(obj/item/W, mob/user) - if(!on_fire && istype(W, /obj/item/flame)) - var/obj/item/flame/F = W - if(F.lit) - ignite() - if(on_fire) - visible_message("\The [user] lights [src] with [W].") - else - to_chat(user, "You manage to singe [src], but fail to light it.") + if(!on_fire && isflamesource(W)) + ignite() + if(on_fire) + user.visible_message( + SPAN_WARNING("\The [user] lights \the [src] with \the [W]!"), + SPAN_DANGER("You light \the [src]!") + ) + else + to_chat(user, SPAN_WARNING("You manage to singe \the [src], but it won't burn on its own.")) // Give a hint about needing fuel . = ..() update_name() diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index 03d42728c42..5ca6b0b1692 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm @@ -8,19 +8,19 @@ item_state = "broken_beer" //Generic held-item sprite until unique ones are made. force = 5 var/smash_duration = 5 //Directly relates to the 'weaken' duration. Lowered by armor (i.e. helmets) - var/isGlass = TRUE //Whether the 'bottle' is made of glass or not so that milk cartons dont shatter when someone gets hit by it + var/isGlass = TRUE //Whether the 'bottle' is made of glass or not so that milk cartons don't shatter when someone gets hit by it var/obj/item/reagent_containers/glass/rag/rag = null var/rag_underlay = "rag" -/obj/item/reagent_containers/food/drinks/bottle/New() - ..() +/obj/item/reagent_containers/food/drinks/bottle/Initialize() + . = ..() if (isGlass) unacidable = TRUE /obj/item/reagent_containers/food/drinks/bottle/Destroy() if(rag) - rag.forceMove(src.loc) + rag.forceMove(loc) rag = null return ..() @@ -30,18 +30,18 @@ if(isGlass && TT.thrower && TT.thrower.a_intent != I_HELP) if(TT.speed > throw_speed || smash_check(TT.dist_travelled)) //not as reliable as smashing directly if(reagents) - hit_atom.visible_message("The contents of \the [src] splash all over [hit_atom]!") + hit_atom.visible_message(SPAN_NOTICE("The contents of \the [src] splash all over \the [hit_atom]!")) reagents.splash(hit_atom, reagents.total_volume) - src.smash(loc, hit_atom) + smash(loc, hit_atom) /obj/item/reagent_containers/food/drinks/bottle/proc/smash_check(var/distance) if(!isGlass || !smash_duration) - return 0 + return var/list/chance_table = list(95, 95, 90, 85, 75, 60, 40, 15) //starting from distance 0 var/idx = max(distance + 1, 1) //since list indices start at 1 if(idx > chance_table.len) - return 0 + return return prob(chance_table[idx]) /obj/item/reagent_containers/food/drinks/bottle/proc/smash(var/newloc, atom/against = null) @@ -49,20 +49,21 @@ var/obj/item/broken_bottle/B = new /obj/item/broken_bottle(newloc) if(prob(33)) new/obj/item/material/shard(newloc) // Create a glass shard at the target's location! - B.icon_state = src.icon_state + B.icon_state = icon_state - var/icon/I = new('icons/obj/drinks.dmi', src.icon_state) + var/icon/I = new('icons/obj/drinks.dmi', icon_state) I.Blend(B.broken_outline, ICON_OVERLAY, rand(5), 1) I.SwapColor(rgb(255, 0, 220, 255), rgb(0, 0, 0, 0)) B.icon = I if(rag && rag.on_fire && isliving(against)) - rag.forceMove(loc) + var/turf/T = get_turf(loc) + rag.forceMove(T) var/mob/living/L = against L.IgniteMob() playsound(src, "shatter", 70, 1) - src.transfer_fingerprints_to(B) + transfer_fingerprints_to(B) qdel(src) return B @@ -71,7 +72,7 @@ if(!rag && istype(W, /obj/item/reagent_containers/glass/rag)) insert_rag(W, user) return - if(rag && istype(W, /obj/item/flame)) + if(rag && isflamesource(W)) rag.attackby(W, user) return ..() @@ -83,23 +84,27 @@ ..() /obj/item/reagent_containers/food/drinks/bottle/proc/insert_rag(obj/item/reagent_containers/glass/rag/R, mob/user) - if(!isGlass || rag) return + if(!isGlass || rag) + return if(user.unEquip(R)) - to_chat(user, "You stuff [R] into [src].") + to_chat(user, SPAN_NOTICE("You stuff \the [R] into \the [src].")) rag = R rag.forceMove(src) atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER update_icon() /obj/item/reagent_containers/food/drinks/bottle/proc/remove_rag(mob/user) - if(!rag) return + if(!rag) + return + to_chat(user, SPAN_NOTICE("You pull \the [rag] out of \the [src].")) user.put_in_hands(rag) rag = null atom_flags |= ATOM_FLAG_OPEN_CONTAINER update_icon() /obj/item/reagent_containers/food/drinks/bottle/open(mob/user) - if(rag) return + if(rag) + return ..() /obj/item/reagent_containers/food/drinks/bottle/on_update_icon() @@ -122,18 +127,18 @@ var/mob/living/carbon/human/H = target if(istype(H) && H.headcheck(hit_zone)) var/obj/item/organ/affecting = H.get_organ(hit_zone) //headcheck should ensure that affecting is not null - user.visible_message("[user] smashes [src] into [H]'s [affecting.name]!") + user.visible_message(SPAN_DANGER("\The [user] smashes \the [src] into \the [H]'s [affecting.name]!")) // You are going to knock someone out for longer if they are not wearing a helmet. var/blocked = target.get_blocked_ratio(hit_zone, BRUTE, damage = 10) * 100 var/weaken_duration = smash_duration + min(0, force - blocked + 10) if(weaken_duration) target.apply_effect(min(weaken_duration, 5), WEAKEN, blocked) // Never weaken more than a flash! else - user.visible_message("\The [user] smashes [src] into [target]!") + user.visible_message(SPAN_DANGER("\The [user] smashes [src] into [target]!")) //The reagents in the bottle splash all over the target, thanks for the idea Nodrak if(reagents) - user.visible_message("The contents of \the [src] splash all over [target]!") + user.visible_message(SPAN_NOTICE("The contents of \the [src] splash all over \the [target]!")) reagents.splash(target, reagents.total_volume) //Finally, smash the bottle. This kills (qdel) the bottle. From 6c8f161d1ca5b5c0700a30ee111732430452990f Mon Sep 17 00:00:00 2001 From: CrimsonShrike Date: Sat, 29 May 2021 20:54:07 +0100 Subject: [PATCH 063/246] Makes falling stuff deal more damage. Fixes exosuit not taking explo d --- code/modules/mechs/components/arms.dm | 1 + code/modules/mechs/components/head.dm | 1 + code/modules/mechs/mech_damage.dm | 42 +++++++++++++++++++++++++-- code/modules/mechs/mech_movement.dm | 2 +- code/modules/mechs/premade/light.dm | 3 +- code/modules/multiz/movement.dm | 2 +- 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/code/modules/mechs/components/arms.dm b/code/modules/mechs/components/arms.dm index eda40e35ffa..dcfc9477468 100644 --- a/code/modules/mechs/components/arms.dm +++ b/code/modules/mechs/components/arms.dm @@ -8,6 +8,7 @@ var/action_delay = 15 var/obj/item/robot_parts/robot_component/actuator/motivator power_use = 10 + w_class = ITEM_SIZE_LARGE /obj/item/mech_component/manipulators/Destroy() QDEL_NULL(motivator) diff --git a/code/modules/mechs/components/head.dm b/code/modules/mechs/components/head.dm index dfea05b4ddd..85375d44627 100644 --- a/code/modules/mechs/components/head.dm +++ b/code/modules/mechs/components/head.dm @@ -11,6 +11,7 @@ has_hardpoints = list(HARDPOINT_HEAD) var/active_sensors = 0 power_use = 15 + w_class = ITEM_SIZE_NORMAL /obj/item/mech_component/sensors/Destroy() QDEL_NULL(camera) diff --git a/code/modules/mechs/mech_damage.dm b/code/modules/mechs/mech_damage.dm index 449863a1d35..759cb21cab4 100644 --- a/code/modules/mechs/mech_damage.dm +++ b/code/modules/mechs/mech_damage.dm @@ -1,3 +1,20 @@ +/mob/living/exosuit/ex_act(severity) + var/b_loss = 0 + var/f_loss = 0 + switch (severity) + if (1) + b_loss = 200 + f_loss = 200 + if (2) + b_loss = 90 + f_loss = 90 + if(3) + b_loss = 45 + + // spread damage overall + apply_damage(b_loss, BRUTE, null, DAM_EXPLODE | DAM_DISPERSED, used_weapon = "Explosive blast") + apply_damage(f_loss, BURN, null, DAM_EXPLODE | DAM_DISPERSED, used_weapon = "Explosive blast") + /mob/living/exosuit/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0) if(!effect || (blocked >= 100)) return 0 @@ -73,15 +90,36 @@ else return body - /mob/living/exosuit/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/damage_flags = 0, var/used_weapon = null, var/armor_pen, var/silent = FALSE) if(!damage) return 0 + if(!def_zone) + if(damage_flags & DAM_DISPERSED) + var/old_damage = damage + var/tally + silent = FALSE + for(var/obj/item/part in list(arms, legs, body, head)) + tally += part.w_class + for(var/obj/item/part in list(arms, legs, body, head)) + damage = old_damage * part.w_class/tally + def_zone = BP_CHEST + if(part == arms) + def_zone = BP_L_ARM + else if(part == legs) + def_zone = BP_L_LEG + else if(part == head) + def_zone = BP_HEAD + + . = .() || . + return + + def_zone = ran_zone(def_zone) + var/list/after_armor = modify_damage_by_armor(def_zone, damage, damagetype, damage_flags, src, armor_pen, TRUE) damage = after_armor[1] damagetype = after_armor[2] - + if(!damage) return 0 diff --git a/code/modules/mechs/mech_movement.dm b/code/modules/mechs/mech_movement.dm index 5e99cb6bb72..bc09218caa7 100644 --- a/code/modules/mechs/mech_movement.dm +++ b/code/modules/mechs/mech_movement.dm @@ -160,7 +160,7 @@ return !length(pilots) /mob/living/exosuit/fall_damage() - return 100 //Exosuits are big and heavy + return 175 //Exosuits are big and heavy /mob/living/exosuit/handle_fall_effect(var/turf/landing) // Return here if for any reason you shouldn´t take damage diff --git a/code/modules/mechs/premade/light.dm b/code/modules/mechs/premade/light.dm index 530b2936a35..d674797b2ab 100644 --- a/code/modules/mechs/premade/light.dm +++ b/code/modules/mechs/premade/light.dm @@ -45,7 +45,8 @@ desc = "These Odysseus series legs are built from lightweight flexible polymers, making them capable of handling falls from up to 120 meters in 1g environments. Provided that the exosuit lands on its feet." max_fall_damage = 0 -/obj/item/mech_component/propulsion/handle_vehicle_fall() +/obj/item/mech_component/propulsion/light/handle_vehicle_fall() + ..() visible_message(SPAN_NOTICE("\The [src] creak as they absorb the impact.")) /obj/item/mech_component/sensors/light diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 831ad5ff685..06f9e80733f 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -236,7 +236,7 @@ if(w_class == ITEM_SIZE_TINY) return 0 if(w_class == ITEM_SIZE_NO_CONTAINER) - return 100 + return 150 return BASE_STORAGE_COST(w_class) /mob/living/carbon/human/handle_fall_effect(var/turf/landing) From b56304ea6f9f854391cfaadd21882742daa8021d Mon Sep 17 00:00:00 2001 From: SealCure Date: Sun, 30 May 2021 22:02:41 +0100 Subject: [PATCH 064/246] Add scihud implant --- code/modules/augment/active/hudimplants.dm | 8 +++++++- .../loadout/lists/augments.dm | 5 +++++ .../preference_setup/loadout/lists/eyegear.dm | 2 +- .../research/designs/designs_mechfab.dm | 7 +++++++ icons/obj/augment.dmi | Bin 7372 -> 7352 bytes maps/torch/loadout/loadout_augments.dm | 3 +++ 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/code/modules/augment/active/hudimplants.dm b/code/modules/augment/active/hudimplants.dm index c70a66d3e12..1625a1ffc05 100644 --- a/code/modules/augment/active/hudimplants.dm +++ b/code/modules/augment/active/hudimplants.dm @@ -56,4 +56,10 @@ name = "integrated filth HUD" desc = "An implantable HUD based on the wearable janitorial version, designed to interface with the user's optic nerve and display information about nearby messes." icon_state = "eye_janitor" - hud_type = HUD_JANITOR \ No newline at end of file + hud_type = HUD_JANITOR + +/obj/item/organ/internal/augment/active/hud/science + name = "integrated sciHUD" + desc = "An implantable HUD fitted with a portable analyzer capable of determining the research potential of a visible item or the components of a machine." + icon_state = "eye_science" + hud_type = HUD_SCIENCE diff --git a/code/modules/client/preference_setup/loadout/lists/augments.dm b/code/modules/client/preference_setup/loadout/lists/augments.dm index 59f1dc1cb01..ebc54c4c9ca 100644 --- a/code/modules/client/preference_setup/loadout/lists/augments.dm +++ b/code/modules/client/preference_setup/loadout/lists/augments.dm @@ -45,3 +45,8 @@ display_name = "integrated filth HUD (head)" cost = 4 path = /obj/item/organ/internal/augment/active/hud/janitor + +/datum/gear/augment/integrated_science_hud + display_name = "integrated sciHUD (head)" + cost = 4 + path = /obj/item/organ/internal/augment/active/hud/science diff --git a/code/modules/client/preference_setup/loadout/lists/eyegear.dm b/code/modules/client/preference_setup/loadout/lists/eyegear.dm index a79e86408a5..dda7d19885a 100644 --- a/code/modules/client/preference_setup/loadout/lists/eyegear.dm +++ b/code/modules/client/preference_setup/loadout/lists/eyegear.dm @@ -134,4 +134,4 @@ /datum/gear/eyes/janitor/prescription display_name = "JaniHUD, prescription" - path = /obj/item/clothing/glasses/hud/janitor/prescription \ No newline at end of file + path = /obj/item/clothing/glasses/hud/janitor/prescription diff --git a/code/modules/research/designs/designs_mechfab.dm b/code/modules/research/designs/designs_mechfab.dm index 74564604ea8..c3156117e13 100644 --- a/code/modules/research/designs/designs_mechfab.dm +++ b/code/modules/research/designs/designs_mechfab.dm @@ -633,6 +633,13 @@ req_tech = list(TECH_BIO = 1, TECH_MAGNET = 3) id= "augment_jani_hud" +/datum/design/item/mechfab/augment/hud/science + name = "Implantable science HUD" + build_path = /obj/item/organ/internal/augment/active/hud/science + materials = list(DEFAULT_WALL_MATERIAL = 250, "glass" = 250) + req_tech = list(TECH_BIO = 1, TECH_MAGNET = 3) + id= "augment_sci_hud" + /datum/design/item/mechfab/augment/nanounit name = "Nanite MCU" build_path = /obj/item/organ/internal/augment/active/nanounit diff --git a/icons/obj/augment.dmi b/icons/obj/augment.dmi index a93fca634d04f1798c2040c06f8ca7281ad1271f..416101e30bee860c3cd0286c9eb696a31f92eb5c 100644 GIT binary patch literal 7352 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 literal 7372 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 WEz<~|4*_o}L8^+H3Keo@LH`48&P^Qv diff --git a/maps/torch/loadout/loadout_augments.dm b/maps/torch/loadout/loadout_augments.dm index fea8ad614c1..066629fa10d 100644 --- a/maps/torch/loadout/loadout_augments.dm +++ b/maps/torch/loadout/loadout_augments.dm @@ -9,3 +9,6 @@ /datum/gear/augment/integrated_janitor_hud allowed_roles = list(/datum/job/janitor) + +/datum/gear/augment/integrated_science_hud + allowed_roles = list(RESEARCH_ROLES, EXPLORATION_ROLES) From 8b6ae4bd4505d75183c63dbb894bec76a4fb4016 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 31 May 2021 02:39:04 +0000 Subject: [PATCH 065/246] Automatic changelog generation --- html/changelog.html | 15 ++++++--------- html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-30764.yml | 4 ---- 3 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30764.yml diff --git a/html/changelog.html b/html/changelog.html index f31a24fec63..b39003b4b6f 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,12 @@ -->
            +

            31 May 2021

            +

            Ilysen updated:

            +
              +
            • You can now properly change the appearance of the modified gas mask.
            • +
            +

            30 May 2021

            0sj/Jaydn updated:

              @@ -595,15 +601,6 @@

              gy1ta23 updated:

              • Added an extra box of sterile gloves to the infirmary, and a supply crate for more.
              - -

              29 March 2021

              -

              SierraKomodo updated:

              -
                -
              • There is now a delay between the exoplanet awakening event starting and mobs actually spawning.
              • -
              • Cargo trolleys can now be hitched together regardless of what direction the trolley is facing.
              • -
              • Cargo trolleys now tell you if and what they're linked to when you examine them.
              • -
              • Natural weapons (I.e. teeth) will no longer embed in people.
              • -
            GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index a88ba30b6ec..8bb567c0ee7 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16943,3 +16943,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Viim: - tweak: Changes traitor skills from three trained, one experienced, to two trained, one experienced, and one master. +2021-05-31: + Ilysen: + - bugfix: You can now properly change the appearance of the modified gas mask. diff --git a/html/changelogs/AutoChangeLog-pr-30764.yml b/html/changelogs/AutoChangeLog-pr-30764.yml deleted file mode 100644 index 4e1193d9da9..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30764.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Ilysen -delete-after: true -changes: - - bugfix: You can now properly change the appearance of the modified gas mask. From 3ddecff1257d1eef711b21a5cbd6ddfe33b77ba8 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 31 May 2021 12:53:15 +0200 Subject: [PATCH 066/246] Automatic changelog generation for PR #30763 [ci skip] --- html/changelogs/AutoChangeLog-pr-30763.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30763.yml diff --git a/html/changelogs/AutoChangeLog-pr-30763.yml b/html/changelogs/AutoChangeLog-pr-30763.yml new file mode 100644 index 00000000000..d0e55b41c8f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30763.yml @@ -0,0 +1,4 @@ +author: SierraKomodo +delete-after: true +changes: + - rscdel: Pickpocket gloves have been removed. From 82eb4b9c8a5dcb65eaae3c4cf48c3eeffc1a5d4a Mon Sep 17 00:00:00 2001 From: CrimsonShrike Date: Mon, 31 May 2021 15:11:25 +0100 Subject: [PATCH 067/246] Makes passengers on mechs keep rational order --- code/modules/mechs/mech_icon.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/mechs/mech_icon.dm b/code/modules/mechs/mech_icon.dm index 987132c600c..1a607fe8c71 100644 --- a/code/modules/mechs/mech_icon.dm +++ b/code/modules/mechs/mech_icon.dm @@ -61,7 +61,8 @@ proc/get_mech_images(var/list/components = list(), var/overlay_layer = FLOAT_LAY var/mob/pilot = pilots[i] var/image/draw_pilot = new draw_pilot.appearance = pilot - draw_pilot.layer = MECH_PILOT_LAYER + (body ? ((LAZYLEN(body.pilot_positions)-i)*0.001) : 0) + var/rel_pos = dir == NORTH ? -1 : 1 + draw_pilot.layer = MECH_PILOT_LAYER + (body ? ((LAZYLEN(body.pilot_positions)-i)*0.001 * rel_pos) : 0) draw_pilot.plane = FLOAT_PLANE if(body && i <= LAZYLEN(body.pilot_positions)) var/list/offset_values = body.pilot_positions[i] From 6d33853e83f8525b684bdc1a9cc656de39b914c2 Mon Sep 17 00:00:00 2001 From: Ilysen Date: Mon, 31 May 2021 11:57:59 -0400 Subject: [PATCH 068/246] Arrows for hairstyles --- .../preference_setup/general/02_body.dm | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/code/modules/client/preference_setup/general/02_body.dm b/code/modules/client/preference_setup/general/02_body.dm index 28c9bcc94f4..f24a3ab8a78 100644 --- a/code/modules/client/preference_setup/general/02_body.dm +++ b/code/modules/client/preference_setup/general/02_body.dm @@ -278,12 +278,12 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O . += "Hair
            " if(has_flag(mob_species, HAS_HAIR_COLOR)) . += "
            Change Color
            __
            " - . += " Style:
            [pref.h_style]
            " + . += " Style: [UIBUTTON("hair_style=1;decrement", "<", null)][UIBUTTON("hair_style=1;increment", ">", null)][pref.h_style]
            " . += "
            Facial
            " if(has_flag(mob_species, HAS_HAIR_COLOR)) . += "Change Color
            __
            " - . += " Style: [pref.f_style]
            " + . += " Style: [UIBUTTON("facial_style=1;decrement", "<", null)][UIBUTTON("facial_style=1;increment", ">", null)][pref.f_style]
            " if(has_flag(mob_species, HAS_EYE_COLOR)) . += "
            Eyes
            " @@ -397,7 +397,17 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["hair_style"]) var/list/valid_hairstyles = mob_species.get_hair_styles() - var/new_h_style = input(user, "Choose your character's hair style:", CHARACTER_PREFERENCE_INPUT_TITLE, pref.h_style) as null|anything in valid_hairstyles + var/new_h_style + var/hair_index = list_find(valid_hairstyles, pref.h_style) + + if (href_list["increment"]) + if (hair_index < valid_hairstyles.len && valid_hairstyles[hair_index + 1]) + new_h_style = valid_hairstyles[hair_index + 1] + else if (href_list["decrement"]) + if (hair_index > 1 && valid_hairstyles[hair_index - 1]) + new_h_style = valid_hairstyles[hair_index - 1] + else + new_h_style = input(user, "Choose your character's hair style:", CHARACTER_PREFERENCE_INPUT_TITLE, pref.h_style) as null|anything in valid_hairstyles mob_species = all_species[pref.species] if(new_h_style && CanUseTopic(user) && (new_h_style in mob_species.get_hair_styles())) @@ -453,11 +463,20 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["facial_style"]) var/list/valid_facialhairstyles = mob_species.get_facial_hair_styles(pref.gender) - - var/new_f_style = input(user, "Choose your character's facial-hair style:", CHARACTER_PREFERENCE_INPUT_TITLE, pref.f_style) as null|anything in valid_facialhairstyles + var/new_f_style + var/hair_index = list_find(valid_facialhairstyles, pref.f_style) + + if (href_list["increment"]) + if (hair_index < valid_facialhairstyles.len && valid_facialhairstyles[hair_index + 1]) + new_f_style = valid_facialhairstyles[hair_index + 1] + else if (href_list["decrement"]) + if (hair_index > 1 && valid_facialhairstyles[hair_index - 1]) + new_f_style = valid_facialhairstyles[hair_index - 1] + else + new_f_style = input(user, "Choose your character's facial-hair style:", CHARACTER_PREFERENCE_INPUT_TITLE, pref.f_style) as null|anything in valid_facialhairstyles mob_species = all_species[pref.species] - if(new_f_style && CanUseTopic(user) && mob_species.get_facial_hair_styles(pref.gender)) + if(new_f_style && CanUseTopic(user) && (new_f_style in mob_species.get_facial_hair_styles(pref.gender))) pref.f_style = new_f_style return TOPIC_REFRESH_UPDATE_PREVIEW From 0d1c0a67116c3a39ac76666778bf1047d9a9604a Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Tue, 1 Jun 2021 03:07:43 +0000 Subject: [PATCH 069/246] Automatic changelog generation --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 3 +++ html/changelogs/AutoChangeLog-pr-30763.yml | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30763.yml diff --git a/html/changelog.html b/html/changelog.html index b39003b4b6f..2ba4328a8e9 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,12 @@ -->
            +

            01 June 2021

            +

            SierraKomodo updated:

            +
              +
            • Pickpocket gloves have been removed.
            • +
            +

            31 May 2021

            Ilysen updated:

              diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 8bb567c0ee7..6b6b9c373f2 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16946,3 +16946,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2021-05-31: Ilysen: - bugfix: You can now properly change the appearance of the modified gas mask. +2021-06-01: + SierraKomodo: + - rscdel: Pickpocket gloves have been removed. diff --git a/html/changelogs/AutoChangeLog-pr-30763.yml b/html/changelogs/AutoChangeLog-pr-30763.yml deleted file mode 100644 index d0e55b41c8f..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30763.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: SierraKomodo -delete-after: true -changes: - - rscdel: Pickpocket gloves have been removed. From 1053e39ab12c575c56bffb0e3808331a4b2a238b Mon Sep 17 00:00:00 2001 From: CrimsonShrike Date: Mon, 31 May 2021 21:36:18 +0100 Subject: [PATCH 070/246] More fixes and changes from nebula --- baystation12.dme | 1 + code/__defines/zmimic.dm | 4 +- code/controllers/subsystems/skybox.dm | 17 ++ code/controllers/subsystems/zcopy.dm | 213 +++++++++++++------- code/datums/mutable_appearance.dm | 16 ++ code/game/turfs/space/space.dm | 17 +- code/game/turfs/turf.dm | 4 +- code/game/turfs/turf_changing.dm | 3 + code/modules/multiz/zmimic/mimic_docs.dm | 51 +++++ code/modules/multiz/zmimic/mimic_movable.dm | 57 +++--- code/modules/multiz/zmimic/mimic_turf.dm | 40 ++-- 11 files changed, 286 insertions(+), 137 deletions(-) create mode 100644 code/datums/mutable_appearance.dm create mode 100644 code/modules/multiz/zmimic/mimic_docs.dm diff --git a/baystation12.dme b/baystation12.dme index 7a2495dfd32..a57f53d5985 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -247,6 +247,7 @@ #include "code\datums\hierarchy.dm" #include "code\datums\local_network.dm" #include "code\datums\mil_ranks.dm" +#include "code\datums\mutable_appearance.dm" #include "code\datums\progressbar.dm" #include "code\datums\recipe.dm" #include "code\datums\ruins.dm" diff --git a/code/__defines/zmimic.dm b/code/__defines/zmimic.dm index 5f48020fb44..517d7522783 100644 --- a/code/__defines/zmimic.dm +++ b/code/__defines/zmimic.dm @@ -8,7 +8,7 @@ #define ZM_ALLOW_LIGHTING 4 // If this turf should permit passage of lighting. #define ZM_ALLOW_ATMOS 8 // If this turf permits passage of air. #define ZM_MIMIC_NO_AO 16 // If the turf shouldn't apply regular turf AO and only do Z-mimic AO. -#define ZM_FIX_BIGTURF 32 // Fix bigturf (greater than world.icon_size) rendering at the cost of breaking object layering a bit. This flag is infectious, so all Z-turfs above this one will also get this flag. Valid on non-zturfs. +#define ZM_NO_OCCLUDE 32 // Don't occlude below atoms if we're a non-mimic z-turf. // Convenience flag. #define ZM_MIMIC_DEFAULTS (ZM_MIMIC_BELOW|ZM_ALLOW_LIGHTING) @@ -20,5 +20,5 @@ var/list/mimic_defines = list( "ZM_ALLOW_LIGHTING", "ZM_ALLOW_ATMOS", "ZM_MIMIC_NO_AO", - "ZM_FIX_BIGTURF" + "ZM_NO_OCCLUDE" ) diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm index e9836738f2c..5a0f93ac1ad 100644 --- a/code/controllers/subsystems/skybox.dm +++ b/code/controllers/subsystems/skybox.dm @@ -12,6 +12,23 @@ SUBSYSTEM_DEF(skybox) var/star_path = 'icons/skybox/skybox.dmi' var/star_state = "stars" var/list/skybox_cache = list() + var/list/space_appearance_cache + +/datum/controller/subsystem/skybox/PreInit() + build_space_appearances() + +/datum/controller/subsystem/skybox/proc/build_space_appearances() + space_appearance_cache = new(26) + for (var/i in 0 to 25) + var/mutable_appearance/dust = mutable_appearance('icons/turf/space_dust.dmi', "[i]") + dust.plane = DUST_PLANE + dust.alpha = 80 + dust.blend_mode = BLEND_ADD + + var/mutable_appearance/space = new /mutable_appearance(/turf/space) + space.icon_state = "white" + space.overlays += dust + space_appearance_cache[i + 1] = space.appearance /datum/controller/subsystem/skybox/Initialize() . = ..() diff --git a/code/controllers/subsystems/zcopy.dm b/code/controllers/subsystems/zcopy.dm index 9a8d0e2d521..e581fe8ad1a 100644 --- a/code/controllers/subsystems/zcopy.dm +++ b/code/controllers/subsystems/zcopy.dm @@ -18,7 +18,10 @@ SUBSYSTEM_DEF(zcopy) var/openspace_overlays = 0 var/openspace_turfs = 0 - // Highest Z level in a given Z-group for absolute layering used by FIX_BIGTURF. + var/multiqueue_skips_turf = 0 + var/multiqueue_skips_object = 0 + + // Highest Z level in a given Z-group for absolute layering. // zstm[zlev] = group_max var/list/zlev_maximums = list() @@ -38,7 +41,7 @@ SUBSYSTEM_DEF(zcopy) T.update_mimic() num_upd += 1 - else if (istype(A, /atom/movable/openspace/overlay)) + else if (istype(A, /atom/movable/openspace/mimic)) var/turf/Tloc = A.loc if (TURF_IS_MIMICING(Tloc)) Tloc.update_mimic() @@ -68,7 +71,7 @@ SUBSYSTEM_DEF(zcopy) T.update_mimic() num_turfs += 1 - else if (istype(A, /atom/movable/openspace/overlay)) + else if (istype(A, /atom/movable/openspace/mimic)) qdel(A) num_deleted += 1 @@ -79,13 +82,12 @@ SUBSYSTEM_DEF(zcopy) enable() /datum/controller/subsystem/zcopy/stat_entry() - ..("Q:{T:[queued_turfs.len - (qt_idex - 1)]|O:[queued_overlays.len - (qo_idex - 1)]} T:{T:[openspace_turfs]|O:[openspace_overlays]}") + ..("Mx:[json_encode(zlev_maximums)]\n\tQ:{T:[queued_turfs.len - (qt_idex - 1)]|O:[queued_overlays.len - (qo_idex - 1)]}\n\tT:{T:[openspace_turfs]|O:[openspace_overlays]}\n\tSk:{T:[multiqueue_skips_turf]|O:[multiqueue_skips_object]}") /datum/controller/subsystem/zcopy/Initialize(timeofday) calculate_zstack_limits() // Flush the queue. fire(FALSE, TRUE) - return ..() // If you add a new Zlevel or change Z-connections, call this. /datum/controller/subsystem/zcopy/proc/calculate_zstack_limits() @@ -98,7 +100,7 @@ SUBSYSTEM_DEF(zcopy) if (z - start_zlev > OPENTURF_MAX_DEPTH) log_ss("zcopy", "WARNING: Z-levels [start_zlev] through [z] exceed maximum depth of [OPENTURF_MAX_DEPTH]; layering may behave strangely in this Z-stack.") else if (z - start_zlev > 1) - log_ss("zcopy", "Found Z-Stack: [start_zlev] -> [z] = [z - start_zlev] zl") + log_ss("zcopy", "Found Z-Stack: [start_zlev] -> [z] = [z - start_zlev + 1] zl") start_zlev = z + 1 log_ss("zcopy", "Z-Level maximums: [json_encode(zlev_maximums)]") @@ -126,7 +128,7 @@ SUBSYSTEM_DEF(zcopy) curr_turfs[qt_idex] = null qt_idex += 1 - if (!isturf(T) || !T.below || !(T.z_flags & ZM_MIMIC_BELOW) || !T.z_queued) + if (!isturf(T) || !(T.z_flags & ZM_MIMIC_BELOW) || !T.z_queued) if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) @@ -136,16 +138,43 @@ SUBSYSTEM_DEF(zcopy) // If we're not at our most recent queue position, don't bother -- we're updating again later anyways. if (T.z_queued > 1) T.z_queued -= 1 + multiqueue_skips_turf += 1 + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + break + continue + + // Z-Turf on the bottom-most level, just fake-copy space. + // If this is ever true, that turf should always pass this condition, so don't bother cleaning up beyond the Destroy() hook. + if (!T.below) // Z-turf on the bottom-most level, just fake-copy space. + if (T.z_flags & ZM_MIMIC_OVERWRITE) + T.appearance = SSskybox.space_appearance_cache[(((T.x + T.y) ^ ~(T.x * T.y) + T.z) % 25) + 1] + T.name = initial(T.name) + T.desc = initial(T.desc) + T.gender = initial(T.gender) + else + // Some openturfs have icons, so we can't overwrite their appearance. + if (!T.mimic_underlay) + T.mimic_underlay = new(T) + var/atom/movable/openspace/turf_proxy/TO = T.mimic_underlay + TO.appearance = SSskybox.space_appearance_cache[(((T.x + T.y) ^ ~(T.x * T.y) + T.z) % 25) + 1] + TO.name = T.name + TO.gender = T.gender // Need to grab this too so PLURAL works properly in examine. + TO.mouse_opacity = initial(TO.mouse_opacity) + if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) - return + break continue if (!T.shadower) // If we don't have a shadower yet, something has gone horribly wrong. WARNING("Turf [T] at [T.x],[T.y],[T.z] was queued, but had no shadower.") continue + T.z_generation += 1 + // Get the bottom-most turf, the one we want to mimic. var/turf/Td = T while (Td.below) @@ -162,13 +191,10 @@ SUBSYSTEM_DEF(zcopy) T.z_eventually_space = TRUE t_target = SPACE_PLANE - if (T.below.z_flags & ZM_FIX_BIGTURF) - T.z_flags |= ZM_FIX_BIGTURF // this flag is infectious - if (T.z_flags & ZM_MIMIC_OVERWRITE) // This openturf doesn't care about its icon, so we can just overwrite it. - if (T.below.bound_overlay) - QDEL_NULL(T.below.bound_overlay) + if (T.below.mimic_proxy) + QDEL_NULL(T.below.mimic_proxy) T.appearance = T.below T.name = initial(T.name) T.desc = initial(T.desc) @@ -177,9 +203,9 @@ SUBSYSTEM_DEF(zcopy) T.plane = t_target else // Some openturfs have icons, so we can't overwrite their appearance. - if (!T.below.bound_overlay) - T.below.bound_overlay = new(T) - var/atom/movable/openspace/turf_delegate/TO = T.below.bound_overlay + if (!T.below.mimic_proxy) + T.below.mimic_proxy = new(T) + var/atom/movable/openspace/turf_proxy/TO = T.below.mimic_proxy TO.appearance = Td TO.name = T.name TO.gender = T.gender // Need to grab this too so PLURAL works properly in examine. @@ -193,15 +219,15 @@ SUBSYSTEM_DEF(zcopy) // I think it's possible to get this to work without discrete delegate copy objects, but I'd rather this just work. if ((T.below.z_flags & (ZM_MIMIC_BELOW|ZM_MIMIC_OVERWRITE)) == ZM_MIMIC_BELOW) // Below is a delegate, gotta explicitly copy it for recursive copy. - if (!T.below.z_delegate) - T.below.z_delegate = new(T) - var/atom/movable/openspace/delegate_copy/DC = T.below.z_delegate + if (!T.below.mimic_above_copy) + T.below.mimic_above_copy = new(T) + var/atom/movable/openspace/turf_mimic/DC = T.below.mimic_above_copy DC.appearance = T.below DC.mouse_opacity = initial(DC.mouse_opacity) DC.plane = OPENTURF_MAX_PLANE - else if (T.below.z_delegate) - QDEL_NULL(T.below.z_delegate) + else if (T.below.mimic_above_copy) + QDEL_NULL(T.below.mimic_above_copy) // Handle below atoms. @@ -214,49 +240,56 @@ SUBSYSTEM_DEF(zcopy) // Special case: these are merged into the shadower to reduce memory usage. if (object.type == /atom/movable/lighting_overlay) - // T.shadower.copy_lighting(object) - else - if (!object.bound_overlay) // Generate a new overlay if the atom doesn't already have one. - object.bound_overlay = new(T) - object.bound_overlay.associated_atom = object - - var/override_depth - var/original_type = object.type - var/original_z = object.z - switch (object.type) - if (/atom/movable/openspace/overlay) - var/atom/movable/openspace/overlay/OOO = object - original_type = OOO.mimiced_type - override_depth = OOO.override_depth - original_z = OOO.original_z - - if (/atom/movable/openspace/turf_delegate, /atom/movable/openspace/delegate_copy) - // If we're a turf overlay (the mimic for a non-OVERWRITE turf), we need to make sure copies of us respect space parallax too - if (T.z_eventually_space) - // Yes, this is an awful hack; I don't want to add yet another override_* var. - override_depth = OPENTURF_MAX_PLANE - SPACE_PLANE - - if ((T.z_flags & ZM_FIX_BIGTURF) && original_type == /atom/movable/openspace/multiplier) - override_depth = turf_depth - - var/atom/movable/openspace/overlay/OO = object.bound_overlay - - // If the OO was queued for destruction but was claimed by another OT, stop the destruction timer. - if (OO.destruction_timer) - deltimer(OO.destruction_timer) - OO.destruction_timer = null - - OO.depth = override_depth || min(T.z - original_z, OPENTURF_MAX_DEPTH) - OO.mimiced_type = original_type - OO.override_depth = override_depth - OO.original_z = original_z - - if (!OO.queued) - OO.queued = TRUE - queued_overlays += OO + //T.shadower.copy_lighting(object) + continue + + if (!object.bound_overlay) // Generate a new overlay if the atom doesn't already have one. + object.bound_overlay = new(T) + object.bound_overlay.associated_atom = object + + var/override_depth + var/original_type = object.type + var/original_z = object.z + switch (object.type) + if (/atom/movable/openspace/mimic) + var/atom/movable/openspace/mimic/OOO = object + original_type = OOO.mimiced_type + override_depth = OOO.override_depth + original_z = OOO.original_z + + if (/atom/movable/openspace/turf_proxy, /atom/movable/openspace/turf_mimic) + // If we're a turf overlay (the mimic for a non-OVERWRITE turf), we need to make sure copies of us respect space parallax too + if (T.z_eventually_space) + // Yes, this is an awful hack; I don't want to add yet another override_* var. + override_depth = OPENTURF_MAX_PLANE - SPACE_PLANE + + var/atom/movable/openspace/mimic/OO = object.bound_overlay + + // If the OO was queued for destruction but was claimed by another OT, stop the destruction timer. + if (OO.destruction_timer) + deltimer(OO.destruction_timer) + OO.destruction_timer = null + + OO.depth = override_depth || min(zlev_maximums[T.z] - original_z, OPENTURF_MAX_DEPTH) + + // These types need to be pushed a layer down for bigturfs to function correctly. + switch (original_type) + if (/atom/movable/openspace/multiplier, /atom/movable/openspace/turf_mimic, /atom/movable/openspace/turf_proxy) + if (OO.depth < OPENTURF_MAX_DEPTH) + OO.depth += 1 + + OO.mimiced_type = original_type + OO.override_depth = override_depth + OO.original_z = original_z + + // Multi-queue to maintain ordering of updates to these + // queueing it multiple times will result in only the most recent + // actually processing. + OO.queued += 1 + queued_overlays += OO T.z_queued -= 1 - if (!no_mc_tick && T.above) + if (T.above) T.above.update_mimic() if (no_mc_tick) @@ -272,11 +305,11 @@ SUBSYSTEM_DEF(zcopy) MC_SPLIT_TICK while (qo_idex <= curr_ov.len) - var/atom/movable/openspace/overlay/OO = curr_ov[qo_idex] + var/atom/movable/openspace/mimic/OO = curr_ov[qo_idex] curr_ov[qo_idex] = null qo_idex += 1 - if (QDELETED(OO)) + if (QDELETED(OO) || !OO.queued) if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) @@ -292,6 +325,16 @@ SUBSYSTEM_DEF(zcopy) break continue + // Don't update unless we're at the most recent queue occurrence. + if (OO.queued > 1) + OO.queued -= 1 + multiqueue_skips_object += 1 + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + break + continue + // Actually update the overlay. if (OO.dir != OO.associated_atom.dir) OO.set_dir(OO.associated_atom.dir) @@ -300,7 +343,7 @@ SUBSYSTEM_DEF(zcopy) OO.plane = OPENTURF_MAX_PLANE - OO.depth OO.opacity = FALSE - OO.queued = FALSE + OO.queued = 0 if (OO.bound_overlay) // If we have a bound overlay, queue it too. OO.update_above() @@ -316,6 +359,9 @@ SUBSYSTEM_DEF(zcopy) #define FMT_DEPTH(X) (X == null ? "(null)" : X) +// This is a dummy object used so overlays can be shown in the analyzer. +/atom/movable/openspace/debug + /client/proc/analyze_openturf(turf/T) set name = "Analyze Openturf" set desc = "Show the layering of an openturf and everything it's mimicking." @@ -324,15 +370,20 @@ SUBSYSTEM_DEF(zcopy) if (!check_rights(R_DEBUG)) return + var/is_above_space = T.is_above_space() var/list/out = list( "", "

              Analysis of [T] at [T.x],[T.y],[T.z]

              ", "Queue occurrences: [T.z_queued]", - "Above space: Apparent [T.z_eventually_space ? "Yes" : "No"], Actual [T.is_above_space() ? "Yes" : "No"]", + "Above space: Apparent [T.z_eventually_space ? "Yes" : "No"], Actual [is_above_space ? "Yes" : "No"] - [T.z_eventually_space == is_above_space ? "OK" : "MISMATCH"]", "Z Flags: [english_list(bitfield2list(T.z_flags, global.mimic_defines), "(none)")]", "Has Shadower: [T.shadower ? "Yes" : "No"]", + "Has turf proxy: [T.mimic_proxy ? "Yes" : "No"]", + "Has above copy: [T.mimic_above_copy ? "Yes" : "No"]", + "Has mimic underlay: [T.mimic_underlay ? "Yes" : "No"]", "Below: [!T.below ? "(nothing)" : "[T.below] at [T.below.x],[T.below.y],[T.below.z]"]", "Depth: [FMT_DEPTH(T.z_depth)] [T.z_depth == OPENTURF_MAX_DEPTH ? "(max)" : ""]", + "Generation: [T.z_generation]", "
                " ) @@ -340,6 +391,14 @@ SUBSYSTEM_DEF(zcopy) for (var/atom/movable/openspace/O in T) found_oo += O + if (T.shadower.overlays.len) + for (var/overlay in T.shadower.overlays) + var/atom/movable/openspace/debug/D = new + D.appearance = overlay + if (D.plane < -10000) // FLOAT_PLANE + D.plane = T.shadower.plane + found_oo += D + sortTim(found_oo, /proc/cmp_planelayer) var/list/atoms_list_list = list() @@ -367,7 +426,11 @@ SUBSYSTEM_DEF(zcopy) // Flush the list so we can find orphans. atoms_list_list -= "[pl]" - log_debug("atoms_list_list => [json_encode(atoms_list_list)]") + if (atoms_list_list["[SPACE_PLANE]"]) // Space parallax plane + out += "Space parallax plane ([SPACE_PLANE])" + SSzcopy.debug_fmt_planelist(atoms_list_list["[SPACE_PLANE]"], out, T) + atoms_list_list -= "[SPACE_PLANE]" + for (var/key in atoms_list_list) out += "Unknown plane: [key]" SSzcopy.debug_fmt_planelist(atoms_list_list[key], out, T) @@ -380,14 +443,14 @@ SUBSYSTEM_DEF(zcopy) // Yes, I know this proc is a bit of a mess. Feel free to clean it up. /datum/controller/subsystem/zcopy/proc/debug_fmt_thing(atom/A, list/out, turf/original) - if (istype(A, /atom/movable/openspace/overlay)) - var/atom/movable/openspace/overlay/OO = A + if (istype(A, /atom/movable/openspace/mimic)) + var/atom/movable/openspace/mimic/OO = A var/atom/movable/AA = OO.associated_atom var/copied_type = AA.type == OO.mimiced_type ? "[AA.type] \[direct\]" : "[AA.type], eventually [OO.mimiced_type]" return "
              • \icon[A] \[Mimic\] plane [A.plane], layer [A.layer], depth [FMT_DEPTH(OO.depth)], associated Z-level [AA.z] - [OO.type] copying [AA] ([copied_type])
              • " - else if (istype(A, /atom/movable/openspace/delegate_copy)) - var/atom/movable/openspace/delegate_copy/DC = A - return "
              • \icon[A] \[Turf Delegate Copy\] plane [A.plane], layer [A.layer], Z-level [A.z], delegate of \icon[DC.delegate] [DC.delegate] ([DC.delegate.type])
              • " + else if (istype(A, /atom/movable/openspace/turf_mimic)) + var/atom/movable/openspace/turf_mimic/DC = A + return "
              • \icon[A] \[Turf Mimic\] plane [A.plane], layer [A.layer], Z-level [A.z], delegate of \icon[DC.delegate] [DC.delegate] ([DC.delegate.type])
              • " else if (isturf(A)) if (A == original) return "
              • \icon[A] \[Turf\] plane [A.plane], layer [A.layer], depth [FMT_DEPTH(A:z_depth)], Z-level [A.z] - [A] ([A.type]) - SELF
              • " @@ -395,8 +458,10 @@ SUBSYSTEM_DEF(zcopy) return "
              • \icon[A] \[Turf\] plane [A.plane], layer [A.layer], depth [FMT_DEPTH(A:z_depth)], Z-level [A.z] - [A] ([A.type]) - FOREIGN
              • " else if (A.type == /atom/movable/openspace/multiplier) return "
              • \icon[A] \[Shadower\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
              • " - else if (A.type == /atom/movable/openspace/turf_delegate) - return "
              • \icon[A] \[Turf Delegate (Mimic)\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
              • " + else if (A.type == /atom/movable/openspace/debug) // These are fake objects that exist just to show the shadower's overlays in this list. + return "
              • \icon[A] \[Shadower True Overlay\] plane [A.plane], layer [A.layer] - VIRTUAL
              • " + else if (A.type == /atom/movable/openspace/turf_proxy) + return "
              • \icon[A] \[Turf Proxy\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
              • " else return "
              • \icon[A] \[?\] plane [A.plane], layer [A.layer], Z-level [A.z] - [A] ([A.type])
              • " diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm new file mode 100644 index 00000000000..679ddb6d43e --- /dev/null +++ b/code/datums/mutable_appearance.dm @@ -0,0 +1,16 @@ +// 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. + +// Helper similar to image() +/proc/mutable_appearance(icon, icon_state, color, flags = RESET_COLOR | RESET_ALPHA, plane = FLOAT_PLANE, layer = FLOAT_LAYER) + var/mutable_appearance/MA = new() + MA.icon = icon + MA.icon_state = icon_state + MA.color = color + MA.appearance_flags = flags + MA.plane = plane + MA.layer = layer + return MA diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 580565cbef6..81fe3935f44 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -7,28 +7,15 @@ dynamic_lighting = 0 temperature = T20C thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT - var/static/list/dust_cache permit_ao = FALSE z_eventually_space = TRUE -/turf/space/proc/build_dust_cache() - LAZYINITLIST(dust_cache) - for (var/i in 0 to 25) - var/image/im = image('icons/turf/space_dust.dmi',"[i]") - im.plane = DUST_PLANE - im.alpha = 80 - im.blend_mode = BLEND_ADD - dust_cache["[i]"] = im - - /turf/space/Initialize() . = ..() - icon_state = "white" update_starlight() - if (!dust_cache) - build_dust_cache() - overlays += dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + + appearance = SSskybox.space_appearance_cache[(((x + y) ^ ~(x * y) + z) % 25) + 1] if(!HasBelow(z)) return diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 7b0d2f7436f..d71195ab4f2 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -82,8 +82,8 @@ if (z_flags & ZM_MIMIC_BELOW) cleanup_zmimic() - if (bound_overlay) - QDEL_NULL(bound_overlay) + if (mimic_proxy) + QDEL_NULL(mimic_proxy) ..() return QDEL_HINT_IWILLGC diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 4d4577b42fd..11850cc569e 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -35,6 +35,7 @@ var/old_lighting_overlay = lighting_overlay var/old_corners = corners var/old_ao_neighbors = ao_neighbors + var/old_above = above // log_debug("Replacing [src.type] with [N]") @@ -82,6 +83,8 @@ for(var/turf/space/S in range(W,1)) S.update_starlight() + W.above = old_above + W.post_change() . = W diff --git a/code/modules/multiz/zmimic/mimic_docs.dm b/code/modules/multiz/zmimic/mimic_docs.dm new file mode 100644 index 00000000000..be6ea015f1c --- /dev/null +++ b/code/modules/multiz/zmimic/mimic_docs.dm @@ -0,0 +1,51 @@ +/* +Types: + openspace/multiplier -> shadows the below level, also copies lighting + openspace/mimic -> copies below movables + openspace/turf_proxy -> holds the appearance of the below turf for non-OVERWRITE Z-turfs + openspace/turf_mimic -> copies openspace/turf_proxy objects + +Public API: + Notifying Z-Mimic of icon updates: + - UPDATE_OO_IF_PRESENT + - valid on movables only + - if this movable is being copied, update the copies + - cheap (if this movable is not being mimiced, this is a null check) + + - atom/update_above() + - similar to UPDATE_OO_IF_PRESENT, but for both turfs and movables + - less cheap (pretty much just proc-call overhead) + + Checking state: + - TURF_IS_MIMICING(turf or any) + - value: bool - if the passed turf is z-mimic enabled + + - movable/get_above_oo() + - return: list of movables + - get a list of every openspace mimic that's copying this atom for things like animate() + + Changing state: + - turf/enable_zmimic(extra_flags = 0) + - return: bool - FALSE if this turf was already mimicing, TRUE otherwise + - Enables z-mimic for this turf, potentially adding extra z_flags. + - This will automatically queue the turf for update. + + - turf/disable_zmimic() + - return: bool - FALSE if this turf was not mimicing, TRUE otherwise + - Disables z-mimic for this turf. + - This will clean up associated mimic objects, but they may hang around for a few additional seconds. + + Vars: + - turf/z_flags + - bitfield + - ZM_MIMIC_BELOW: copy below atoms + - ZM_MIMIC_OVERWRITE: z-mimic can overwrite this turf's appearance + - ZM_ALLOW_LIGHTING: lighting should pass through this turf + - ZM_ALLOW_ATMOS: air should pass through this turf + - ZM_MIMIC_NO_AO: normal turf AO should be skipped, only do openspace AO (if your turf is not solid, you probably want this) + - ZM_NO_OCCLUDE: don't block clicking on below atoms if not OVERWRITE + + - movable/no_z_overlay + - bool + - Set this to TRUE if you want Z-Mimic to ignore this atom. Atoms with INVISIBLITY_ABSTRACT are automatically ignored; other invisibility values are inherited. +*/ diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 8bda9dd0444..d6dd1e1b536 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -1,6 +1,8 @@ /atom/movable - var/tmp/atom/movable/openspace/overlay/bound_overlay // The overlay that is directly mirroring us that we proxy movement to. - var/no_z_overlay // If TRUE, this atom will not be drawn on open turfs. + /// The mimic (if any) that's *directly* copying us. + var/tmp/atom/movable/openspace/mimic/bound_overlay + /// If TRUE, this atom is ignored by Z-Mimic. + var/no_z_overlay /atom/movable/forceMove(atom/dest) . = ..(dest) @@ -32,9 +34,8 @@ var/turf/T = loc if (TURF_IS_MIMICING(T.above)) - if (!bound_overlay.queued) - SSzcopy.queued_overlays += bound_overlay - bound_overlay.queued = TRUE + SSzcopy.queued_overlays += bound_overlay + bound_overlay.queued += 1 else qdel(bound_overlay) @@ -140,21 +141,21 @@ // todo: rename // Object used to hold a mimiced atom's appearance. -/atom/movable/openspace/overlay +/atom/movable/openspace/mimic plane = OPENTURF_MAX_PLANE var/atom/movable/associated_atom var/depth - var/queued = FALSE + var/queued = 0 var/destruction_timer var/mimiced_type var/original_z var/override_depth -/atom/movable/openspace/overlay/New() +/atom/movable/openspace/mimic/New() atom_flags |= ATOM_FLAG_INITIALIZED SSzcopy.openspace_overlays += 1 -/atom/movable/openspace/overlay/Destroy() +/atom/movable/openspace/mimic/Destroy() SSzcopy.openspace_overlays -= 1 if (associated_atom) @@ -166,17 +167,17 @@ return ..() -/atom/movable/openspace/overlay/attackby(obj/item/W, mob/user) +/atom/movable/openspace/mimic/attackby(obj/item/W, mob/user) to_chat(user, SPAN_NOTICE("\The [src] is too far away.")) -/atom/movable/openspace/overlay/attack_hand(mob/user) +/atom/movable/openspace/mimic/attack_hand(mob/user) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) -/atom/movable/openspace/overlay/examine(...) +/atom/movable/openspace/mimic/examine(...) SHOULD_CALL_PARENT(FALSE) . = associated_atom.examine(arglist(args)) // just pass all the args to the copied atom -/atom/movable/openspace/overlay/forceMove(turf/dest) +/atom/movable/openspace/mimic/forceMove(turf/dest) . = ..() if (TURF_IS_MIMICING(dest)) if (destruction_timer) @@ -186,54 +187,54 @@ destruction_timer = addtimer(CALLBACK(src, /datum/.proc/qdel_self), 10 SECONDS, TIMER_STOPPABLE) // Called when the turf we're on is deleted/changed. -/atom/movable/openspace/overlay/proc/owning_turf_changed() +/atom/movable/openspace/mimic/proc/owning_turf_changed() if (!destruction_timer) destruction_timer = addtimer(CALLBACK(src, /datum/.proc/qdel_self), 10 SECONDS, TIMER_STOPPABLE) -// -- TURF DELEGATE -- +// -- TURF PROXY -- // This thing holds the mimic appearance for non-OVERWRITE turfs. -/atom/movable/openspace/turf_delegate +/atom/movable/openspace/turf_proxy plane = OPENTURF_MAX_PLANE mouse_opacity = 0 no_z_overlay = TRUE // Only one of these should ever be visible at a time, the mimic logic will handle that. -/atom/movable/openspace/turf_delegate/attackby(obj/item/W, mob/user) +/atom/movable/openspace/turf_proxy/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/turf_delegate/attack_hand(mob/user as mob) +/atom/movable/openspace/turf_proxy/attack_hand(mob/user as mob) loc.attack_hand(user) -/atom/movable/openspace/turf_delegate/attack_generic(mob/user as mob) +/atom/movable/openspace/turf_proxy/attack_generic(mob/user as mob) loc.attack_generic(user) -/atom/movable/openspace/turf_delegate/examine(mob/examiner) +/atom/movable/openspace/turf_proxy/examine(mob/examiner) SHOULD_CALL_PARENT(FALSE) . = loc.examine(examiner) -// -- DELEGATE COPY -- +// -- TURF MIMIC -- -// A type for copying delegates' self-appearance. -/atom/movable/openspace/delegate_copy +// A type for copying non-overwrite turfs' self-appearance. +/atom/movable/openspace/turf_mimic plane = OPENTURF_MAX_PLANE // These *should* only ever be at the top? mouse_opacity = 0 var/turf/delegate -/atom/movable/openspace/delegate_copy/Initialize(mapload, ...) +/atom/movable/openspace/turf_mimic/Initialize(mapload, ...) . = ..() ASSERT(isturf(loc)) delegate = loc:below -/atom/movable/openspace/delegate_copy/attackby(obj/item/W, mob/user) +/atom/movable/openspace/turf_mimic/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/delegate_copy/attack_hand(mob/user as mob) +/atom/movable/openspace/turf_mimic/attack_hand(mob/user as mob) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) -/atom/movable/openspace/delegate_copy/attack_generic(mob/user as mob) +/atom/movable/openspace/turf_mimic/attack_generic(mob/user as mob) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) -/atom/movable/openspace/delegate_copy/examine(mob/examiner) +/atom/movable/openspace/turf_mimic/examine(mob/examiner) SHOULD_CALL_PARENT(FALSE) . = delegate.examine(examiner) diff --git a/code/modules/multiz/zmimic/mimic_turf.dm b/code/modules/multiz/zmimic/mimic_turf.dm index 3212507a280..f5105d493cc 100644 --- a/code/modules/multiz/zmimic/mimic_turf.dm +++ b/code/modules/multiz/zmimic/mimic_turf.dm @@ -2,16 +2,23 @@ // Reference to any open turf that might be above us to speed up atom Entered() updates. var/tmp/turf/above var/tmp/turf/below - // If we're a delegate z-turf, this holds the appearance of the bottom-most Z-turf in the z-stack. - var/tmp/atom/movable/openspace/turf_delegate/bound_overlay - // Overlay used to multiply color of all OO overlays at once. + /// If we're a non-overwrite z-turf, this holds the appearance of the bottom-most Z-turf in the z-stack. + var/tmp/atom/movable/openspace/turf_proxy/mimic_proxy + /// Overlay used to multiply color of all OO overlays at once. var/tmp/atom/movable/openspace/multiplier/shadower - // If this is a delegate (non-overwrite) Z-turf with a z-turf above, this is the delegate copy that's copying us. - var/tmp/atom/movable/openspace/delegate_copy/z_delegate - var/tmp/z_queued = 0 // How many times this turf is currently queued - multiple queue occurrences are allowed to ensure update consistency + /// If this is a delegate (non-overwrite) Z-turf with a z-turf above, this is the delegate copy that's copying us. + var/tmp/atom/movable/openspace/turf_mimic/mimic_above_copy + /// If we're at the bottom of the stack, a proxy used to fake a below space turf. + var/tmp/atom/movable/openspace/turf_proxy/mimic_underlay + /// How many times this turf is currently queued - multiple queue occurrences are allowed to ensure update consistency. + var/tmp/z_queued = 0 + /// If this Z-turf leads to space, uninterrupted. var/tmp/z_eventually_space = FALSE var/z_flags = 0 + + // debug var/tmp/z_depth + var/tmp/z_generation = 0 /turf/Entered(atom/movable/thing, turf/oldLoc) . = ..() @@ -27,11 +34,10 @@ if (!(z_flags & ZM_MIMIC_BELOW)) return - if (below) - z_queued += 1 - SSzcopy.queued_turfs += src + z_queued += 1 + SSzcopy.queued_turfs += src -// Enables Z-mimic for a turf that didn't already have it enabled. +/// Enables Z-mimic for a turf that didn't already have it enabled. /turf/proc/enable_zmimic(additional_flags = 0) if (z_flags & ZM_MIMIC_BELOW) return FALSE @@ -40,15 +46,16 @@ setup_zmimic(FALSE) return TRUE -// Disables Z-mimic for a turf. +/// Disables Z-mimic for a turf. /turf/proc/disable_zmimic() if (!(z_flags & ZM_MIMIC_BELOW)) return FALSE z_flags &= ~ZM_MIMIC_BELOW cleanup_zmimic() + return TRUE -// Sets up Z-mimic for this turf. You shouldn't call this directly 99% of the time. +/// Sets up Z-mimic for this turf. You shouldn't call this directly 99% of the time. /turf/proc/setup_zmimic(mapload) if (shadower) CRASH("Attempt to enable Z-mimic on already-enabled turf!") @@ -59,21 +66,22 @@ below = under below.above = src - if (!(z_flags & ZM_MIMIC_OVERWRITE) && mouse_opacity) + if (!(z_flags & (ZM_MIMIC_OVERWRITE|ZM_NO_OCCLUDE)) && mouse_opacity) mouse_opacity = 2 update_mimic(!mapload) // Only recursively update if the map isn't loading. -// Cleans up Z-mimic objects for this turf. You shouldn't call this directly 99% of the time. +/// Cleans up Z-mimic objects for this turf. You shouldn't call this directly 99% of the time. /turf/proc/cleanup_zmimic() SSzcopy.openspace_turfs -= 1 // Don't remove ourselves from the queue, the subsystem will explode. We'll naturally fall out of the queue. z_queued = 0 QDEL_NULL(shadower) - QDEL_NULL(z_delegate) + QDEL_NULL(mimic_above_copy) + QDEL_NULL(mimic_underlay) - for (var/atom/movable/openspace/overlay/OO in src) + for (var/atom/movable/openspace/mimic/OO in src) OO.owning_turf_changed() if (above) From 31fafea81ac52aa030490ce68a0f8ffef4550452 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Tue, 1 Jun 2021 17:28:12 +0200 Subject: [PATCH 071/246] Automatic changelog generation for PR #30766 [ci skip] --- html/changelogs/AutoChangeLog-pr-30766.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30766.yml diff --git a/html/changelogs/AutoChangeLog-pr-30766.yml b/html/changelogs/AutoChangeLog-pr-30766.yml new file mode 100644 index 00000000000..e6d01198ad3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30766.yml @@ -0,0 +1,5 @@ +author: Ilysen +delete-after: true +changes: + - bugfix: You can now properly light molotov cocktails with any open flame source + like welding tools, not just lighters. From c957a30facb153745bc414ae9da3aed9daae6f31 Mon Sep 17 00:00:00 2001 From: CSCMe <19313511+CSCMe@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:28:47 +0200 Subject: [PATCH 072/246] Changes to personal goals You can now no longer have the same goal twice. You can now see the goal that was added to you when you generate a new one. Your job now specifies how many personal goals you can have (default: 0-5) The game now tells you when your preferences are set so you can't generate goals when you click on "Show Goals" More admin logging for adding/removing/rerolling goals Fixed rerolled goals sometimes disappearing without generating a new one Fixed removing/rerolling goals sometimes removing/rerolling the wrong goals --- code/datums/mind/mind.dm | 65 +++++++++++++-------------- code/game/jobs/job/job.dm | 4 +- code/modules/goals/_goal.dm | 6 +-- code/modules/goals/goal_ambition.dm | 2 +- code/modules/goals/goal_department.dm | 2 +- code/modules/goals/goal_mind.dm | 58 +++++++++++++++++------- code/modules/goals/goal_mob.dm | 17 ++++--- maps/torch/job/misc_jobs.dm | 2 + 8 files changed, 96 insertions(+), 60 deletions(-) diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 9e152962183..5d9a6ff887b 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -131,11 +131,6 @@ out += "Ambitions: [ambition ? ambition.description : "None"] \[edit\]
                " show_browser(usr, out, "window=edit_memory[src]") -/datum/mind/proc/get_goal_from_href(var/href) - var/ind = isnum(href) ? href : text2num(href) - if(ind > 0 && ind <= LAZYLEN(goals)) - return goals[ind] - /datum/mind/Topic(href, href_list) var/is_admin = FALSE @@ -146,14 +141,16 @@ if(href_list["add_goal"]) var/mob/caller = locate(href_list["add_goal_caller"]) - if(caller && caller == current) can_modify = TRUE + if(!isghost(usr) && caller && caller == current) can_modify = TRUE if(can_modify) - if(is_admin) - log_admin("[key_name_admin(usr)] added a random goal to [key_name(current)].") - var/did_generate_goal = generate_goals(assigned_job, TRUE, 1) - if(did_generate_goal) - to_chat(current, SPAN_NOTICE("You have received a new goal. Use Show Goals to view it.")) + var/did_generate_goal = generate_goals(assigned_job, TRUE, 1, bypass_goal_checks = is_admin) + if(did_generate_goal && goals) + var/datum/goal/goal = goals[LAZYLEN(goals)] + to_chat(current, SPAN_NOTICE("You have received a new goal: '[goal.summarize(FALSE, FALSE)]'.")) + if(usr != current) + to_chat(usr, SPAN_NOTICE("You have added a new goal to \the [current]: '[goal.summarize(FALSE, FALSE)]'.")) + log_admin("[key_name_admin(usr)] added a random goal to [key_name(current)].") return TRUE // To avoid 'you are not an admin' spam. if(href_list["remove_memory"]) @@ -162,36 +159,38 @@ return TRUE if(href_list["abandon_goal"]) - var/datum/goal/goal = get_goal_from_href(href_list["abandon_goal"]) + var/datum/goal/goal = locate(href_list["abandon_goal"]) var/mob/caller = locate(href_list["abandon_goal_caller"]) - if(caller && caller == current) can_modify = TRUE + if(!isghost(usr) && caller && caller == current) can_modify = TRUE - if(goal && can_modify) - if(usr == current) - to_chat(current, SPAN_NOTICE("You have abandoned your goal: '[goal.summarize(FALSE, FALSE)]'.")) - else - to_chat(usr, SPAN_NOTICE("You have removed a goal from \the [current]: '[goal.summarize(FALSE, FALSE)]'.")) - to_chat(current, SPAN_NOTICE("A goal has been removed: '[goal.summarize(FALSE, FALSE)]'.")) - qdel(goal) + if(can_modify && goal && (goal in goals)) + if(delete_goal(assigned_job, goal, is_admin)) + if(usr == current) + to_chat(current, SPAN_NOTICE("You have abandoned your goal: '[goal.summarize(FALSE, FALSE)]'.")) + else + to_chat(usr, SPAN_NOTICE("You have removed a goal from \the [current]: '[goal.summarize(FALSE, FALSE)]'.")) + to_chat(current, SPAN_NOTICE("A goal has been removed: '[goal.summarize(FALSE, FALSE)]'.")) + log_admin("[key_name_admin(usr)] removed a goal from [key_name(current)].") return TRUE if(href_list["reroll_goal"]) - var/datum/goal/goal = get_goal_from_href(href_list["reroll_goal"]) + var/datum/goal/goal = locate(href_list["reroll_goal"]) var/mob/caller = locate(href_list["reroll_goal_caller"]) - if(caller && caller == current) can_modify = TRUE - - if(goal && (goal in goals) && can_modify) - qdel(goal) - generate_goals(assigned_job, TRUE, 1) - if(goals) - goal = goals[LAZYLEN(goals)] - if(usr == current) - to_chat(usr, SPAN_NOTICE("You have re-rolled a goal. Your new goal is: '[goal.summarize(FALSE, FALSE)]'.")) - else - to_chat(usr, SPAN_NOTICE("You have re-rolled a goal for \the [current]. Their new goal is: '[goal.summarize(FALSE, FALSE)]'.")) - to_chat(current, SPAN_NOTICE("A goal has been re-rolled. Your new goal is: '[goal.summarize(FALSE, FALSE)]'.")) + if(!isghost(usr) && caller && caller == current) can_modify = TRUE + + if(can_modify && goal && (goal in goals)) + if(generate_goals(assigned_job, TRUE, 1, bypass_goal_checks = TRUE)) + delete_goal(assigned_job, goal, TRUE) + if(goals) + goal = goals[LAZYLEN(goals)] + if(usr == current) + to_chat(usr, SPAN_NOTICE("You have re-rolled a goal. Your new goal is: '[goal.summarize(FALSE, FALSE)]'.")) + else + to_chat(usr, SPAN_NOTICE("You have re-rolled a goal for \the [current]. Their new goal is: '[goal.summarize(FALSE, FALSE)]'.")) + to_chat(current, SPAN_NOTICE("A goal has been re-rolled. Your new goal is: '[goal.summarize(FALSE, FALSE)]'.")) + log_admin("[key_name_admin(usr)] rerolled a goal for [key_name(current)].") return TRUE if(!is_admin) return diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index e51962c15b7..904882f5103 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -44,8 +44,8 @@ var/available_by_default = TRUE var/list/possible_goals - var/min_goals = 1 - var/max_goals = 3 + var/min_goals = 0 + var/max_goals = 5 var/defer_roundstart_spawn = FALSE // If true, the job will be put off until all other jobs have been populated. var/list/species_branch_rank_cache_ = list() diff --git a/code/modules/goals/_goal.dm b/code/modules/goals/_goal.dm index 33f86877765..8a7b1262e2c 100644 --- a/code/modules/goals/_goal.dm +++ b/code/modules/goals/_goal.dm @@ -23,13 +23,13 @@ owner = null . = ..() -/datum/goal/proc/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller ,var/position = 1) +/datum/goal/proc/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller) . = "[description][get_summary_value()]" if(show_success) . += get_success_string() if(allow_modification) - if(can_abandon) . += " (Abandon)" - if(can_reroll) . += " (Reroll)" + if(can_abandon) . += " (Abandon)" + if(can_reroll) . += " (Reroll)" /datum/goal/proc/get_success_string() return check_success() ? " Success!" : " Failure." diff --git a/code/modules/goals/goal_ambition.dm b/code/modules/goals/goal_ambition.dm index 2029bc5cf23..9013cc3d08b 100644 --- a/code/modules/goals/goal_ambition.dm +++ b/code/modules/goals/goal_ambition.dm @@ -13,5 +13,5 @@ /datum/goal/ambition/get_success_string() return "" -/datum/goal/ambition/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller ,var/position = 1) +/datum/goal/ambition/summarize(var/show_success = FALSE, var/allow_modification = FALSE, var/mob/caller) . = SPAN_DANGER(..(show_success)) diff --git a/code/modules/goals/goal_department.dm b/code/modules/goals/goal_department.dm index e527f3da5a5..8b1744c3ba9 100644 --- a/code/modules/goals/goal_department.dm +++ b/code/modules/goals/goal_department.dm @@ -24,7 +24,7 @@ . = list() for(var/i = 1 to LAZYLEN(goals)) var/datum/goal/goal = goals[i] - . += "[i]. [goal.summarize(show_success, position = i)]" + . += "[i]. [goal.summarize(show_success)]" /datum/department/proc/update_progress(var/goal_type, var/progress) var/datum/goal/goal = locate(goal_type) in goals diff --git a/code/modules/goals/goal_mind.dm b/code/modules/goals/goal_mind.dm index a957d394742..1a401ac32d5 100644 --- a/code/modules/goals/goal_mind.dm +++ b/code/modules/goals/goal_mind.dm @@ -13,23 +13,16 @@ if(LAZYLEN(goals)) for(var/i = 1 to LAZYLEN(goals)) var/datum/goal/goal = goals[i] - . += "[i]. [goal.summarize(show_success, allow_modification, caller, position = i)]" + . += "[i]. [goal.summarize(show_success, allow_modification, caller)]" // Create and display personal goals for this round. -/datum/mind/proc/generate_goals(var/datum/job/job, var/adding_goals = FALSE, var/add_amount, var/is_spawning = FALSE) +/datum/mind/proc/generate_goals(datum/job/job, adding_goals, add_amount, is_spawning, bypass_goal_checks) if(!adding_goals) goals = null - var/pref_val = current.get_preference_value(/datum/client_preference/give_personal_goals) - if(pref_val == GLOB.PREF_NEVER || (pref_val == GLOB.PREF_NON_ANTAG && player_is_antag(src))) - if(!is_spawning) - to_chat(src.current, "Your preferences do not allow you to add random goals.") - return FALSE - var/list/available_goals = SSgoals.global_personal_goals ? SSgoals.global_personal_goals.Copy() : list() - if(job && LAZYLEN(job.possible_goals)) - available_goals |= job.possible_goals + if(ishuman(current)) var/mob/living/carbon/human/H = current for(var/token in H.cultural_info) @@ -37,15 +30,50 @@ var/list/new_goals = culture.get_possible_personal_goals(job ? job.department_flag : null) if(LAZYLEN(new_goals)) available_goals |= new_goals + + var/min_goals = 0 + var/max_goals = 5 + if(job) + min_goals = job.min_goals + max_goals = job.max_goals + if(LAZYLEN(job.possible_goals)) + available_goals |= job.possible_goals + if(isnull(add_amount)) - var/min_goals = 1 - var/max_goals = 3 - if(job) - min_goals = job.min_goals - max_goals = job.max_goals add_amount = rand(min_goals, max_goals) + + if (!bypass_goal_checks) + add_amount = min(max(0, max_goals - LAZYLEN(goals)), add_amount) + if(add_amount <= 0) + if(!is_spawning) + to_chat(src.current, SPAN_WARNING("Your job doesn't allow for any more distractions.")) + return FALSE + + var/pref_val = current.get_preference_value(/datum/client_preference/give_personal_goals) + if (pref_val == GLOB.PREF_NEVER || (pref_val == GLOB.PREF_NON_ANTAG && player_is_antag(src))) + if(!is_spawning) + to_chat(src.current, SPAN_WARNING("Your preferences do not allow you to add random goals.")) + return FALSE + + if(LAZYLEN(goals)) + for (var/datum/goal/mind_goal in goals) + LAZYREMOVE(available_goals, mind_goal.type) + if(!LAZYLEN(available_goals)) + if(!is_spawning) + to_chat(src.current, SPAN_WARNING("There are no more goals available.")) + return FALSE for(var/i = 1 to min(LAZYLEN(available_goals), add_amount)) var/goal = pick_n_take(available_goals) new goal(src) return TRUE + +/datum/mind/proc/delete_goal(datum/job/job, datum/goal/goal, override_min_goals) + var/min_goals = job ? job.min_goals : 1 + + if(!override_min_goals && LAZYLEN(goals) == min_goals) + to_chat(src.current, SPAN_WARNING("Your job needs you to have at least [min_goals] distraction\s.")) + return FALSE + else + qdel(goal) + return TRUE diff --git a/code/modules/goals/goal_mob.dm b/code/modules/goals/goal_mob.dm index 164c84e9d9f..a2f781f4c8c 100644 --- a/code/modules/goals/goal_mob.dm +++ b/code/modules/goals/goal_mob.dm @@ -20,22 +20,29 @@ to_chat(src, SPAN_WARNING("You are mindless and cannot have goals.")) return + var/max_goals = 5 var/datum/department/dept - if(mind.assigned_job && mind.assigned_job.department_flag && SSgoals.departments["[mind.assigned_job.department_flag]"]) - dept = SSgoals.departments["[mind.assigned_job.department_flag]"] + if(mind.assigned_job) + max_goals = mind.assigned_job.max_goals + if(mind.assigned_job.department_flag && SSgoals.departments["[mind.assigned_job.department_flag]"]) + dept = SSgoals.departments["[mind.assigned_job.department_flag]"] //No goals to display if(!(allow_modification || LAZYLEN(mind.goals)) && !(dept && LAZYLEN(dept.goals))) return + var/pref_val = get_preference_value(/datum/client_preference/give_personal_goals) + var/prefs_no_personal_goals = pref_val == GLOB.PREF_NEVER || (pref_val == GLOB.PREF_NON_ANTAG && player_is_antag(mind)) to_chat(src, "
                ") if(LAZYLEN(mind.goals)) to_chat(src, SPAN_NOTICE("This round, you have the following personal goals:
                [jointext(mind.summarize_goals(show_success, allow_modification, mind.current), "
                ")]")) - else + else if(prefs_no_personal_goals) + to_chat(src, SPAN_NOTICE("Your preferences do not allow for personal goals.")) + else to_chat(src, SPAN_NOTICE("You have no personal goals this round.")) - if(allow_modification && LAZYLEN(mind.goals) < 5) + if(allow_modification && !prefs_no_personal_goals && LAZYLEN(mind.goals) < max_goals) to_chat(src, SPAN_NOTICE("Add Random Goal")) - if(dept) + if(dept && get_preference_value(/datum/client_preference/show_department_goals) == GLOB.PREF_SHOW) if(LAZYLEN(dept.goals)) to_chat(src, SPAN_NOTICE("

                This round, [dept.name] has the following departmental goals:
                [jointext(dept.summarize_goals(show_success), "
                ")]")) else diff --git a/maps/torch/job/misc_jobs.dm b/maps/torch/job/misc_jobs.dm index 0e518857543..48c1f3fc2e4 100644 --- a/maps/torch/job/misc_jobs.dm +++ b/maps/torch/job/misc_jobs.dm @@ -48,6 +48,8 @@ Civilian /datum/mil_rank/civ/civ, /datum/mil_rank/civ/contractor ) + min_goals = 2 + max_goals = 7 /datum/job/merchant title = "Merchant" From 05b0e28c0490cedbabea8568316335c92ca244bf Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 1 Jun 2021 23:36:39 +0530 Subject: [PATCH 073/246] Add Pixel-Movement to Overmap --- .../machinery/_machines_base/machinery.dm | 3 + code/modules/mob/login.dm | 3 + code/modules/mob/mob.dm | 6 +- code/modules/overmap/ships/computers/ship.dm | 9 +++ code/modules/overmap/ships/ship.dm | 58 +++++++++++++------ 5 files changed, 60 insertions(+), 19 deletions(-) diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 143ee6e9366..6334ba0a758 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -461,3 +461,6 @@ Class Procs: var/obj/item/stock_parts/power/battery/battery = get_component_of_type(/obj/item/stock_parts/power/battery) if(battery) return battery.get_cell() + +/obj/machinery/proc/on_user_login(mob/M) + return diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index a84dd03785c..c0b03f467bd 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -97,6 +97,9 @@ add_click_catcher() update_action_buttons() + if(machine) + machine.on_user_login(src) + //set macro to normal incase it was overriden (like cyborg currently does) winset(src, null, "mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5") diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 692ea06f455..ae491de0eb9 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -291,6 +291,8 @@ /mob/proc/reset_view(atom/A) if (client) + client.pixel_x = initial(client.pixel_x) + client.pixel_y = initial(client.pixel_y) A = A ? A : eyeobj if (istype(A, /atom/movable)) client.perspective = EYE_PERSPECTIVE @@ -584,11 +586,11 @@ else if(H.has_organ(BP_L_ARM) || H.has_organ(BP_R_ARM)) //If they only have one arm grabtype = "arm" else //If they have no arms - grabtype = "torso" + grabtype = "torso" visible_message(SPAN_WARNING("\The [src] leans down and grips \the [H]'s [grabtype]."), SPAN_NOTICE("You lean down and grip \the [H]'s [grabtype]."), exclude_mobs = list(H)) if(!H.stat) - to_chat(H, SPAN_WARNING("\The [src] leans down and grips your [grabtype].")) + to_chat(H, SPAN_WARNING("\The [src] leans down and grips your [grabtype].")) else //Otherwise we're probably just holding their arm to lead them somewhere var/grabtype diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm index e01f1aa3294..c52210b62c5 100644 --- a/code/modules/overmap/ships/computers/ship.dm +++ b/code/modules/overmap/ships/computers/ship.dm @@ -14,8 +14,14 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov return if(sector.check_ownership(src)) linked = sector + LAZYSET(linked.consoles, src, TRUE) return 1 +/obj/machinery/computer/ship/Destroy() + if(linked) + LAZYREMOVE(linked.consoles, src) + . = ..() + /obj/machinery/computer/ship/proc/sync_linked() var/obj/effect/overmap/visitable/ship/sector = map_sectors["[z]"] if(!sector) @@ -52,6 +58,9 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov // Management of mob view displacement. look to shift view to the ship on the overmap; unlook to shift back. +/obj/machinery/computer/ship/on_user_login(mob/M) + unlook(M) + /obj/machinery/computer/ship/proc/look(var/mob/user) if(linked) user.reset_view(linked) diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm index 58669887ff7..d09e8be26ed 100644 --- a/code/modules/overmap/ships/ship.dm +++ b/code/modules/overmap/ships/ship.dm @@ -1,3 +1,4 @@ +var/const/OVERMAP_SPEED_CONSTANT = (1 SECOND) #define SHIP_MOVE_RESOLUTION 0.00001 #define MOVING(speed) abs(speed) >= min_speed #define SANITIZE_SPEED(speed) SIGN(speed) * Clamp(abs(speed), 0, max_speed) @@ -14,18 +15,19 @@ desc = "Space faring vessel." icon_state = "ship" var/moving_state = "ship_moving" + var/list/consoles - var/vessel_mass = 10000 //tonnes, arbitrary number, affects acceleration provided by engines - var/vessel_size = SHIP_SIZE_LARGE //arbitrary number, affects how likely are we to evade meteors - var/max_speed = 1/(1 SECOND) //"speed of light" for the ship, in turfs/tick. + var/vessel_mass = 10000 // tonnes, arbitrary number, affects acceleration provided by engines + var/vessel_size = SHIP_SIZE_LARGE // arbitrary number, affects how likely are we to evade meteors + var/max_speed = 1/(1 SECOND) // "speed of light" for the ship, in turfs/tick. var/min_speed = 1/(2 MINUTES) // Below this, we round speed to 0 to avoid math errors. var/max_autopilot = 1 / (20 SECONDS) // The maximum speed any attached helm can try to autopilot at. - var/list/speed = list(0,0) //speed in x,y direction - var/last_burn = 0 //worldtime when ship last acceleated - var/burn_delay = 1 SECOND //how often ship can do burns - var/list/last_movement = list(0,0) //worldtime when ship last moved in x,y direction - var/fore_dir = NORTH //what dir ship flies towards for purpose of moving stars effect procs + var/list/speed = list(0,0) // speed in x,y direction + var/list/position = list(0,0) // position within a tile. + var/last_burn = 0 // worldtime when ship last acceleated + var/burn_delay = 1 SECOND // how often ship can do burns + var/fore_dir = NORTH // what dir ship flies towards for purpose of moving stars effect procs var/list/engines = list() var/engines_state = 0 //global on/off toggle for all engines @@ -36,6 +38,7 @@ /obj/effect/overmap/visitable/ship/Initialize() . = ..() + glide_size = world.icon_size min_speed = round(min_speed, SHIP_MOVE_RESOLUTION) max_speed = round(max_speed, SHIP_MOVE_RESOLUTION) SSshuttle.ships += src @@ -44,6 +47,11 @@ /obj/effect/overmap/visitable/ship/Destroy() STOP_PROCESSING(SSobj, src) SSshuttle.ships -= src + if(LAZYLEN(consoles)) + for(var/obj/machinery/computer/ship/machine in consoles) + if(machine.linked == src) + machine.linked = null + consoles = null . = ..() /obj/effect/overmap/visitable/ship/relaymove(mob/user, direction, accel_limit) @@ -154,22 +162,38 @@ /obj/effect/overmap/visitable/ship/Process() if(!halted && !is_still()) var/list/deltas = list(0,0) - for(var/i=1, i<=2, i++) - if(MOVING(speed[i]) && world.time > last_movement[i] + 1/abs(speed[i])) - deltas[i] = SIGN(speed[i]) - last_movement[i] = world.time + for(var/i = 1 to 2) + if(MOVING(speed[i])) + position[i] += speed[i] * OVERMAP_SPEED_CONSTANT + if(position[i] < 0) + deltas[i] = ceil(position[i]) + else if(position[i] > 0) + deltas[i] = Floor(position[i]) + if(deltas[i] != 0) + position[i] -= deltas[i] + position[i] += (deltas[i] > 0) ? -1 : 1 + + update_icon() var/turf/newloc = locate(x + deltas[1], y + deltas[2], z) - if(newloc) + if(newloc && loc != newloc) Move(newloc) handle_wraparound() - update_icon() /obj/effect/overmap/visitable/ship/on_update_icon() + pixel_x = position[1] * (world.icon_size/2) + pixel_y = position[2] * (world.icon_size/2) if(!is_still()) icon_state = moving_state dir = get_heading() else icon_state = initial(icon_state) + for(var/obj/machinery/computer/ship/machine in consoles) + if(machine.z in map_z) + for(var/weakref/W in machine.viewers) + var/mob/M = W.resolve() + if(istype(M) && M.client) + M.client.pixel_x = pixel_x + M.client.pixel_y = pixel_y ..() /obj/effect/overmap/visitable/ship/proc/burn() @@ -191,10 +215,10 @@ //deciseconds to next step /obj/effect/overmap/visitable/ship/proc/ETA() . = INFINITY - for(var/i=1, i<=2, i++) + for(var/i = 1 to 2) if(MOVING(speed[i])) - . = min(last_movement[i] - world.time + 1/abs(speed[i]), .) - . = max(.,0) + . = min(., ((speed[i] > 0 ? 1 : -1) - position[i]) / speed[i]) + . = max(ceil(.),0) /obj/effect/overmap/visitable/ship/proc/handle_wraparound() var/nx = x From 7c52b21f52c9a9c1ff8aa7aa68cf71037367ea09 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Wed, 2 Jun 2021 03:06:24 +0000 Subject: [PATCH 074/246] Automatic changelog generation --- html/changelog.html | 6 ++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-30766.yml | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30766.yml diff --git a/html/changelog.html b/html/changelog.html index 2ba4328a8e9..29e379f130b 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,12 @@ -->
                +

                02 June 2021

                +

                Ilysen updated:

                +
                  +
                • You can now properly light molotov cocktails with any open flame source like welding tools, not just lighters.
                • +
                +

                01 June 2021

                SierraKomodo updated:

                  diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 6b6b9c373f2..9f42dc9f83b 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16949,3 +16949,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2021-06-01: SierraKomodo: - rscdel: Pickpocket gloves have been removed. +2021-06-02: + Ilysen: + - bugfix: You can now properly light molotov cocktails with any open flame source + like welding tools, not just lighters. diff --git a/html/changelogs/AutoChangeLog-pr-30766.yml b/html/changelogs/AutoChangeLog-pr-30766.yml deleted file mode 100644 index e6d01198ad3..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30766.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Ilysen -delete-after: true -changes: - - bugfix: You can now properly light molotov cocktails with any open flame source - like welding tools, not just lighters. From c34814eac6d15d13b883c25cbf2f7b5461f3cdd1 Mon Sep 17 00:00:00 2001 From: Mucker Date: Tue, 1 Jun 2021 22:15:34 -0700 Subject: [PATCH 075/246] mob ai fixes --- .../simple_animal/friendly/farm_animals.dm | 5 ++-- .../living/simple_animal/friendly/possum.dm | 6 ++--- .../simple_animal/hostile/retaliate/drone.dm | 4 +-- .../hostile/retaliate/exoplanet.dm | 26 ++++--------------- .../hostile/retaliate/king_of_goats.dm | 4 ++- 5 files changed, 15 insertions(+), 30 deletions(-) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index a9df36cba57..0f456416ccc 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -5,7 +5,6 @@ icon_state = "goat" icon_living = "goat" icon_dead = "goat_dead" - speak_emote = list("brays") turns_per_move = 5 see_in_dark = 6 response_help = "pets" @@ -44,7 +43,7 @@ . = ..() if(.) //chance to go crazy and start wacking stuff - if(!length(ai_holder.attackers) && prob(1)) + if(!length(ai_holder?.attackers) && prob(1)) var/list/nearby_stuff = hearers(src, ai_holder.vision_range) if (length(nearby_stuff)) ai_holder.react_to_attack(pick(nearby_stuff)) @@ -318,4 +317,4 @@ var/global/chicken_count = 0 /datum/say_list/chicken speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") emote_hear = list("clucks") - emote_see = list("pecks at the ground","flaps its wings viciously") \ No newline at end of file + emote_see = list("pecks at the ground","flaps its wings viciously") diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index 3a4d30b2c5b..1bdc2b31a5e 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -29,7 +29,7 @@ can_pull_mobs = MOB_PULL_SMALLER var/is_angry = FALSE - ai_holder_type = /datum/ai_holder/simple_animal/opossum + ai_holder_type = /datum/ai_holder/simple_animal/passive/opossum say_list_type = /datum/say_list/opossum /mob/living/simple_animal/friendly/opossum/Life() @@ -115,10 +115,10 @@ respond_to_damage() return -/datum/ai_holder/simple_animal/opossum +/datum/ai_holder/simple_animal/passive/opossum speak_chance = 1 /datum/say_list/opossum speak = list("Hiss!","Aaa!","Aaa?") emote_hear = list("hisses") - emote_see = list("forages for trash", "lounges") \ No newline at end of file + emote_see = list("forages for trash", "lounges") diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index 8bd8aecdfcf..0de6543b0e8 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -28,7 +28,7 @@ skin_material = null skin_amount = 0 - ai_holder_type = /datum/ai_holder/simple_animal/ranged/malf_drone + ai_holder_type = /datum/ai_holder/simple_animal/ranged/pointblank/malf_drone var/datum/effect/effect/system/trail/ion_trail @@ -289,7 +289,7 @@ ..() -/datum/ai_holder/simple_animal/ranged/malf_drone +/datum/ai_holder/simple_animal/ranged/pointblank/malf_drone speak_chance = 5 /datum/say_list/malf_drone diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm index 06daba76956..d00915af364 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm @@ -2,7 +2,7 @@ /mob/living/simple_animal/hostile/retaliate/beast var/hunger = 0 var/list/prey = list() - // ai_holder_type = /datum/ai_holder/simple_animal/retaliate/beast + ai_holder_type = /datum/ai_holder/simple_animal/beast /mob/living/simple_animal/hostile/retaliate/beast/Life() . = ..() @@ -218,28 +218,12 @@ say_list_type = /datum/say_list/shantak/lava -//Mob AI's - -// /datum/ai_holder/simple_animal/retaliate/beast/list_targets() -// . = ..() - -// var/mob/living/simple_animal/hostile/retaliate/beast/B = holder -// var/list/see = ..() -// if(see.len) -// return see -// if(B.prey.len) -// . = list() -// for(var/weakref/W in B.prey) -// var/mob/M = W.resolve() -// if(M) -// . += M -// return -// if(B.hunger > 500) //time to look for some food -// hostile = TRUE - /* AI */ +/datum/ai_holder/simple_animal/beast + speak_chance = 5 + /datum/ai_holder/simple_animal/diyaab/post_melee_attack(atom/A) . = ..() if(holder.Adjacent(A)) @@ -284,4 +268,4 @@ emote_see = list("skitters","oozes liquid from its mouth", "scratches at the ground", "clicks its claws") /datum/say_list/shantak/lava - speak = list("Karuph","Karump") \ No newline at end of file + speak = list("Karuph","Karump") diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm index 18a4f6fd352..0df15186baf 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm @@ -156,6 +156,8 @@ break_stuff_probability = 40 stun_chance = 7 + ai_holder_type = /datum/ai_holder/simple_animal/goat/king/phase2 + var/spellscast = 0 var/phase3 = FALSE var/datum/sound_token/boss_theme @@ -261,4 +263,4 @@ /datum/say_list/goat/king emote_hear = list("brays in a booming voice") - emote_see = list("stamps a mighty foot, shaking the surroundings") \ No newline at end of file + emote_see = list("stamps a mighty foot, shaking the surroundings") From 610dce407d0fe13abc9fac242ab0dfbd0a313245 Mon Sep 17 00:00:00 2001 From: Ilysen Date: Wed, 2 Jun 2021 15:11:07 -0400 Subject: [PATCH 076/246] build mode confirmation prompt thing --- code/modules/admin/buildmode/build.dm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/modules/admin/buildmode/build.dm b/code/modules/admin/buildmode/build.dm index 836236fdbc1..6c7af32eece 100644 --- a/code/modules/admin/buildmode/build.dm +++ b/code/modules/admin/buildmode/build.dm @@ -33,6 +33,16 @@ if (parameters["right"]) if (isturf(target)) return + if (isobserver(target)) // don't delete ghosts because it causes very weird things to happen + return + if (ismob(target)) + var/mob/M = target + if (M.ckey && !QDELETED(M)) + var/alert_result = alert(user, "[M] is a player-controlled mob. Confirm?", "Build Mode", "Yes, Delete", "Cancel") + if (alert_result != "Yes, Delete" || QDELETED(M)) + return + to_chat(M, SPAN_DEBUG(FONT_LARGE("OOC: You have been deleted by an admin using build mode. If this seems to be in error, please adminhelp and let them know."))) + M.ghostize() qdel(target) else if (parameters["left"]) if (!build_type) From c6b83b59cd2d9bb2f031454877c326fc447e5c4f Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 3 Jun 2021 13:38:39 +0200 Subject: [PATCH 077/246] Automatic changelog generation for PR #30772 [ci skip] --- html/changelogs/AutoChangeLog-pr-30772.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30772.yml diff --git a/html/changelogs/AutoChangeLog-pr-30772.yml b/html/changelogs/AutoChangeLog-pr-30772.yml new file mode 100644 index 00000000000..f2f4fc209ad --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30772.yml @@ -0,0 +1,5 @@ +author: Ilysen +delete-after: true +changes: + - rscadd: You can now browse through hairstyles with arrows in addition to selecting + a style from a list. Useful for looking around more without having to pick randomly! From a545396ddc7309b8c6f97a4f5751f74bb949b3af Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 3 Jun 2021 13:59:02 +0200 Subject: [PATCH 078/246] Automatic changelog generation for PR #30767 [ci skip] --- html/changelogs/AutoChangeLog-pr-30767.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30767.yml diff --git a/html/changelogs/AutoChangeLog-pr-30767.yml b/html/changelogs/AutoChangeLog-pr-30767.yml new file mode 100644 index 00000000000..8dda33db28a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30767.yml @@ -0,0 +1,5 @@ +author: SealCure +delete-after: true +changes: + - rscadd: Adds scienceHUD implants, buildable from the robotics fabricator and selectable + at the loadout screen. From 5d6ea0696777109c5609bcfd343af283c1baccb7 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 3 Jun 2021 13:59:16 +0200 Subject: [PATCH 079/246] Automatic changelog generation for PR #30712 [ci skip] --- html/changelogs/AutoChangeLog-pr-30712.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30712.yml diff --git a/html/changelogs/AutoChangeLog-pr-30712.yml b/html/changelogs/AutoChangeLog-pr-30712.yml new file mode 100644 index 00000000000..92dece9e0b3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30712.yml @@ -0,0 +1,5 @@ +author: Huntime +delete-after: true +changes: + - maptweak: Aligned infirmary entrance with ladders and added borders to colored + infirmary floor sections. From bb6ad36e72db0db2204261bdd821e7f813426d19 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Thu, 3 Jun 2021 14:51:18 +0200 Subject: [PATCH 080/246] Automatic changelog generation for PR #30768 [ci skip] --- html/changelogs/AutoChangeLog-pr-30768.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30768.yml diff --git a/html/changelogs/AutoChangeLog-pr-30768.yml b/html/changelogs/AutoChangeLog-pr-30768.yml new file mode 100644 index 00000000000..023f54c40f2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30768.yml @@ -0,0 +1,5 @@ +author: CrimsonShrike +delete-after: true +changes: + - tweak: Big things hurt more when they fall on you. + - tweak: Exosuits now take explosion damage correctly From b595038c3fa40cc5ba18bbb236404044d1346c79 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Fri, 4 Jun 2021 02:04:00 +0000 Subject: [PATCH 081/246] Automatic changelog generation --- html/changelog.html | 19 +++++++++++++++++++ html/changelogs/.all_changelog.yml | 13 +++++++++++++ html/changelogs/AutoChangeLog-pr-30712.yml | 5 ----- html/changelogs/AutoChangeLog-pr-30767.yml | 5 ----- html/changelogs/AutoChangeLog-pr-30768.yml | 5 ----- html/changelogs/AutoChangeLog-pr-30772.yml | 5 ----- 6 files changed, 32 insertions(+), 20 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30712.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30767.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30768.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30772.yml diff --git a/html/changelog.html b/html/changelog.html index 29e379f130b..3ad1d68facb 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,25 @@ -->
                  +

                  04 June 2021

                  +

                  CrimsonShrike updated:

                  +
                    +
                  • Big things hurt more when they fall on you.
                  • +
                  • Exosuits now take explosion damage correctly
                  • +
                  +

                  Huntime updated:

                  +
                    +
                  • Aligned infirmary entrance with ladders and added borders to colored infirmary floor sections.
                  • +
                  +

                  Ilysen updated:

                  +
                    +
                  • You can now browse through hairstyles with arrows in addition to selecting a style from a list. Useful for looking around more without having to pick randomly!
                  • +
                  +

                  SealCure updated:

                  +
                    +
                  • Adds scienceHUD implants, buildable from the robotics fabricator and selectable at the loadout screen.
                  • +
                  +

                  02 June 2021

                  Ilysen updated:

                    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 9f42dc9f83b..5f6b2301dee 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16953,3 +16953,16 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Ilysen: - bugfix: You can now properly light molotov cocktails with any open flame source like welding tools, not just lighters. +2021-06-04: + CrimsonShrike: + - tweak: Big things hurt more when they fall on you. + - tweak: Exosuits now take explosion damage correctly + Huntime: + - maptweak: Aligned infirmary entrance with ladders and added borders to colored + infirmary floor sections. + Ilysen: + - rscadd: You can now browse through hairstyles with arrows in addition to selecting + a style from a list. Useful for looking around more without having to pick randomly! + SealCure: + - rscadd: Adds scienceHUD implants, buildable from the robotics fabricator and selectable + at the loadout screen. diff --git a/html/changelogs/AutoChangeLog-pr-30712.yml b/html/changelogs/AutoChangeLog-pr-30712.yml deleted file mode 100644 index 92dece9e0b3..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30712.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Huntime -delete-after: true -changes: - - maptweak: Aligned infirmary entrance with ladders and added borders to colored - infirmary floor sections. diff --git a/html/changelogs/AutoChangeLog-pr-30767.yml b/html/changelogs/AutoChangeLog-pr-30767.yml deleted file mode 100644 index 8dda33db28a..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30767.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: SealCure -delete-after: true -changes: - - rscadd: Adds scienceHUD implants, buildable from the robotics fabricator and selectable - at the loadout screen. diff --git a/html/changelogs/AutoChangeLog-pr-30768.yml b/html/changelogs/AutoChangeLog-pr-30768.yml deleted file mode 100644 index 023f54c40f2..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30768.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: CrimsonShrike -delete-after: true -changes: - - tweak: Big things hurt more when they fall on you. - - tweak: Exosuits now take explosion damage correctly diff --git a/html/changelogs/AutoChangeLog-pr-30772.yml b/html/changelogs/AutoChangeLog-pr-30772.yml deleted file mode 100644 index f2f4fc209ad..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30772.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Ilysen -delete-after: true -changes: - - rscadd: You can now browse through hairstyles with arrows in addition to selecting - a style from a list. Useful for looking around more without having to pick randomly! From 41d0b8a9f5fa0d9fb2491e49bb7ac58e5afe3435 Mon Sep 17 00:00:00 2001 From: Mucker Date: Fri, 4 Jun 2021 15:01:34 -0700 Subject: [PATCH 082/246] runtime fixers --- code/game/machinery/doors/door.dm | 6 ++++-- code/modules/ai/say_list.dm | 6 +++++- code/modules/organs/internal/brain.dm | 5 ++++- code/modules/shield_generators/shield.dm | 3 +++ code/modules/surgery/surgery.dm | 8 ++++++-- maps/antag_spawn/heist/heist_base.dmm | 2 +- maps/torch/datums/uniforms_fleet.dm | 4 +--- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 5ca633ce676..3ad236406b3 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -214,7 +214,7 @@ // This is legacy code that should be revisited, probably by moving the bulk of the logic into here. /obj/machinery/door/interface_interact(user) if(CanInteract(user, DefaultTopicState())) - return attackby(user, user) + return attackby(user = user) /obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) src.add_fingerprint(user, 0, I) @@ -243,7 +243,7 @@ else repairing = stack.split(amount_needed) if (repairing) - repairing.dropInto(src) + repairing.forceMove(src) transfer = repairing.amount if (transfer) @@ -307,6 +307,8 @@ return 1 /obj/machinery/door/proc/check_force(obj/item/I, mob/user) + if (!istype(I)) + return FALSE if (!density || user.a_intent != I_HURT) return FALSE if (I.damtype != BRUTE && I.damtype != BURN) diff --git a/code/modules/ai/say_list.dm b/code/modules/ai/say_list.dm index 773c0e78172..313a9468a21 100644 --- a/code/modules/ai/say_list.dm +++ b/code/modules/ai/say_list.dm @@ -122,4 +122,8 @@ speak = list("Hi","Hello!","Cracker?") emote_hear = list("squawks","bawks") - emote_see = list("flutters its wings") \ No newline at end of file + emote_see = list("flutters its wings") + +/* For Meatbag */ +/datum/say_list/parrot/heist + speak = list("Yaaar!","Squaaak!","Fight me Matey!","BAWWWWK Vox trying to eat me!") diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 688906ec410..0c393e92875 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -216,6 +216,9 @@ addtimer(CALLBACK(src, .proc/brain_damage_callback, damage), rand(6, 20) SECONDS, TIMER_UNIQUE) /obj/item/organ/internal/brain/proc/brain_damage_callback(var/damage) //Confuse them as a somewhat uncommon aftershock. Side note: Only here so a spawn isn't used. Also, for the sake of a unique timer. + if (!owner) + return + to_chat(owner, "I can't remember which way is forward...") owner.confused += damage @@ -264,4 +267,4 @@ . = (species.total_health - max_damage)/species.total_health /obj/item/organ/internal/brain/get_mechanical_assisted_descriptor() - return "machine-interface [name]" \ No newline at end of file + return "machine-interface [name]" diff --git a/code/modules/shield_generators/shield.dm b/code/modules/shield_generators/shield.dm index b5f38dea58c..cc67a309177 100644 --- a/code/modules/shield_generators/shield.dm +++ b/code/modules/shield_generators/shield.dm @@ -82,6 +82,9 @@ /obj/effect/shield/proc/diffuse(var/duration) + if (!gen) + return + // The shield is trying to counter diffusers. Cause lasting stress on the shield. if(gen.check_flag(MODEFLAG_BYPASS) && !disabled_for) take_damage(duration * rand(8, 12), SHIELD_DAMTYPE_EM) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index e5b5ae0d148..b4e710bbbb4 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -228,8 +228,12 @@ GLOBAL_LIST_INIT(surgery_tool_exception_cache, new) var/skill_reqs = S.get_skill_reqs(user, M, src, zone) var/duration = user.skill_delay_mult(skill_reqs[1]) * rand(S.min_duration, S.max_duration) if(prob(S.success_chance(user, M, src, zone)) && do_after(user, duration, M)) - S.end_step(user, M, zone, src) - handle_post_surgery() + if (S.can_use(user, M, zone, src)) + S.end_step(user, M, zone, src) + handle_post_surgery() + else + to_chat(user, SPAN_WARNING("The patient lost the target organ before you could finish operating!")) + else if ((src in user.contents) && user.Adjacent(M)) S.fail_step(user, M, zone, src) else diff --git a/maps/antag_spawn/heist/heist_base.dmm b/maps/antag_spawn/heist/heist_base.dmm index a198e4d22f2..f5cb3218989 100644 --- a/maps/antag_spawn/heist/heist_base.dmm +++ b/maps/antag_spawn/heist/heist_base.dmm @@ -960,7 +960,7 @@ available_channels = list(); ears = list(); name = "\proper Meatbag"; - speak = list("Yaaar!","Squaaak!","Fight me Matey!","BAWWWWK Vox trying to eat me!") + say_list_type = /datum/say_list/parrot/heist }, /turf/unsimulated/floor{ icon_state = "asteroid" diff --git a/maps/torch/datums/uniforms_fleet.dm b/maps/torch/datums/uniforms_fleet.dm index f8d7dd32115..e946bf84d3e 100644 --- a/maps/torch/datums/uniforms_fleet.dm +++ b/maps/torch/datums/uniforms_fleet.dm @@ -57,9 +57,7 @@ service_hat = /obj/item/clothing/head/solgov/dress/fleet service_over = /obj/item/clothing/suit/storage/solgov/service/fleet - dress_over = list( - /obj/item/clothing/suit/storage/solgov/dress/fleet - ) + dress_over = /obj/item/clothing/suit/storage/solgov/dress/fleet /decl/hierarchy/mil_uniform/fleet/eng/snco name = "Fleet engineering SNCO" From 329c86610304120e706b8fbedb9802ed8e053450 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sat, 5 Jun 2021 19:29:36 +0530 Subject: [PATCH 083/246] Add robotised stomach --- code/modules/organs/internal/stomach.dm | 16 ++++++++++------ icons/obj/surgery.dmi | Bin 37246 -> 37919 bytes 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index d43ed8b1862..6becb16e6fa 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -33,6 +33,10 @@ ingested.my_atom = owner ingested.parent = owner +/obj/item/organ/internal/stomach/robotize() + . = ..() + icon_state = "stomach-prosthetic" + /obj/item/organ/internal/stomach/proc/can_eat_atom(var/atom/movable/food) return !isnull(get_devour_time(food)) @@ -91,9 +95,9 @@ /obj/item/organ/internal/stomach/proc/metabolize() if(is_usable()) ingested.metabolize() - + #define STOMACH_VOLUME 65 - + /obj/item/organ/internal/stomach/Process() ..() @@ -121,18 +125,18 @@ owner.custom_pain("Your stomach cramps agonizingly!",1) var/alcohol_volume = ingested.get_reagent_amount(/datum/reagent/ethanol) - + var/alcohol_threshold_met = alcohol_volume > STOMACH_VOLUME / 2 if(alcohol_threshold_met && (owner.disabilities & EPILEPSY) && prob(20)) owner.seizure() - + // Alcohol counts as double volume for the purposes of vomit probability var/effective_volume = ingested.total_volume + alcohol_volume - + // Just over the limit, the probability will be low. It rises a lot such that at double ingested it's 64% chance. var/vomit_probability = (effective_volume / STOMACH_VOLUME) ** 6 if(prob(vomit_probability)) owner.vomit() #undef STOMACH_VOLUME -#undef PUKE_ACTION_NAME \ No newline at end of file +#undef PUKE_ACTION_NAME diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index da78c889ccc7c1b781151128ab9622cad7c1c976..3e86b787765f471fc36ac249f392edf553f30028 100644 GIT binary patch literal 37919 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 From 99e8794ee3c5aba88bc1102a72bae2a05de0a3f4 Mon Sep 17 00:00:00 2001 From: Archemagus <32466328+Archemagus@users.noreply.github.com> Date: Sun, 6 Jun 2021 11:49:01 +0300 Subject: [PATCH 084/246] Update misc.dm --- code/__defines/misc.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index b4958c8a575..c076730e675 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -5,6 +5,7 @@ #define TRANSITIONEDGE 7 // Distance from edge to move to another z-level. #define RUIN_MAP_EDGE_PAD 15 +#define LANDING_ZONE_RADIUS 15 // Used for autoplacing landmarks on exoplanets // Invisibility constants. #define INVISIBILITY_LIGHTING 20 From 4096b4a33c11640e0c52f82253d1e10dee445ea9 Mon Sep 17 00:00:00 2001 From: Archemagus <32466328+Archemagus@users.noreply.github.com> Date: Sun, 6 Jun 2021 11:52:48 +0300 Subject: [PATCH 085/246] Update _exoplanet.dm --- code/modules/overmap/exoplanets/_exoplanet.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/overmap/exoplanets/_exoplanet.dm b/code/modules/overmap/exoplanets/_exoplanet.dm index 8373b7bb394..b241e12daf2 100644 --- a/code/modules/overmap/exoplanets/_exoplanet.dm +++ b/code/modules/overmap/exoplanets/_exoplanet.dm @@ -241,12 +241,12 @@ GLOBAL_VAR(planet_repopulation_disabled) var/new_type = landmark_type while(num) attempts-- - var/turf/T = locate(rand(20, maxx - 30), rand(20, maxy - 30),map_z[map_z.len]) + var/turf/T = locate(rand(TRANSITIONEDGE + LANDING_ZONE_RADIUS, maxx - TRANSITIONEDGE - LANDING_ZONE_RADIUS), rand(TRANSITIONEDGE + LANDING_ZONE_RADIUS, maxy - TRANSITIONEDGE - LANDING_ZONE_RADIUS),map_z[map_z.len]) if (!T || (T in places)) // Two landmarks on one turf is forbidden as the landmark code doesn't work with it. continue if (attempts >= 0) // While we have the patience, try to find better spawn points. If out of patience, put them down wherever, so long as there are no repeats. var/valid = TRUE - var/list/block_to_check = block(locate(T.x - 15, T.y - 15, T.z), locate(T.x + 15, T.y + 15, T.z)) + var/list/block_to_check = block(locate(T.x - LANDING_ZONE_RADIUS, T.y - LANDING_ZONE_RADIUS, T.z), locate(T.x + LANDING_ZONE_RADIUS, T.y + LANDING_ZONE_RADIUS, T.z)) for (var/turf/check in block_to_check) if (!istype(get_area(check), /area/exoplanet) || check.turf_flags & TURF_FLAG_NORUINS) valid = FALSE From f5fe65515479333b9d7b69d4cb078ffc7e3f63b5 Mon Sep 17 00:00:00 2001 From: Archemagus <32466328+Archemagus@users.noreply.github.com> Date: Sun, 6 Jun 2021 11:53:49 +0300 Subject: [PATCH 086/246] Update landmarks.dm --- code/modules/shuttles/landmarks.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm index 767c34f1457..6ae3ae38258 100644 --- a/code/modules/shuttles/landmarks.dm +++ b/code/modules/shuttles/landmarks.dm @@ -110,7 +110,7 @@ //Subtype that calls explosion on init to clear space for shuttles /obj/effect/shuttle_landmark/automatic/clearing - var/radius = 15 + var/radius = LANDING_ZONE_RADIUS /obj/effect/shuttle_landmark/automatic/clearing/Initialize() ..() @@ -153,4 +153,4 @@ /obj/item/device/spaceflare/on_update_icon() if(active) icon_state = "bluflare_on" - set_light(0.3, 0.1, 6, 2, "85d1ff") \ No newline at end of file + set_light(0.3, 0.1, 6, 2, "85d1ff") From cebe19b7010f811d72793442c37e0e7da44eee9e Mon Sep 17 00:00:00 2001 From: Archemagus <32466328+Archemagus@users.noreply.github.com> Date: Sun, 6 Jun 2021 12:00:21 +0300 Subject: [PATCH 087/246] Maybe that's better --- code/modules/shuttles/landmarks.dm | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm index 6ae3ae38258..fb1fc912b81 100644 --- a/code/modules/shuttles/landmarks.dm +++ b/code/modules/shuttles/landmarks.dm @@ -109,16 +109,13 @@ SetName("[O.name] - [initial(name)] ([x],[y])") //Subtype that calls explosion on init to clear space for shuttles -/obj/effect/shuttle_landmark/automatic/clearing - var/radius = LANDING_ZONE_RADIUS - /obj/effect/shuttle_landmark/automatic/clearing/Initialize() ..() return INITIALIZE_HINT_LATELOAD /obj/effect/shuttle_landmark/automatic/clearing/LateInitialize() ..() - for(var/turf/T in range(radius, src)) + for(var/turf/T in range(LANDING_ZONE_RADIUS, src)) if(T.density) T.ChangeTurf(get_base_turf_by_area(T)) From 6cc2d0bd45eec04fde935543c501e74b80788fec Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 6 Jun 2021 12:22:46 +0200 Subject: [PATCH 088/246] Automatic changelog generation for PR #30777 [ci skip] --- html/changelogs/AutoChangeLog-pr-30777.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30777.yml diff --git a/html/changelogs/AutoChangeLog-pr-30777.yml b/html/changelogs/AutoChangeLog-pr-30777.yml new file mode 100644 index 00000000000..c2ac83caf91 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30777.yml @@ -0,0 +1,7 @@ +author: Ilysen +delete-after: true +changes: + - bugfix: Being deleted in build mode should no longer crash your game. + - tweak: 'For admins: build mode now has a confirmation prompt if you attempt to + delete a player with an active ckey, and gives them a unique message informing + them about what happened if you do.' From ee607b9d1880895d8439d7d26a52a74e0e956cc5 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Sun, 6 Jun 2021 13:25:33 +0200 Subject: [PATCH 089/246] Automatic changelog generation for PR #30786 [ci skip] --- html/changelogs/AutoChangeLog-pr-30786.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30786.yml diff --git a/html/changelogs/AutoChangeLog-pr-30786.yml b/html/changelogs/AutoChangeLog-pr-30786.yml new file mode 100644 index 00000000000..0001084556c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30786.yml @@ -0,0 +1,4 @@ +author: Ryan180602 +delete-after: true +changes: + - tweak: Add robotic stomach sprite (from Azlan) for Stomachs from Prosthetic Fabricators. From 36fc162bd0265fda4ad7dd07d5a3cd97c4c0cb6d Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 7 Jun 2021 01:41:38 +0000 Subject: [PATCH 090/246] Automatic changelog generation --- html/changelog.html | 11 +++++++++++ html/changelogs/.all_changelog.yml | 8 ++++++++ html/changelogs/AutoChangeLog-pr-30777.yml | 7 ------- html/changelogs/AutoChangeLog-pr-30786.yml | 4 ---- 4 files changed, 19 insertions(+), 11 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-30777.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-30786.yml diff --git a/html/changelog.html b/html/changelog.html index 3ad1d68facb..cf3d53bc645 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,17 @@ -->
                    +

                    07 June 2021

                    +

                    Ilysen updated:

                    +
                      +
                    • Being deleted in build mode should no longer crash your game.
                    • +
                    • For admins: build mode now has a confirmation prompt if you attempt to delete a player with an active ckey, and gives them a unique message informing them about what happened if you do.
                    • +
                    +

                    Ryan180602 updated:

                    +
                      +
                    • Add robotic stomach sprite (from Azlan) for Stomachs from Prosthetic Fabricators.
                    • +
                    +

                    04 June 2021

                    CrimsonShrike updated:

                      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 5f6b2301dee..026c6a6c5d2 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -16966,3 +16966,11 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. SealCure: - rscadd: Adds scienceHUD implants, buildable from the robotics fabricator and selectable at the loadout screen. +2021-06-07: + Ilysen: + - bugfix: Being deleted in build mode should no longer crash your game. + - tweak: 'For admins: build mode now has a confirmation prompt if you attempt to + delete a player with an active ckey, and gives them a unique message informing + them about what happened if you do.' + Ryan180602: + - tweak: Add robotic stomach sprite (from Azlan) for Stomachs from Prosthetic Fabricators. diff --git a/html/changelogs/AutoChangeLog-pr-30777.yml b/html/changelogs/AutoChangeLog-pr-30777.yml deleted file mode 100644 index c2ac83caf91..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30777.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: Ilysen -delete-after: true -changes: - - bugfix: Being deleted in build mode should no longer crash your game. - - tweak: 'For admins: build mode now has a confirmation prompt if you attempt to - delete a player with an active ckey, and gives them a unique message informing - them about what happened if you do.' diff --git a/html/changelogs/AutoChangeLog-pr-30786.yml b/html/changelogs/AutoChangeLog-pr-30786.yml deleted file mode 100644 index 0001084556c..00000000000 --- a/html/changelogs/AutoChangeLog-pr-30786.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Ryan180602 -delete-after: true -changes: - - tweak: Add robotic stomach sprite (from Azlan) for Stomachs from Prosthetic Fabricators. From 0e52a74b9521db9e3d8d89890a6ba67f93fa9776 Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 7 Jun 2021 11:53:09 +0200 Subject: [PATCH 091/246] Automatic changelog generation for PR #30718 [ci skip] --- html/changelogs/AutoChangeLog-pr-30718.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30718.yml diff --git a/html/changelogs/AutoChangeLog-pr-30718.yml b/html/changelogs/AutoChangeLog-pr-30718.yml new file mode 100644 index 00000000000..c3e2df5b732 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30718.yml @@ -0,0 +1,10 @@ +author: CSCMe +delete-after: true +changes: + - tweak: Clicking "Show Goals" tells you that they are disabled in preferences if + they are. + - tweak: Jobs allow 0 to 5 personal goals by default, instead of 1 to 3. + - bugfix: Removing/re-rolling goals acts on the correct goals if used more than + once. + - bugfix: You cannot roll the same goal twice in the same set, or no goal if re-rolling + one. From 3446ed1df98eb1503eac6135adffd5eaf771a55e Mon Sep 17 00:00:00 2001 From: Bay12Bot Date: Mon, 7 Jun 2021 12:02:30 +0200 Subject: [PATCH 092/246] Automatic changelog generation for PR #30785 [ci skip] --- html/changelogs/AutoChangeLog-pr-30785.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-30785.yml diff --git a/html/changelogs/AutoChangeLog-pr-30785.yml b/html/changelogs/AutoChangeLog-pr-30785.yml new file mode 100644 index 00000000000..b66f1b05d7d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-30785.yml @@ -0,0 +1,4 @@ +author: Mucker +delete-after: true +changes: + - bugfix: Fix fleet jacket entry in the uniform vendor. From 0576190a86be1c0acf5f5b38697b752e73507eba Mon Sep 17 00:00:00 2001 From: Mucker Date: Mon, 7 Jun 2021 11:08:38 -0700 Subject: [PATCH 093/246] gives life seeded planets less extreme temperature variance --- code/modules/overmap/exoplanets/planet_types/grass.dm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/modules/overmap/exoplanets/planet_types/grass.dm b/code/modules/overmap/exoplanets/planet_types/grass.dm index 192956cb2b1..5aa9997979b 100644 --- a/code/modules/overmap/exoplanets/planet_types/grass.dm +++ b/code/modules/overmap/exoplanets/planet_types/grass.dm @@ -96,6 +96,12 @@ /obj/effect/overmap/visitable/sector/exoplanet/grass/terraformed/generate_habitability() habitability_class = HABITABILITY_IDEAL +/obj/effect/overmap/visitable/sector/exoplanet/grass/terraformed/generate_atmosphere() + ..() + if(atmosphere) + atmosphere.temperature = T0C + rand(0, 50) + atmosphere.update_values() + /obj/effect/overmap/visitable/sector/exoplanet/grass/generate_map() lightlevel = rand(0.7,0.9)/10 ..() From 86aa017441091bcc267cc45665022198e78b0219 Mon Sep 17 00:00:00 2001 From: Mucker Date: Mon, 7 Jun 2021 13:02:18 -0700 Subject: [PATCH 094/246] fix character panel icon not refreshing in some instances --- code/modules/client/preferences.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8827209d418..b9db6e8070a 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -151,6 +151,7 @@ datum/preferences /datum/preferences/proc/open_setup_window(mob/user) if (!SScharacter_setup.initialized) return + var/datum/browser/popup = new(user, "preferences_browser", "Character Setup", 1200, 800, src) var/content = {"