diff --git a/_maps/RandomRuins/IceRuins/fluffy/icemoon_interdyne_base_ff.dmm b/_maps/RandomRuins/IceRuins/fluffy/icemoon_interdyne_base_ff.dmm
index 8cde42e1356..8f189099807 100644
--- a/_maps/RandomRuins/IceRuins/fluffy/icemoon_interdyne_base_ff.dmm
+++ b/_maps/RandomRuins/IceRuins/fluffy/icemoon_interdyne_base_ff.dmm
@@ -13,6 +13,10 @@
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4,
/obj/item/mmi/posibrain/syndie/interdyne,
+/obj/item/clothing/neck/link_scryer{
+ pixel_x = 12;
+ pixel_y = 7
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"ag" = (
@@ -262,17 +266,13 @@
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
"bv" = (
-/obj/structure/frame/machine{
- anchored = 1;
- state = 2;
- icon_state = "box_1"
- },
/obj/item/circuitboard/machine/ore_redemption,
/obj/item/assembly/igniter,
/obj/structure/marker_beacon/burgundy,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/cable,
+/obj/structure/frame/machine/secured,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"bz" = (
@@ -308,6 +308,23 @@
},
/turf/open/floor/iron/dark/textured,
/area/ruin/interdyne_planetary_base/serv)
+"bK" = (
+/obj/machinery/camera/xray{
+ pixel_x = 10;
+ name = "Inderdyne security camera";
+ network = list("Interdyne");
+ color = "#adadcd";
+ pixel_y = 25
+ },
+/obj/machinery/vending/wallmed{
+ products = list(/obj/item/reagent_containers/pill/patch/libital = 5, /obj/item/reagent_containers/pill/patch/aiuri = 5, /obj/item/reagent_containers/pill/multiver = 2, /obj/item/storage/box/bandages = 4);
+ displayed_currency_name = " soviet ruble";
+ name = "Interdyne NanoMed";
+ light_color = "#adadcd";
+ color = "#adadcd"
+ },
+/turf/closed/wall/r_wall/syndicate/nodiagonal,
+/area/ruin/interdyne_planetary_base)
"bM" = (
/obj/effect/turf_decal/stripes/red/line,
/obj/effect/turf_decal/trimline/dark/filled/corner{
@@ -429,15 +446,11 @@
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main)
"cm" = (
-/obj/structure/frame/machine{
- anchored = 1;
- state = 2;
- icon_state = "box_1"
- },
/obj/item/circuitboard/machine/ore_silo,
/obj/structure/marker_beacon/burgundy,
/obj/structure/sign/flag/syndicate/directional/north,
/obj/effect/turf_decal/tile/dark/full,
+/obj/structure/frame/machine/secured,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main/vault)
"cn" = (
@@ -609,6 +622,14 @@
/obj/structure/table/wood/fancy/black,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
+"da" = (
+/obj/structure/marker_beacon/burgundy,
+/obj/structure/sign/poster/contraband/syndicate_medical/directional/south{
+ name = "Interdyne Pharmaceutics";
+ desc = "Beware of perimeter turrets.'"
+ },
+/turf/open/misc/asteroid/snow/icemoon,
+/area/icemoon/underground/explored)
"db" = (
/obj/structure/sign/poster/contraband/c20r/directional/south,
/obj/effect/turf_decal/trimline/dark/filled/line,
@@ -843,6 +864,12 @@
},
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/main)
+"ei" = (
+/obj/structure/fence/end{
+ dir = 4
+ },
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"ej" = (
/obj/machinery/portable_atmospherics/canister/bz,
/obj/machinery/atmospherics/components/unary/portables_connector,
@@ -1153,9 +1180,6 @@
/obj/effect/turf_decal/trimline/dark/filled/mid_joiner,
/obj/structure/marker_beacon/indigo,
/obj/structure/drain,
-/obj/effect/turf_decal/box/white/corners{
- dir = 8
- },
/obj/effect/turf_decal/trimline/dark/filled/line,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
@@ -1247,6 +1271,13 @@
},
/turf/open/floor/iron/dark/textured,
/area/ruin/interdyne_planetary_base/serv)
+"gH" = (
+/obj/effect/turf_decal/weather/snow/corner,
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 1
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"gL" = (
/obj/effect/turf_decal/siding/wood{
dir = 9
@@ -1359,7 +1390,7 @@
pixel_y = -3
},
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -1454,6 +1485,12 @@
/obj/structure/table/wood/fancy/black,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
+"io" = (
+/obj/structure/fence/corner{
+ dir = 1
+ },
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"it" = (
/obj/structure/bookcase/random/adult,
/turf/open/floor/wood/large,
@@ -1626,6 +1663,7 @@
},
/obj/machinery/shower/directional/east,
/obj/structure/drain,
+/obj/structure/sign/warning/cold_temp/directional/west,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"jy" = (
@@ -1875,6 +1913,12 @@
/obj/effect/baseturf_helper/reinforced_plating/ceiling,
/turf/closed/wall/r_wall/syndicate/nodiagonal,
/area/ruin/interdyne_planetary_base/main/vault)
+"lg" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 10
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"ln" = (
/obj/effect/turf_decal/trimline/dark/filled/end,
/obj/effect/turf_decal/box/red/corners{
@@ -1911,6 +1955,12 @@
/obj/structure/extinguisher_cabinet/directional/west,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"lz" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 8
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"lD" = (
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/marker_beacon/indigo,
@@ -1948,7 +1998,7 @@
/obj/machinery/light/directional/south{
light_color = "#5148A6"
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"lZ" = (
/obj/effect/turf_decal/siding/wood{
@@ -1975,14 +2025,10 @@
/turf/open/misc/asteroid/snow/icemoon,
/area/icemoon/underground/explored)
"mg" = (
-/obj/machinery/quantumpad{
- map_pad_id = "interdyne";
- map_pad_link_id = "ds2";
- name = "quantum pad to DS-2"
- },
-/obj/structure/cable,
-/turf/open/floor/iron/dark/textured_large,
-/area/ruin/interdyne_planetary_base/cargo)
+/obj/structure/marker_beacon/burgundy,
+/obj/structure/flora/grass/both/style_random,
+/turf/open/misc/asteroid/snow/icemoon,
+/area/icemoon/underground/explored)
"mp" = (
/obj/structure/marker_beacon/burgundy,
/obj/structure/railing{
@@ -2490,6 +2536,7 @@
dir = 5
},
/obj/structure/hedge,
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"oN" = (
@@ -2507,7 +2554,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/north{
pixel_y = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"oO" = (
/obj/structure/table/optable,
@@ -2663,6 +2710,12 @@
},
/turf/open/floor/plating/elevatorshaft,
/area/ruin/interdyne_planetary_base/cargo)
+"pv" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 5
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"pF" = (
/obj/structure/chair/comfy/black{
dir = 4
@@ -2825,12 +2878,6 @@
/obj/structure/marker_beacon/bronze,
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/cable,
-/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
- icon_state = "syndicrate";
- base_icon_state = "syndicrate";
- req_access = list("syndicate")
- },
/obj/item/card/id/advanced/chameleon/black,
/obj/item/card/id/advanced/chameleon/black,
/obj/item/card/id/advanced/chameleon/black,
@@ -2850,6 +2897,12 @@
/obj/item/clothing/glasses/night,
/obj/item/clothing/glasses/night,
/obj/item/clothing/glasses/night,
+/obj/structure/closet/crate/secure/gear{
+ desc = "A secute crate with huge mark 'to Surface' on it.";
+ icon_state = "syndicrate";
+ base_icon_state = "syndicrate";
+ req_access = list("syndicate")
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"qK" = (
@@ -3058,7 +3111,8 @@
/area/ruin/interdyne_planetary_base/serv)
"ss" = (
/obj/machinery/vending/hydronutrients{
- onstation = 0
+ onstation = 0;
+ extended_inventory = 1
},
/obj/effect/turf_decal/tile/dark/full,
/turf/open/floor/iron/dark/textured_large,
@@ -3413,11 +3467,19 @@
"uk" = (
/obj/structure/rack/shelf,
/obj/effect/turf_decal/tile/dark/full,
-/obj/item/storage/bag/ore,
-/obj/item/shovel,
+/obj/item/shovel{
+ throw_speed = 7;
+ throwforce = 7;
+ tk_throw_range = 18;
+ throw_range = 9;
+ attack_speed = 24;
+ name = "Bladed battle shovel";
+ desc = "A large tool for digging, and breaching skulls."
+ },
/obj/item/pickaxe,
-/obj/item/t_scanner/adv_mining_scanner/lesser,
/obj/item/pickaxe/drill,
+/obj/item/storage/bag/ore,
+/obj/item/t_scanner/adv_mining_scanner/lesser,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"um" = (
@@ -3604,6 +3666,9 @@
/obj/item/restraints/legcuffs/beartrap,
/obj/item/restraints/legcuffs/beartrap,
/obj/item/restraints/legcuffs/beartrap,
+/obj/item/storage/toolbox/fishing{
+ pixel_y = 7
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"vz" = (
@@ -3741,7 +3806,7 @@
/obj/effect/turf_decal/bot_red,
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -4344,10 +4409,18 @@
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"Am" = (
-/obj/machinery/suit_storage_unit/mining,
/obj/effect/turf_decal/tile/dark/full,
+/obj/machinery/vending/wardrobe/syndie_wardrobe/interdyne{
+ extended_inventory = 1
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"Au" = (
+/obj/structure/fence/corner{
+ dir = 4
+ },
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"AF" = (
/obj/machinery/light/directional/north{
bulb_colour = "#AC3CFF";
@@ -4480,6 +4553,13 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
+"BM" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 5
+ },
+/obj/effect/turf_decal/weather/snow/corner,
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"BO" = (
/obj/effect/turf_decal/siding/wood/corner{
dir = 8
@@ -4548,7 +4628,7 @@
/obj/effect/turf_decal/trimline/dark/filled/mid_joiner{
dir = 8
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"Cc" = (
/obj/effect/turf_decal/stripes/red/line{
@@ -4753,7 +4833,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/north{
pixel_y = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"CZ" = (
/obj/effect/turf_decal/trimline/dark/filled/corner{
@@ -4894,11 +4974,24 @@
/obj/effect/mapping_helpers/apc/syndicate_access,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"DN" = (
+/obj/structure/flora/grass/brown/style_random,
+/obj/structure/shipping_container/interdyne,
+/turf/open/misc/asteroid/snow/icemoon,
+/area/icemoon/underground/explored)
+"DQ" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 1
+ },
+/obj/effect/turf_decal/weather/snow/corner,
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"DT" = (
/obj/effect/turf_decal/siding/wood{
dir = 9
},
/obj/structure/hedge,
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"DX" = (
@@ -5249,6 +5342,10 @@
/obj/effect/turf_decal/stripes/red/corner,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"FI" = (
+/obj/structure/fence/corner,
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"FK" = (
/obj/structure/chair/sofa/bench/left{
dir = 4
@@ -5409,6 +5506,7 @@
/obj/effect/turf_decal/siding/wood{
dir = 9
},
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"Gv" = (
@@ -5497,7 +5595,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/east{
pixel_x = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"GM" = (
/obj/effect/turf_decal/stripes/red/line{
@@ -5527,8 +5625,14 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/north{
pixel_y = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
+"GV" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 6
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"GZ" = (
/obj/structure/railing{
dir = 8
@@ -5559,6 +5663,15 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
+"Hl" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 6
+ },
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 8
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"Hn" = (
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/plumbing/input{
@@ -5609,6 +5722,7 @@
dir = 4
},
/obj/effect/turf_decal/box/white/corners,
+/obj/structure/sign/warning/gas_mask/directional/east,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"HF" = (
@@ -5808,6 +5922,12 @@
/obj/structure/sign/poster/contraband/the_griffin/directional/north,
/turf/open/floor/grass,
/area/ruin/interdyne_planetary_base/serv)
+"Jb" = (
+/obj/structure/fence/end{
+ dir = 8
+ },
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"Jd" = (
/obj/effect/turf_decal/weather/snow/corner{
dir = 10
@@ -5854,7 +5974,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/cable,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -5869,6 +5989,11 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"JV" = (
+/obj/structure/flora/rock/pile/icy/style_random,
+/obj/structure/flora/tree/pine/style_random,
+/turf/open/misc/asteroid/snow/icemoon,
+/area/icemoon/underground/explored)
"Kb" = (
/obj/effect/turf_decal/arrows/red,
/obj/effect/turf_decal/box/red/corners,
@@ -5981,6 +6106,12 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main)
+"KM" = (
+/obj/structure/fence/corner{
+ dir = 9
+ },
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"KP" = (
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/vending/medical/syndicate_access{
@@ -6071,6 +6202,12 @@
},
/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/main)
+"Lh" = (
+/obj/structure/fence{
+ dir = 4
+ },
+/turf/closed/mineral/random/snow/underground,
+/area/icemoon/underground/explored)
"Li" = (
/obj/structure/chair/stool/bar{
dir = 4
@@ -6220,7 +6357,10 @@
/obj/structure/bodycontainer/morgue{
dir = 8
},
-/obj/item/bodybag,
+/obj/item/bodybag{
+ pixel_x = 5;
+ pixel_y = 9
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/med)
"LP" = (
@@ -6460,6 +6600,12 @@
},
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/underground/explored)
+"NI" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 9
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"NJ" = (
/obj/effect/turf_decal/trimline/dark/filled/line,
/obj/effect/turf_decal/trimline/dark/filled/mid_joiner,
@@ -6574,15 +6720,17 @@
/area/ruin/interdyne_planetary_base/main)
"OD" = (
/obj/structure/rack,
-/obj/item/rcd_ammo,
-/obj/item/rcd_ammo,
-/obj/item/rcd_ammo,
-/obj/item/rcd_ammo,
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
/obj/item/flatpacked_machine,
-/obj/item/construction/rcd/loaded/interdyne,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo,
+/obj/item/construction/rcd/loaded/interdyne{
+ name = "interdyne RCD"
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"OH" = (
@@ -6607,6 +6755,9 @@
pixel_y = 7
},
/obj/item/storage/fancy/egg_box,
+/obj/effect/turf_decal/box/white/corners{
+ dir = 8
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
"OI" = (
@@ -6647,6 +6798,18 @@
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/table/reinforced/plastitaniumglass,
/obj/item/storage/backpack/duffelbag/synth_treatment_kit/trauma/advanced/unzipped,
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/med)
"OV" = (
@@ -6720,6 +6883,15 @@
/obj/structure/cable,
/turf/open/floor/plating/reinforced,
/area/ruin/interdyne_planetary_base/main)
+"Ps" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 10
+ },
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 1
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"Pt" = (
/obj/structure/dresser{
color = "#8d8d8d";
@@ -6927,6 +7099,16 @@
},
/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/science)
+"RL" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 10
+ },
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 4;
+ layer = 3
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"RM" = (
/turf/closed/wall/r_wall/syndicate,
/area/ruin/interdyne_planetary_base/serv)
@@ -7231,8 +7413,8 @@
/area/ruin/interdyne_planetary_base/serv)
"TC" = (
/obj/structure/rack/shelf,
-/obj/item/storage/toolbox/syndicate,
/obj/effect/turf_decal/tile/dark/full,
+/obj/item/storage/toolbox/syndicate,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"TH" = (
@@ -7488,15 +7670,17 @@
/turf/open/floor/engine/o2,
/area/ruin/interdyne_planetary_base/main)
"Vr" = (
-/obj/machinery/vending/wardrobe/syndie_wardrobe{
- onstation = 0;
- extended_inventory = 1
- },
/obj/structure/marker_beacon/burgundy,
/obj/effect/turf_decal/tile/dark/full,
/obj/effect/turf_decal/stripes/blue{
dir = 8
},
+/obj/machinery/vending/wardrobe/syndie_wardrobe{
+ onstation = 0;
+ extended_inventory = 1;
+ name = "SusDrobe";
+ desc = "A suspicious wardrobe with suspicious wares. Nothing to look at here."
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"Vs" = (
@@ -7631,6 +7815,7 @@
syndicate_network = 1;
color = "#adadcd"
},
+/obj/machinery/newscaster/directional/west,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"VM" = (
@@ -7687,6 +7872,8 @@
network = list("Interdyne")
},
/obj/item/bedsheet/syndie,
+/obj/item/stack/sheet/mineral/wood/fifty,
+/obj/item/lighter,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"Wa" = (
@@ -7694,10 +7881,30 @@
/obj/effect/turf_decal/trimline/dark/filled/corner,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"Wj" = (
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 9
+ },
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 4;
+ layer = 3
+ },
+/turf/open/misc/ice/icemoon,
+/area/icemoon/underground/explored)
"Wm" = (
/obj/effect/baseturf_helper/reinforced_plating,
/turf/closed/wall/r_wall/syndicate/nodiagonal,
/area/ruin/interdyne_planetary_base/main/vault)
+"Wo" = (
+/obj/machinery/camera/xray{
+ pixel_x = 10;
+ name = "Inderdyne security camera";
+ network = list("Interdyne");
+ color = "#adadcd";
+ pixel_y = 25
+ },
+/turf/closed/wall/r_wall/syndicate/nodiagonal,
+/area/ruin/interdyne_planetary_base)
"Wt" = (
/obj/structure/table/reinforced/plastitaniumglass,
/obj/machinery/reagentgrinder{
@@ -7753,7 +7960,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -7834,9 +8041,11 @@
/turf/open/floor/iron/dark/textured,
/area/ruin/interdyne_planetary_base/serv/rstrm)
"WU" = (
-/turf/open/misc/ice/smooth{
- initial_gas_mix = "ICEMOON_ATMOS"
+/obj/effect/turf_decal/weather/snow/corner{
+ dir = 4;
+ layer = 3
},
+/turf/open/misc/ice/icemoon,
/area/icemoon/underground/explored)
"WX" = (
/obj/effect/turf_decal/trimline/dark/filled/line{
@@ -7914,9 +8123,7 @@
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
"Xw" = (
-/obj/structure/rack/wooden,
-/obj/item/stack/sheet/mineral/wood/fifty,
-/obj/item/lighter,
+/obj/structure/weightmachine/weightlifter,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"Xz" = (
@@ -8062,7 +8269,7 @@
"Ym" = (
/obj/structure/table/reinforced/plastitaniumglass,
/obj/effect/turf_decal/tile/dark/full,
-/obj/item/disk/tech_disk,
+/obj/item/forcefield_projector,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"Yv" = (
@@ -8123,7 +8330,8 @@
/area/ruin/interdyne_planetary_base/serv)
"YM" = (
/obj/machinery/vending/hydroseeds{
- onstation = 0
+ onstation = 0;
+ extended_inventory = 1
},
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/marker_beacon/olive,
@@ -8154,7 +8362,7 @@
},
/obj/structure/cable,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -8292,6 +8500,12 @@
/obj/structure/table/reinforced/plastitaniumglass,
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
+/obj/item/stock_parts/subspace/amplifier{
+ desc = "Cybernetic spine replacement early prototype, that in one day will be able to sustain direct connection between human and station network systems, to fulfill ai duties.";
+ name = "Wavefield Science spinal cord implant prototype";
+ light_color = "#7d7fFF";
+ color = "#7d7fFF"
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"Zx" = (
@@ -8527,9 +8741,9 @@ Se
Se
Iy
dM
-WU
-WU
-WU
+NI
+lz
+Hl
Iy
Se
Iy
@@ -8579,15 +8793,15 @@ Iy
eE
Iy
SM
-WU
-WU
-WU
+Wj
+lz
+RL
Se
Iy
Se
Iy
-WU
-WU
+pv
+GV
eE
Iy
dM
@@ -8639,7 +8853,7 @@ Iy
eE
Iy
Se
-WU
+BM
Iy
SH
Iy
@@ -8659,7 +8873,7 @@ Iy
Iy
JM
Se
-Iy
+eE
SH
ac
ac
@@ -8723,7 +8937,7 @@ Iy
dM
Iy
Im
-Iy
+eE
ac
ac
ac
@@ -8745,7 +8959,7 @@ SH
Se
Iy
Im
-WU
+Ps
SM
Se
Iy
@@ -8804,7 +9018,7 @@ Iy
Jx
Iy
Se
-WU
+gH
SH
Iy
Iy
@@ -8844,7 +9058,7 @@ Se
Iy
Iy
Iy
-Iy
+eE
ac
ac
ac
@@ -8863,7 +9077,7 @@ Iy
Iy
eE
Iy
-WU
+BM
Iy
Iy
eE
@@ -9022,7 +9236,7 @@ ab
Iy
Iy
SM
-WU
+Ps
SH
ac
ac
@@ -9081,14 +9295,14 @@ ab
ab
Iy
Se
-WU
+BM
ac
ac
ac
aa
"}
(13,1,1) = {"
-ac
+ei
Iy
eE
Iy
@@ -9147,7 +9361,7 @@ ac
aa
"}
(14,1,1) = {"
-ac
+Lh
ac
ac
Iy
@@ -9206,7 +9420,7 @@ ac
ac
"}
(15,1,1) = {"
-ac
+Lh
ac
ac
Im
@@ -9265,9 +9479,9 @@ ac
ac
"}
(16,1,1) = {"
+Lh
ac
-ac
-Se
+Zx
dM
Se
Iy
@@ -9317,14 +9531,14 @@ ab
ab
ab
Iy
-Iy
eE
Iy
Iy
+eE
ac
"}
(17,1,1) = {"
-ac
+Lh
ac
Se
SM
@@ -9383,13 +9597,13 @@ ac
ac
"}
(18,1,1) = {"
-ac
-SH
+Lh
+JV
Se
Iy
dM
SH
-WU
+Ps
Iy
Iy
eE
@@ -9442,13 +9656,13 @@ ac
ac
"}
(19,1,1) = {"
-ac
+Lh
Im
Iy
eE
Iy
Iy
-WU
+BM
Se
Iy
Iy
@@ -9501,7 +9715,7 @@ ac
ac
"}
(20,1,1) = {"
-ac
+Lh
SH
Iy
Iy
@@ -9560,7 +9774,7 @@ ac
ac
"}
(21,1,1) = {"
-ac
+Lh
Iy
eE
Iy
@@ -9619,13 +9833,13 @@ ac
ac
"}
(22,1,1) = {"
-ac
+Lh
Zx
Iy
uQ
SH
Se
-Se
+Iy
SH
Iy
ab
@@ -9678,13 +9892,13 @@ Se
ac
"}
(23,1,1) = {"
-ac
+Lh
ac
Iy
Se
Iy
Iy
-Iy
+dM
ab
ab
ab
@@ -9737,8 +9951,8 @@ ac
ac
"}
(24,1,1) = {"
-ac
-ac
+Au
+io
Se
Iy
mN
@@ -9796,8 +10010,8 @@ SH
ac
"}
(25,1,1) = {"
-Im
-ac
+mg
+bK
SH
Iy
MR
@@ -9934,7 +10148,7 @@ Fk
JN
PO
PO
-mg
+PO
Kb
bv
er
@@ -9973,7 +10187,7 @@ ac
ac
"}
(28,1,1) = {"
-Iy
+SH
NF
Iy
sO
@@ -10032,16 +10246,16 @@ ac
ac
"}
(29,1,1) = {"
-Im
-ac
+da
+Wo
dM
Iy
kr
ab
-Iy
+SH
Iy
tT
-Iy
+dM
ab
ab
mp
@@ -10150,10 +10364,10 @@ ac
ac
"}
(31,1,1) = {"
-ac
-ac
+KM
+FI
SH
-Se
+DN
Iy
SH
SH
@@ -10209,9 +10423,9 @@ ac
ac
"}
(32,1,1) = {"
+Lh
ac
Iy
-eE
Iy
SM
Iy
@@ -10268,7 +10482,7 @@ SM
ac
"}
(33,1,1) = {"
-ac
+Lh
eE
Im
SH
@@ -10327,7 +10541,7 @@ Im
ac
"}
(34,1,1) = {"
-ac
+Lh
ac
Iy
eE
@@ -10386,7 +10600,7 @@ SH
ac
"}
(35,1,1) = {"
-ac
+Jb
ac
ac
Jx
@@ -10512,8 +10726,8 @@ eE
Im
Iy
SH
-WU
-WU
+NI
+lg
Iy
Iy
ab
@@ -10571,9 +10785,9 @@ Iy
Iy
eE
Se
+pv
WU
-WU
-WU
+RL
Iy
dM
ab
@@ -10967,7 +11181,7 @@ ab
dM
Iy
Iy
-WU
+Ps
Im
ac
ac
@@ -11026,7 +11240,7 @@ Iy
Iy
Iy
SH
-WU
+BM
dM
ac
ac
@@ -11113,7 +11327,7 @@ SM
Iy
Se
Se
-WU
+Ps
Iy
Iy
ab
@@ -11167,12 +11381,12 @@ aa
ac
ac
ac
-Iy
+eE
Se
Iy
Iy
dM
-WU
+DQ
Iy
Iy
dM
@@ -11227,14 +11441,14 @@ aa
ac
ac
ac
-Iy
+eE
Iy
Im
Iy
-WU
+BM
dM
Iy
-Iy
+eE
Iy
ab
ab
diff --git a/_maps/RandomRuins/LavaRuins/fluffy/lavaland_interdyne_base_ff.dmm b/_maps/RandomRuins/LavaRuins/fluffy/lavaland_interdyne_base_ff.dmm
index a59f914bdd4..adc63dfebd8 100644
--- a/_maps/RandomRuins/LavaRuins/fluffy/lavaland_interdyne_base_ff.dmm
+++ b/_maps/RandomRuins/LavaRuins/fluffy/lavaland_interdyne_base_ff.dmm
@@ -653,6 +653,12 @@
/obj/structure/cable,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/main)
+"dU" = (
+/obj/structure/fence/corner{
+ dir = 1
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"dW" = (
/obj/effect/turf_decal/box/white/corners{
dir = 8
@@ -759,9 +765,6 @@
/obj/effect/turf_decal/trimline/dark/filled/mid_joiner,
/obj/structure/marker_beacon/indigo,
/obj/structure/drain,
-/obj/effect/turf_decal/box/white/corners{
- dir = 8
- },
/obj/effect/turf_decal/trimline/dark/filled/line,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
@@ -865,6 +868,9 @@
/obj/item/restraints/legcuffs/beartrap,
/obj/item/restraints/legcuffs/beartrap,
/obj/item/restraints/legcuffs/beartrap,
+/obj/item/storage/toolbox/fishing{
+ pixel_y = 7
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"fe" = (
@@ -1073,7 +1079,7 @@
},
/obj/structure/cable,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -1117,9 +1123,7 @@
/turf/open/floor/carpet/red,
/area/ruin/interdyne_planetary_base/serv)
"gR" = (
-/obj/structure/rack/wooden,
-/obj/item/stack/sheet/mineral/wood/fifty,
-/obj/item/lighter,
+/obj/structure/weightmachine/weightlifter,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"gU" = (
@@ -1172,6 +1176,7 @@
/obj/effect/turf_decal/siding/wood{
dir = 9
},
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"hh" = (
@@ -1303,6 +1308,15 @@
/obj/structure/cable,
/turf/open/floor/catwalk_floor/iron_dark,
/area/ruin/interdyne_planetary_base/main)
+"hX" = (
+/obj/machinery/computer/shuttle/interdyne_cargo{
+ dir = 2;
+ icon_screen = "syndishuttle";
+ icon_keyboard = "syndie_key";
+ pixel_y = -5
+ },
+/turf/closed/wall/r_wall/syndicate/nodiagonal,
+/area/ruin/interdyne_planetary_base/cargo)
"hY" = (
/obj/structure/flora/ash/stem_shroom,
/turf/open/misc/asteroid/basalt/lava_land_surface,
@@ -1360,17 +1374,13 @@
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main/dorms)
"iu" = (
-/obj/structure/frame/machine{
- anchored = 1;
- state = 2;
- icon_state = "box_1"
- },
/obj/item/circuitboard/machine/ore_redemption,
/obj/item/assembly/igniter,
/obj/structure/marker_beacon/burgundy,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/cable,
+/obj/structure/frame/machine/secured,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"iw" = (
@@ -2349,11 +2359,12 @@
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main)
"oh" = (
-/obj/structure/railing{
- dir = 1
+/obj/structure/marker_beacon/burgundy,
+/obj/structure/sign/poster/contraband/syndicate_medical/directional/south{
+ name = "Interdyne Pharmaceutics";
+ desc = "Beware of perimeter turrets.'"
},
-/obj/structure/lattice/catwalk/mining,
-/turf/open/lava/smooth/lava_land_surface,
+/turf/open/misc/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"on" = (
/obj/effect/turf_decal/stripes/red/line{
@@ -2366,7 +2377,7 @@
/obj/effect/turf_decal/trimline/dark/filled/mid_joiner{
dir = 8
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"or" = (
/obj/effect/baseturf_helper/reinforced_plating,
@@ -2762,7 +2773,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -2914,6 +2925,9 @@
pixel_y = 7
},
/obj/item/storage/fancy/egg_box,
+/obj/effect/turf_decal/box/white/corners{
+ dir = 8
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/serv)
"rw" = (
@@ -3010,6 +3024,8 @@
dir = 1
},
/obj/item/bedsheet/syndie,
+/obj/item/stack/sheet/mineral/wood/fifty,
+/obj/item/lighter,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"se" = (
@@ -3215,6 +3231,7 @@
syndicate_network = 1;
color = "#adadcd"
},
+/obj/machinery/newscaster/directional/west,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"uk" = (
@@ -3372,7 +3389,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/north{
pixel_y = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"vy" = (
/obj/item/tank/internals/oxygen/yellow,
@@ -3407,6 +3424,7 @@
},
/obj/structure/drain,
/obj/machinery/shower/directional/east,
+/obj/structure/sign/warning/hot_temp/directional/west,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"vF" = (
@@ -3724,6 +3742,10 @@
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4,
/obj/item/mmi/posibrain/syndie/interdyne,
+/obj/item/clothing/neck/link_scryer{
+ pixel_x = 12;
+ pixel_y = 7
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"xy" = (
@@ -3840,20 +3862,22 @@
/turf/open/floor/iron/dark/textured,
/area/ruin/interdyne_planetary_base/serv/rstrm)
"xZ" = (
-/obj/machinery/quantumpad{
- map_pad_id = "interdyne";
- map_pad_link_id = "ds2";
- name = "quantum pad to DS-2"
+/obj/structure/lattice/catwalk/mining,
+/obj/structure/railing{
+ dir = 1
},
-/obj/structure/cable,
-/turf/open/floor/iron/dark/textured_large,
-/area/ruin/interdyne_planetary_base/cargo)
+/obj/structure/shipping_container/interdyne,
+/turf/open/lava/smooth/lava_land_surface,
+/area/lavaland/surface/outdoors)
"yc" = (
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/bodycontainer/morgue{
dir = 8
},
-/obj/item/bodybag,
+/obj/item/bodybag{
+ pixel_x = 5;
+ pixel_y = 9
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/med)
"yi" = (
@@ -3873,7 +3897,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/north{
pixel_y = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"yj" = (
/obj/effect/turf_decal/trimline/dark/filled/line{
@@ -3899,6 +3923,12 @@
/obj/structure/bookcase/random/religion,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
+"yz" = (
+/obj/structure/fence/end{
+ dir = 4
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"yA" = (
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/marker_beacon/indigo,
@@ -3913,6 +3943,12 @@
/obj/structure/rack/shelf,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main/dorms)
+"yB" = (
+/obj/structure/fence/corner{
+ dir = 9
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"yE" = (
/obj/structure/railing{
dir = 9
@@ -3929,6 +3965,18 @@
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/table/reinforced/plastitaniumglass,
/obj/item/storage/backpack/duffelbag/synth_treatment_kit/trauma/advanced/unzipped,
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
+/obj/item/borg/sight/hud/med{
+ pixel_y = 9
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/med)
"yI" = (
@@ -4034,15 +4082,17 @@
/area/ruin/interdyne_planetary_base/science)
"zh" = (
/obj/structure/rack,
-/obj/item/rcd_ammo,
-/obj/item/rcd_ammo,
-/obj/item/rcd_ammo,
-/obj/item/rcd_ammo,
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
/obj/item/flatpacked_machine,
-/obj/item/construction/rcd/loaded/interdyne,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo,
+/obj/item/rcd_ammo,
+/obj/item/construction/rcd/loaded/interdyne{
+ name = "interdyne RCD"
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"zm" = (
@@ -4081,13 +4131,8 @@
dir = 9
},
/obj/effect/turf_decal/sand/plating/volcanic,
-/obj/machinery/computer/shuttle/interdyne_cargo{
- dir = 4;
- icon_screen = "syndishuttle";
- icon_keyboard = "syndie_key"
- },
/turf/open/floor/engine/hull/reinforced/lavaland,
-/area/ruin/interdyne_planetary_base)
+/area/lavaland/surface/outdoors)
"zv" = (
/obj/machinery/suit_storage_unit/mining,
/obj/structure/marker_beacon/burgundy,
@@ -4122,6 +4167,12 @@
/obj/effect/turf_decal/sand/plating/volcanic,
/turf/open/floor/plating/reinforced/lavaland,
/area/lavaland/surface/outdoors)
+"zS" = (
+/obj/structure/fence/end{
+ dir = 8
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"zV" = (
/obj/item/flashlight{
pixel_x = -5;
@@ -4150,7 +4201,7 @@
/obj/effect/turf_decal/bot_red,
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -4331,6 +4382,7 @@
/obj/effect/turf_decal/siding/wood{
dir = 5
},
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"Bl" = (
@@ -4484,6 +4536,7 @@
dir = 4
},
/obj/effect/turf_decal/box/white/corners,
+/obj/structure/sign/warning/gas_mask/directional/east,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"Cn" = (
@@ -4505,7 +4558,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/cable,
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -4570,10 +4623,18 @@
"CS" = (
/obj/structure/rack/shelf,
/obj/effect/turf_decal/tile/dark/full,
-/obj/item/storage/bag/ore,
-/obj/item/pickaxe/drill,
-/obj/item/shovel,
+/obj/item/shovel{
+ throw_speed = 7;
+ throwforce = 7;
+ tk_throw_range = 18;
+ throw_range = 9;
+ attack_speed = 24;
+ name = "Bladed battle shovel";
+ desc = "A large tool for digging, and breaching skulls."
+ },
/obj/item/pickaxe,
+/obj/item/pickaxe/drill,
+/obj/item/storage/bag/ore,
/obj/item/t_scanner/adv_mining_scanner/lesser,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
@@ -4689,12 +4750,6 @@
/obj/structure/marker_beacon/bronze,
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/cable,
-/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
- icon_state = "syndicrate";
- base_icon_state = "syndicrate";
- req_access = list("syndicate")
- },
/obj/item/card/id/advanced/chameleon/black,
/obj/item/card/id/advanced/chameleon/black,
/obj/item/card/id/advanced/chameleon/black,
@@ -4714,6 +4769,12 @@
/obj/item/clothing/glasses/night,
/obj/item/clothing/glasses/night,
/obj/item/clothing/glasses/night,
+/obj/structure/closet/crate/secure/gear{
+ desc = "A secute crate with huge mark 'to Surface' on it.";
+ icon_state = "syndicrate";
+ base_icon_state = "syndicrate";
+ req_access = list("syndicate")
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"DT" = (
@@ -5031,8 +5092,10 @@
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"FQ" = (
-/obj/machinery/suit_storage_unit/mining,
/obj/effect/turf_decal/tile/dark/full,
+/obj/machinery/vending/wardrobe/syndie_wardrobe/interdyne{
+ extended_inventory = 1
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"FS" = (
@@ -5056,20 +5119,17 @@
/turf/closed/wall/r_wall/syndicate/nodiagonal,
/area/ruin/interdyne_planetary_base/serv)
"Gc" = (
-/obj/structure/frame/machine{
- anchored = 1;
- state = 2;
- icon_state = "box_1"
- },
/obj/item/circuitboard/machine/ore_silo,
/obj/structure/marker_beacon/burgundy,
/obj/structure/sign/flag/syndicate/directional/north,
/obj/effect/turf_decal/tile/dark/full,
+/obj/structure/frame/machine/secured,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main/vault)
"Gf" = (
/obj/machinery/vending/hydronutrients{
- onstation = 0
+ onstation = 0;
+ extended_inventory = 1
},
/obj/effect/turf_decal/tile/dark/full,
/turf/open/floor/iron/dark/textured_large,
@@ -5658,6 +5718,12 @@
/obj/structure/cable,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/main)
+"JC" = (
+/obj/structure/fence/corner{
+ dir = 4
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"JJ" = (
/obj/effect/turf_decal/trimline/dark/filled/corner{
dir = 4
@@ -5890,7 +5956,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/north{
pixel_y = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"KN" = (
/obj/effect/turf_decal/siding/wood{
@@ -6043,6 +6109,23 @@
},
/turf/closed/wall/r_wall/syndicate/nodiagonal,
/area/ruin/interdyne_planetary_base)
+"LI" = (
+/obj/machinery/camera/xray{
+ pixel_x = 10;
+ name = "Inderdyne security camera";
+ network = list("Interdyne");
+ color = "#adadcd";
+ pixel_y = 25
+ },
+/obj/machinery/vending/wallmed{
+ products = list(/obj/item/reagent_containers/pill/patch/libital = 5, /obj/item/reagent_containers/pill/patch/aiuri = 5, /obj/item/reagent_containers/pill/multiver = 2, /obj/item/storage/box/bandages = 4);
+ displayed_currency_name = " soviet ruble";
+ name = "Interdyne NanoMed";
+ light_color = "#adadcd";
+ color = "#adadcd"
+ },
+/turf/closed/wall/r_wall/syndicate/nodiagonal,
+/area/ruin/interdyne_planetary_base)
"LQ" = (
/obj/effect/turf_decal/siding/wood{
dir = 4
@@ -6089,6 +6172,7 @@
dir = 5
},
/obj/structure/hedge,
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"LX" = (
@@ -6244,7 +6328,6 @@
/obj/effect/turf_decal/trimline/dark/filled/line,
/obj/effect/turf_decal/trimline/dark/filled/mid_joiner,
/obj/effect/turf_decal/stripes/red/line,
-/obj/structure/sign/poster/contraband/syndicate_medical/directional/north,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
"Nf" = (
@@ -6268,6 +6351,12 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"Nm" = (
+/obj/structure/fence{
+ dir = 4
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"Nq" = (
/obj/structure/marker_beacon/teal,
/obj/effect/decal/cleanable/glitter/blue,
@@ -6378,17 +6467,29 @@
/turf/open/floor/catwalk_floor/iron_dark,
/area/ruin/interdyne_planetary_base/main)
"NU" = (
-/obj/machinery/vending/wardrobe/syndie_wardrobe{
- onstation = 0;
- extended_inventory = 1
- },
/obj/structure/marker_beacon/burgundy,
/obj/effect/turf_decal/tile/dark/full,
/obj/effect/turf_decal/stripes/blue{
dir = 8
},
+/obj/machinery/vending/wardrobe/syndie_wardrobe{
+ onstation = 0;
+ extended_inventory = 1;
+ name = "SusDrobe";
+ desc = "A suspicious wardrobe with suspicious wares. Nothing to look at here."
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/cargo)
+"NW" = (
+/obj/machinery/camera/xray{
+ pixel_x = 10;
+ name = "Inderdyne security camera";
+ network = list("Interdyne");
+ color = "#adadcd";
+ pixel_y = 25
+ },
+/turf/closed/wall/r_wall/syndicate/nodiagonal,
+/area/ruin/interdyne_planetary_base)
"Oc" = (
/obj/effect/turf_decal/siding/wood,
/turf/open/floor/wood/large,
@@ -6407,7 +6508,7 @@
pixel_y = -3
},
/obj/structure/closet/crate/secure/gear{
- desc = "A secure gear crate, with the Syndicate logo on it.";
+ desc = "A secute crate with huge mark 'to Surface' on it.";
icon_state = "syndicrate";
base_icon_state = "syndicrate";
req_access = list("syndicate")
@@ -6473,7 +6574,7 @@
/obj/machinery/light/directional/south{
light_color = "#5148A6"
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"Ox" = (
/obj/effect/turf_decal/trimline/dark/filled/line{
@@ -6798,7 +6899,8 @@
/area/ruin/interdyne_planetary_base/main)
"PY" = (
/obj/machinery/vending/hydroseeds{
- onstation = 0
+ onstation = 0;
+ extended_inventory = 1
},
/obj/effect/turf_decal/tile/dark/full,
/obj/structure/marker_beacon/olive,
@@ -6830,6 +6932,12 @@
/obj/structure/cable,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/main)
+"Qc" = (
+/obj/structure/fence/cut/large{
+ dir = 4
+ },
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"Qi" = (
/obj/effect/turf_decal/trimline/dark/filled/line{
dir = 9
@@ -7112,6 +7220,12 @@
/obj/structure/table/reinforced/plastitaniumglass,
/obj/effect/turf_decal/tile/dark/full,
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
+/obj/item/stock_parts/subspace/amplifier{
+ desc = "Cybernetic spine replacement early prototype, that in one day will be able to sustain direct connection between human and station network systems, to fulfill ai duties.";
+ name = "Wavefield Science spinal cord implant prototype";
+ light_color = "#7d7fFF";
+ color = "#7d7fFF"
+ },
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"Sf" = (
@@ -7363,6 +7477,7 @@
dir = 9
},
/obj/structure/hedge,
+/obj/item/decoration/new_year/garland,
/turf/open/floor/wood/large,
/area/ruin/interdyne_planetary_base/serv)
"TS" = (
@@ -7580,7 +7695,7 @@
"Vi" = (
/obj/structure/table/reinforced/plastitaniumglass,
/obj/effect/turf_decal/tile/dark/full,
-/obj/item/disk/tech_disk,
+/obj/item/forcefield_projector,
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
"Vl" = (
@@ -7619,6 +7734,10 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/ruin/interdyne_planetary_base/science)
+"Vr" = (
+/obj/structure/fence/corner,
+/turf/closed/mineral/random/volcanic,
+/area/lavaland/surface/outdoors)
"Vv" = (
/obj/effect/turf_decal/siding/wood/corner{
dir = 4
@@ -8245,7 +8364,7 @@
/obj/structure/window/reinforced/survival_pod/spawner/directional/east{
pixel_x = 4
},
-/turf/open/floor/iron/dark/textured_large,
+/turf/open/floor/engine,
/area/ruin/interdyne_planetary_base/med)
"Zg" = (
/obj/structure/table/wood/fancy/black,
@@ -8933,7 +9052,7 @@ Co
CH
"}
(10,1,1) = {"
-Co
+yz
Co
eO
eO
@@ -8992,7 +9111,7 @@ Co
CH
"}
(11,1,1) = {"
-Co
+Nm
BO
ku
pp
@@ -9051,7 +9170,7 @@ Co
CH
"}
(12,1,1) = {"
-Co
+Nm
BO
zn
yq
@@ -9110,7 +9229,7 @@ Co
CH
"}
(13,1,1) = {"
-Co
+Nm
BO
zn
nr
@@ -9169,7 +9288,7 @@ Co
CH
"}
(14,1,1) = {"
-Co
+Qc
BO
Ed
uH
@@ -9228,7 +9347,7 @@ Co
Co
"}
(15,1,1) = {"
-Co
+Nm
BO
yt
nr
@@ -9287,8 +9406,8 @@ BO
Co
"}
(16,1,1) = {"
-Co
-eO
+Nm
+BO
Ak
yq
OG
@@ -9346,9 +9465,9 @@ BO
Co
"}
(17,1,1) = {"
-Co
-eO
-oh
+Nm
+BO
+yt
OG
nr
OG
@@ -9405,8 +9524,8 @@ ZT
Co
"}
(18,1,1) = {"
-Co
-eO
+Nm
+BO
Ak
uH
nr
@@ -9464,9 +9583,9 @@ Co
Co
"}
(19,1,1) = {"
-Co
-eO
-oh
+Nm
+BO
+yt
nr
BZ
OG
@@ -9523,7 +9642,7 @@ Co
Co
"}
(20,1,1) = {"
-Co
+Nm
BO
Bx
yq
@@ -9582,7 +9701,7 @@ Co
Co
"}
(21,1,1) = {"
-Co
+Nm
fU
sf
KR
@@ -9641,12 +9760,12 @@ fU
Co
"}
(22,1,1) = {"
-Co
+Nm
hY
BO
-eO
-eO
-MQ
+BO
+BO
+xZ
pN
eO
eO
@@ -9700,11 +9819,11 @@ BO
Co
"}
(23,1,1) = {"
-Co
+Nm
Co
BO
ZT
-eO
+BO
MQ
pN
eO
@@ -9759,11 +9878,11 @@ Co
Co
"}
(24,1,1) = {"
-Co
-Co
+JC
+dU
+BO
+BO
BO
-eO
-eO
MQ
pN
eO
@@ -9819,11 +9938,11 @@ Co
"}
(25,1,1) = {"
fU
-Co
+LI
ZT
BO
zr
-MQ
+pu
pN
eO
yE
@@ -9956,7 +10075,7 @@ DP
Eh
mj
mj
-xZ
+mj
SR
iu
kk
@@ -10054,8 +10173,8 @@ Co
Co
"}
(29,1,1) = {"
-fU
-Co
+oh
+NW
BO
BO
pn
@@ -10113,8 +10232,8 @@ Co
Co
"}
(30,1,1) = {"
-Co
-Co
+yB
+Vr
Nv
BO
eO
@@ -10129,7 +10248,7 @@ eO
vm
vm
vm
-vm
+hX
Na
zV
Od
@@ -10172,7 +10291,7 @@ Co
Co
"}
(31,1,1) = {"
-Co
+Nm
Co
BO
BO
@@ -10231,7 +10350,7 @@ Co
Co
"}
(32,1,1) = {"
-Co
+Nm
uy
BO
BO
@@ -10290,9 +10409,9 @@ Jt
Co
"}
(33,1,1) = {"
-Co
+Nm
fU
-BO
+ZT
ZT
BO
Jt
@@ -10349,8 +10468,8 @@ fU
Co
"}
(34,1,1) = {"
-Co
-ZT
+Nm
+BO
Co
BO
Co
@@ -10408,7 +10527,7 @@ ZT
Co
"}
(35,1,1) = {"
-Co
+zS
Co
Co
Co
diff --git a/_maps/RandomRuins/SpaceRuins/nova/piratefort.dmm b/_maps/RandomRuins/SpaceRuins/nova/piratefort.dmm
index b0770c8277a..ef04bb90071 100644
--- a/_maps/RandomRuins/SpaceRuins/nova/piratefort.dmm
+++ b/_maps/RandomRuins/SpaceRuins/nova/piratefort.dmm
@@ -281,7 +281,7 @@
/turf/open/floor/carpet,
/area/ruin/space/has_grav/powered)
"kb" = (
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space)
"kh" = (
/obj/structure/safe,
@@ -433,7 +433,7 @@
/turf/closed/wall/r_wall/syndicate/nodiagonal,
/area/ruin/space/has_grav/powered)
"oZ" = (
-/turf/open/floor/engine,
+/turf/open/floor/engine/airless,
/area/ruin/space)
"pu" = (
/mob/living/basic/trooper/syndicate/ranged{
@@ -1192,7 +1192,7 @@
/area/ruin/space/has_grav/powered)
"Ph" = (
/obj/structure/marker_beacon/burgundy,
-/turf/open/floor/engine,
+/turf/open/floor/engine/airless,
/area/ruin/space)
"Qc" = (
/obj/effect/turf_decal/trimline/dark_red/filled/corner{
@@ -1282,7 +1282,7 @@
/area/ruin/space/has_grav/powered)
"TK" = (
/mob/living/basic/trooper/pirate/ranged/space,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space)
"TO" = (
/obj/machinery/door/airlock/vault{
@@ -1361,7 +1361,7 @@
/area/ruin/space/has_grav/powered)
"VI" = (
/mob/living/basic/trooper/pirate/melee/space,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space)
"VL" = (
/mob/living/basic/trooper/pirate/melee,
diff --git a/_maps/RandomRuins/SpaceRuins/nova/syndibase.dmm b/_maps/RandomRuins/SpaceRuins/nova/syndibase.dmm
index 38e6203163a..adb022be52e 100644
--- a/_maps/RandomRuins/SpaceRuins/nova/syndibase.dmm
+++ b/_maps/RandomRuins/SpaceRuins/nova/syndibase.dmm
@@ -12,7 +12,7 @@
/turf/open/floor/iron/dark,
/area/ruin/space/has_grav/powered)
"aZ" = (
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space)
"be" = (
/obj/effect/spawner/random/trash/bin,
@@ -57,10 +57,10 @@
/area/ruin/space/has_grav/powered)
"dY" = (
/obj/structure/chair/sofa/corp/left,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"ei" = (
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"eo" = (
/obj/machinery/atmospherics/components/tank/air{
@@ -92,7 +92,7 @@
/area/ruin/space/has_grav/powered)
"hE" = (
/mob/living/basic/trooper/syndicate/ranged/space/anthro/cat,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"if" = (
/mob/living/basic/trooper/syndicate/ranged,
@@ -167,7 +167,7 @@
/area/ruin/space/has_grav/powered)
"pL" = (
/obj/structure/table/reinforced,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"qo" = (
/obj/structure/table/reinforced,
@@ -189,7 +189,7 @@
/area/ruin/space/has_grav/powered)
"tD" = (
/obj/structure/chair/sofa/corp/right,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"tH" = (
/obj/structure/table/reinforced,
@@ -271,7 +271,7 @@
/area/ruin/space/has_grav/powered)
"DI" = (
/mob/living/basic/trooper/syndicate/melee/space/stormtrooper,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"DM" = (
/obj/structure/table/optable,
@@ -300,7 +300,7 @@
/area/ruin/space/has_grav/powered)
"EL" = (
/mob/living/basic/trooper/syndicate/melee/sword/space,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"FA" = (
/obj/effect/spawner/random/entertainment/arcade,
@@ -387,7 +387,7 @@
/obj/machinery/porta_turret/syndicate/energy{
dir = 9
},
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"NA" = (
/obj/machinery/computer{
@@ -414,7 +414,7 @@
/area/ruin/space)
"Qt" = (
/obj/structure/showcase/machinery/signal_decrypter,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"Qw" = (
/turf/closed/wall/mineral/plastitanium,
@@ -479,7 +479,7 @@
/area/ruin/space/has_grav/powered)
"Yw" = (
/obj/structure/barricade/sandbags,
-/turf/open/misc/asteroid/lowpressure,
+/turf/open/misc/asteroid/airless,
/area/ruin/space/has_grav/powered)
"ZK" = (
/obj/structure/fluff/empty_sleeper/syndicate{
diff --git a/_maps/map_files/Deathmatch/OSHA_Violator.dmm b/_maps/deathmatch/OSHA_Violator.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/OSHA_Violator.dmm
rename to _maps/deathmatch/OSHA_Violator.dmm
diff --git a/_maps/map_files/Deathmatch/starwars.dmm b/_maps/deathmatch/arena_station.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/starwars.dmm
rename to _maps/deathmatch/arena_station.dmm
diff --git a/_maps/map_files/Deathmatch/raidthebase.dmm b/_maps/deathmatch/backalley.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/raidthebase.dmm
rename to _maps/deathmatch/backalley.dmm
diff --git a/_maps/map_files/Deathmatch/instagib.dmm b/_maps/deathmatch/instagib.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/instagib.dmm
rename to _maps/deathmatch/instagib.dmm
diff --git a/_maps/map_files/Deathmatch/Maint_Mania.dmm b/_maps/deathmatch/maint_mania.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/Maint_Mania.dmm
rename to _maps/deathmatch/maint_mania.dmm
diff --git a/_maps/map_files/Deathmatch/meatower.dmm b/_maps/deathmatch/meat_tower.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/meatower.dmm
rename to _maps/deathmatch/meat_tower.dmm
diff --git a/_maps/map_files/Deathmatch/mech_madness.dmm b/_maps/deathmatch/mech_madness.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/mech_madness.dmm
rename to _maps/deathmatch/mech_madness.dmm
diff --git a/_maps/map_files/Deathmatch/The_Brig.dmm b/_maps/deathmatch/meta_brig.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/The_Brig.dmm
rename to _maps/deathmatch/meta_brig.dmm
diff --git a/_maps/map_files/Deathmatch/ragecage.dmm b/_maps/deathmatch/ragecage.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/ragecage.dmm
rename to _maps/deathmatch/ragecage.dmm
diff --git a/_maps/map_files/Deathmatch/SecuRing.dmm b/_maps/deathmatch/secu_ring.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/SecuRing.dmm
rename to _maps/deathmatch/secu_ring.dmm
diff --git a/_maps/map_files/Deathmatch/shooting_range.dmm b/_maps/deathmatch/shooting_range.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/shooting_range.dmm
rename to _maps/deathmatch/shooting_range.dmm
diff --git a/_maps/map_files/Deathmatch/Sniper_elite.dmm b/_maps/deathmatch/sniper_elite.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/Sniper_elite.dmm
rename to _maps/deathmatch/sniper_elite.dmm
diff --git a/_maps/map_files/Deathmatch/chinatown.dmm b/_maps/deathmatch/sunrise.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/chinatown.dmm
rename to _maps/deathmatch/sunrise.dmm
diff --git a/_maps/map_files/Deathmatch/arena.dmm b/_maps/deathmatch/underground_arena.dmm
similarity index 100%
rename from _maps/map_files/Deathmatch/arena.dmm
rename to _maps/deathmatch/underground_arena.dmm
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index 49deea1fe47..7b7fd7a2eb3 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -1466,8 +1466,8 @@
dir = 1
},
/obj/effect/turf_decal/tile/neutral/fourcorners,
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/smooth,
/area/station/command/bridge)
"aEl" = (
@@ -11922,7 +11922,6 @@
/obj/structure/disposalpipe/trunk{
dir = 4
},
-/obj/machinery/disposal/bin,
/obj/structure/railing{
dir = 10
},
@@ -11931,6 +11930,7 @@
dir = 10
},
/obj/effect/decal/cleanable/dirt,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/plating,
/area/station/engineering/break_room)
"ewi" = (
@@ -18008,10 +18008,8 @@
/area/station/hallway/primary/fore)
"gBT" = (
/obj/structure/window/reinforced/spawner/directional/east,
+/obj/structure/secure_safe/caps_spare,
/obj/structure/table/glass,
-/obj/item/folder/blue{
- pixel_y = 2
- },
/turf/open/floor/glass/reinforced,
/area/station/command/bridge)
"gCe" = (
@@ -24768,9 +24766,9 @@
/area/station/construction/mining/aux_base)
"iUN" = (
/obj/structure/disposalpipe/trunk,
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
/obj/machinery/airalarm/directional/north,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/cargo/storage)
"iVq" = (
@@ -35084,15 +35082,6 @@
/area/station/maintenance/central/lesser)
"mxa" = (
/obj/structure/window/reinforced/spawner/directional/east,
-/obj/structure/table/glass,
-/obj/item/paper_bin{
- pixel_x = -2;
- pixel_y = 8
- },
-/obj/item/pen{
- pixel_x = -1;
- pixel_y = 8
- },
/turf/open/floor/glass/reinforced,
/area/station/command/bridge)
"mxe" = (
@@ -40627,7 +40616,6 @@
/area/station/hallway/primary/central/fore)
"owZ" = (
/obj/item/kirbyplants/organic/applebush,
-/obj/structure/secure_safe/caps_spare/directional/west,
/turf/open/floor/iron/dark/smooth_large,
/area/station/command/bridge)
"oxb" = (
@@ -50925,7 +50913,7 @@
/turf/open/misc/sandy_dirt,
/area/station/maintenance/starboard/aft)
"rLN" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
/obj/item/lazarus_injector,
/obj/effect/turf_decal/bot_white,
/obj/effect/turf_decal/siding/thinplating_new{
@@ -57444,8 +57432,8 @@
/obj/structure/disposalpipe/trunk{
dir = 8
},
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white/side{
dir = 4
},
@@ -63482,10 +63470,7 @@
dir = 8
},
/obj/effect/turf_decal/stripes/box,
-/obj/machinery/disposal/bin{
- desc = "A pneumatic waste disposal unit. This one leads to the morgue.";
- name = "corpse disposal"
- },
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark,
/area/station/medical/medbay/central)
"vLT" = (
@@ -65241,8 +65226,8 @@
/obj/structure/disposalpipe/trunk{
dir = 8
},
-/obj/machinery/disposal/bin,
/obj/structure/sign/poster/official/random/directional/north,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark,
/area/station/security/office)
"woT" = (
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 30e548835e8..56237c8fea2 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -7668,7 +7668,6 @@
/area/station/maintenance/port)
"bRw" = (
/obj/structure/sign/warning/deathsposal/directional/east,
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 8
},
@@ -7680,6 +7679,7 @@
/obj/effect/turf_decal/tile/purple/half/contrasted{
dir = 8
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/science/xenobiology)
"bRy" = (
@@ -10252,7 +10252,6 @@
"cxu" = (
/obj/structure/table/reinforced,
/obj/item/storage/medkit/regular,
-/obj/structure/secure_safe/caps_spare/directional/west,
/obj/effect/turf_decal/tile/blue{
dir = 1
},
@@ -17953,7 +17952,7 @@
},
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/bot,
-/obj/machinery/disposal/bin,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/engineering/storage)
"eua" = (
@@ -22028,7 +22027,6 @@
dir = 1
},
/obj/machinery/status_display/evac/directional/south,
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
/obj/structure/sign/directions/arrival{
dir = 1;
@@ -22038,6 +22036,7 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/hallway/secondary/entry)
"fux" = (
@@ -27315,7 +27314,6 @@
/turf/open/floor/plating,
/area/station/maintenance/department/engine/atmos)
"gIa" = (
-/obj/machinery/disposal/bin,
/obj/machinery/light/small/directional/north,
/obj/structure/disposalpipe/trunk{
dir = 8
@@ -27324,6 +27322,7 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/security/brig)
"gIc" = (
@@ -36847,7 +36846,7 @@
/turf/open/floor/iron/large,
/area/station/security/processing)
"jeZ" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
/obj/item/clothing/neck/stethoscope,
/obj/item/book{
desc = "An undeniably handy book.";
@@ -38976,7 +38975,6 @@
/turf/open/floor/iron/dark,
/area/station/engineering/transit_tube)
"jFa" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
/obj/machinery/status_display/evac/directional/west,
/obj/effect/turf_decal/siding/purple{
@@ -38986,6 +38984,7 @@
dir = 4
},
/obj/machinery/light/directional/west,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/science/lab)
"jFb" = (
@@ -47062,7 +47061,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/structure/table/wood,
/obj/structure/window/reinforced/spawner/directional/east,
-/obj/item/flashlight/lamp,
+/obj/machinery/computer/security/wooden_tv,
/turf/open/floor/iron/grimy,
/area/station/command/bridge)
"lGE" = (
@@ -51497,16 +51496,13 @@
/turf/open/floor/iron,
/area/station/medical/virology)
"mNL" = (
-/obj/machinery/disposal/bin{
- desc = "A pneumatic waste disposal unit. This one leads to the morgue.";
- name = "corpse disposal"
- },
/obj/effect/turf_decal/bot,
/obj/structure/disposalpipe/trunk{
dir = 8
},
/obj/effect/turf_decal/tile/blue/fourcorners,
/obj/machinery/light/directional/east,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/medical/medbay)
"mNX" = (
@@ -60185,7 +60181,6 @@
},
/area/station/commons/fitness/recreation)
"pen" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
/obj/structure/disposalpipe/trunk{
dir = 1
@@ -60194,6 +60189,7 @@
/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{
dir = 1
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/science/xenobiology)
"peo" = (
@@ -66326,8 +66322,8 @@
"qBY" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/structure/secure_safe/caps_spare,
/obj/structure/table/wood,
-/obj/machinery/computer/security/wooden_tv,
/turf/open/floor/iron/grimy,
/area/station/command/bridge)
"qCb" = (
@@ -70546,7 +70542,6 @@
/turf/open/floor/iron,
/area/station/engineering/atmos/storage)
"rFV" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
/obj/structure/disposalpipe/trunk,
/obj/machinery/camera/directional/north{
@@ -70556,6 +70551,7 @@
/obj/machinery/airalarm/directional/north,
/obj/effect/turf_decal/tile/neutral/fourcorners,
/obj/machinery/light/directional/north,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark,
/area/station/security/office)
"rFZ" = (
@@ -95608,7 +95604,7 @@
/obj/item/clothing/head/costume/kitty,
/obj/item/clothing/under/costume/maid,
/obj/item/clothing/head/costume/rabbitears,
-/obj/item/clothing/under/dress/redeveninggown,
+/obj/item/clothing/under/dress/eveninggown,
/turf/open/floor/wood,
/area/station/commons/dorms)
"xRa" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index 6637caabd3e..6130743dbeb 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -11419,8 +11419,8 @@
/turf/open/floor/iron,
/area/station/commons/storage/mining)
"drG" = (
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/cargo/office)
"drH" = (
@@ -17960,11 +17960,8 @@
/turf/open/floor/iron/showroomfloor,
/area/station/engineering/atmos)
"fwQ" = (
+/obj/structure/secure_safe/caps_spare,
/obj/structure/table/reinforced,
-/obj/structure/secure_safe/caps_spare/directional/east,
-/obj/item/papercutter{
- pixel_x = 7
- },
/turf/open/floor/iron,
/area/station/command/bridge)
"fwS" = (
@@ -19051,8 +19048,8 @@
/area/station/science/explab)
"fOR" = (
/obj/structure/disposalpipe/trunk,
-/obj/machinery/disposal/bin,
/obj/machinery/airalarm/directional/east,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white/side{
dir = 9
},
@@ -25360,7 +25357,7 @@
/turf/open/floor/plating,
/area/station/ai_monitored/turret_protected/aisat_interior)
"hPf" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
/obj/item/clothing/head/costume/bearpelt,
/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass,
/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass,
@@ -60422,9 +60419,9 @@
/obj/structure/disposalpipe/trunk{
dir = 1
},
-/obj/machinery/disposal/bin,
/obj/machinery/computer/security/telescreen/entertainment/directional/south,
/obj/machinery/light/small/directional/east,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark,
/area/station/engineering/storage)
"sto" = (
@@ -61433,7 +61430,7 @@
dir = 5
},
/obj/structure/disposalpipe/trunk,
-/obj/machinery/disposal/bin,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white,
/area/station/medical/medbay/aft)
"sHh" = (
@@ -74208,8 +74205,8 @@
/turf/open/floor/plating,
/area/station/maintenance/port/aft)
"wMV" = (
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark/textured_half{
dir = 1
},
@@ -237775,7 +237772,7 @@ utR
uEQ
lET
grA
-cEv
+fwQ
nOH
gxU
gZq
@@ -240344,8 +240341,8 @@ nfk
utR
tmQ
qnV
-fwQ
kBr
+cEv
lhv
iGH
bGm
diff --git a/_maps/map_files/KiloStation2/KiloStation2.dmm b/_maps/map_files/KiloStation2/KiloStation2.dmm
index 34c1a35256f..5b1ec1757ec 100644
--- a/_maps/map_files/KiloStation2/KiloStation2.dmm
+++ b/_maps/map_files/KiloStation2/KiloStation2.dmm
@@ -8423,6 +8423,7 @@
name = "Security Customs Checkpoint"
},
/obj/effect/mapping_helpers/airlock/access/all/security/general,
+/obj/structure/cable,
/turf/open/floor/iron/dark,
/area/station/security/checkpoint/customs)
"cQb" = (
@@ -8439,6 +8440,7 @@
name = "arrivals navigation beacon"
},
/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/structure/cable,
/turf/open/floor/iron,
/area/station/hallway/secondary/exit/departure_lounge)
"cQt" = (
@@ -11539,7 +11541,6 @@
/turf/open/floor/plating,
/area/station/maintenance/disposal/incinerator)
"dNx" = (
-/obj/structure/cable,
/obj/effect/turf_decal/tile/blue{
dir = 4
},
@@ -15158,6 +15159,7 @@
"eZL" = (
/obj/effect/landmark/event_spawn,
/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/structure/cable,
/turf/open/floor/iron,
/area/station/hallway/secondary/exit/departure_lounge)
"fac" = (
@@ -23813,6 +23815,7 @@
/obj/effect/landmark/start/hangover,
/obj/effect/turf_decal/tile/blue/half/contrasted,
/obj/effect/turf_decal/tile/neutral,
+/obj/structure/cable,
/turf/open/floor/iron,
/area/station/hallway/secondary/exit/departure_lounge)
"hIq" = (
@@ -25456,7 +25459,7 @@
"ifu" = (
/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/cobweb,
-/obj/structure/safe{
+/obj/structure/safe/vault{
pixel_x = 3
},
/obj/item/book{
@@ -35744,6 +35747,7 @@
/obj/effect/decal/cleanable/dirt,
/obj/effect/landmark/start/hangover,
/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/structure/cable,
/turf/open/floor/iron,
/area/station/hallway/secondary/exit/departure_lounge)
"luA" = (
@@ -36401,6 +36405,7 @@
/turf/open/floor/iron,
/area/station/hallway/secondary/exit/departure_lounge)
"lFC" = (
+/obj/structure/cable,
/turf/open/floor/iron,
/area/station/hallway/secondary/exit/departure_lounge)
"lFN" = (
@@ -37201,6 +37206,17 @@
/obj/effect/landmark/start/blueshield,
/turf/open/floor/carpet/royalblack,
/area/station/command/heads_quarters/captain)
+"lUb" = (
+/obj/machinery/door/firedoor,
+/obj/machinery/door/airlock/public/glass{
+ name = "Departure Lounge"
+ },
+/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{
+ cycle_id = "departures-entrance"
+ },
+/obj/structure/cable,
+/turf/open/floor/iron/dark,
+/area/station/hallway/secondary/exit/departure_lounge)
"lUq" = (
/turf/closed/wall/r_wall/rust,
/area/station/service/chapel/storage)
@@ -72469,7 +72485,6 @@
/turf/open/floor/grass,
/area/station/security/prison/garden)
"xhJ" = (
-/obj/structure/cable,
/obj/effect/turf_decal/tile/red,
/obj/effect/turf_decal/tile/blue{
dir = 4
@@ -116235,7 +116250,7 @@ wAZ
cQR
eMo
fBh
-pIl
+lUb
mbE
iSw
nXu
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 7ac2807b480..2192d36a613 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -1389,10 +1389,10 @@
/turf/open/floor/plating,
/area/station/engineering/supermatter/room)
"aBJ" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/siding,
/obj/structure/disposalpipe/trunk,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/science/lab)
"aBL" = (
@@ -3254,13 +3254,13 @@
network = list("ss13","medbay")
},
/obj/item/radio/intercom/directional/west,
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 4
},
/obj/effect/turf_decal/tile/blue/anticorner/contrasted{
dir = 1
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white,
/area/station/medical/medbay/central)
"bgV" = (
@@ -6857,7 +6857,6 @@
/turf/open/space,
/area/space/nearstation)
"cxq" = (
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 1
},
@@ -6869,6 +6868,7 @@
name = "Labor Camp Shuttle Lockdown";
req_access = list("brig")
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/security/brig)
"cxt" = (
@@ -9560,7 +9560,6 @@
/turf/open/floor/iron,
/area/station/science/research)
"dBv" = (
-/obj/machinery/disposal/bin,
/obj/machinery/airalarm/directional/east,
/obj/structure/disposalpipe/trunk{
dir = 1
@@ -9569,6 +9568,7 @@
dir = 8
},
/obj/machinery/light/small/directional/east,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/cargo/storage)
"dBV" = (
@@ -38614,10 +38614,7 @@
/turf/open/floor/iron/dark,
/area/station/engineering/atmos)
"nOq" = (
-/obj/machinery/holopad/secure{
- pixel_x = 9;
- pixel_y = -9
- },
+/obj/machinery/holopad/secure,
/turf/open/floor/carpet,
/area/station/command/heads_quarters/captain/private)
"nOv" = (
@@ -41714,7 +41711,7 @@
/turf/open/floor/plating,
/area/station/maintenance/solars/starboard/aft)
"oXK" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
/obj/item/storage/briefcase/secure/riches,
/obj/item/storage/backpack/duffelbag/syndie/hitman,
/obj/item/card/id/advanced/silver/reaper,
@@ -43748,11 +43745,11 @@
/turf/open/floor/iron,
/area/station/engineering/atmos/storage/gas)
"pIz" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/bot,
/obj/structure/disposalpipe/trunk{
dir = 8
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/wood,
/area/station/commons/lounge)
"pID" = (
@@ -47699,10 +47696,7 @@
/turf/open/floor/iron,
/area/station/cargo/sorting)
"raJ" = (
-/obj/machinery/computer/security/wooden_tv{
- pixel_x = 1;
- pixel_y = 6
- },
+/obj/structure/secure_safe/caps_spare,
/obj/structure/table/glass,
/turf/open/floor/iron/dark,
/area/station/command/bridge)
@@ -55714,13 +55708,11 @@
/area/station/maintenance/port)
"tTL" = (
/obj/structure/table/glass,
-/obj/item/folder/blue{
- pixel_y = 2
- },
-/obj/item/folder/blue{
- pixel_y = 2
- },
/obj/structure/window/reinforced/spawner/directional/east,
+/obj/machinery/computer/security/wooden_tv{
+ pixel_x = 1;
+ pixel_y = 6
+ },
/turf/open/floor/iron/dark,
/area/station/command/bridge)
"tTP" = (
@@ -57213,7 +57205,6 @@
pixel_y = -3
},
/obj/structure/table/glass,
-/obj/structure/secure_safe/caps_spare/directional/west,
/obj/effect/turf_decal/tile/green/half/contrasted{
dir = 8
},
@@ -59399,11 +59390,11 @@
/turf/open/floor/iron/dark,
/area/station/security/courtroom)
"vfO" = (
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 8
},
/obj/effect/turf_decal/tile/yellow/half/contrasted,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/engineering/break_room)
"vfU" = (
diff --git a/_maps/map_files/NSSJourney/NSSJourney.dmm b/_maps/map_files/NSSJourney/NSSJourney.dmm
index fffdc892162..e5b6c7470cb 100644
--- a/_maps/map_files/NSSJourney/NSSJourney.dmm
+++ b/_maps/map_files/NSSJourney/NSSJourney.dmm
@@ -43982,7 +43982,7 @@
/turf/open/floor/iron,
/area/station/service/hydroponics/garden/abandoned)
"lxv" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
/obj/item/clothing/head/costume/bearpelt,
/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass,
/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass,
diff --git a/_maps/map_files/NSVBlueshift/Blueshift.dmm b/_maps/map_files/NSVBlueshift/Blueshift.dmm
index 27d29fdd5f1..f853df30699 100644
--- a/_maps/map_files/NSVBlueshift/Blueshift.dmm
+++ b/_maps/map_files/NSVBlueshift/Blueshift.dmm
@@ -6079,6 +6079,14 @@
/obj/machinery/duct,
/turf/open/floor/plating,
/area/station/service/cafeteria)
+"bkw" = (
+/obj/effect/turf_decal/tile/yellow/half/contrasted{
+ dir = 4
+ },
+/obj/structure/disposalpipe/segment,
+/obj/machinery/camera/directional/east,
+/turf/open/floor/iron,
+/area/station/hallway/primary/aft)
"bkx" = (
/obj/structure/railing,
/obj/effect/decal/cleanable/dirt{
@@ -9212,10 +9220,6 @@
/area/station/ai_monitored/turret_protected/aisat_interior)
"bOx" = (
/obj/item/radio/intercom/directional/south,
-/obj/structure/secure_safe/caps_spare{
- pixel_x = 5;
- pixel_y = -37
- },
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -11067,6 +11071,10 @@
},
/turf/closed/wall,
/area/station/maintenance/central)
+"cgs" = (
+/obj/machinery/camera/directional/west,
+/turf/open/floor/engine/vacuum,
+/area/station/maintenance/department/engineering/atmos_aux_port)
"cgu" = (
/obj/effect/turf_decal/box/white/corners,
/turf/open/floor/iron/dark,
@@ -12082,6 +12090,7 @@
pixel_x = -8
},
/obj/item/straight_razor,
+/obj/machinery/camera/directional/north,
/turf/open/floor/iron/dark/side{
dir = 8
},
@@ -14706,10 +14715,14 @@
/obj/machinery/computer/pod/old/mass_driver_controller/ordnancedriver/longrange{
pixel_y = 28
},
-/obj/machinery/door/window/left/directional/east{
- name = "Mass Driver Door";
- req_access = list("ordnance")
+/obj/structure/railing{
+ dir = 8
},
+/obj/structure/window/reinforced/spawner/directional/east,
+/obj/structure/disposalpipe/trunk{
+ dir = 4
+ },
+/obj/machinery/disposal/bin,
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
"cLt" = (
@@ -18862,6 +18875,7 @@
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
+/obj/machinery/camera/directional/north,
/turf/open/floor/iron/white,
/area/station/science/research)
"dBT" = (
@@ -19803,6 +19817,14 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/turf/open/floor/wood,
/area/station/service/hydroponics/garden)
+"dKA" = (
+/obj/machinery/camera/directional/north{
+ c_tag = "Bridge - Vanguard Quarters";
+ name = "command camera";
+ dir = 3
+ },
+/turf/closed/wall,
+/area/station/commons/toilet/auxiliary)
"dKB" = (
/obj/structure/table/wood,
/obj/machinery/light/directional/south,
@@ -20520,11 +20542,12 @@
/turf/open/floor/plating,
/area/station/maintenance/department/crew_quarters/bar)
"dTa" = (
-/obj/structure/safe,
-/obj/item/storage/briefcase/secure,
+/obj/structure/safe/vault,
+/obj/item/storage/briefcase/secure/riches,
/obj/item/storage/backpack/duffelbag/syndie/hitman,
/obj/item/card/id/advanced/silver/reaper,
/obj/item/lazarus_injector,
+/obj/item/gun/energy/disabler,
/obj/item/gun/ballistic/revolver/russian,
/obj/item/ammo_box/a357,
/obj/item/clothing/neck/stethoscope,
@@ -20534,6 +20557,7 @@
name = "\improper A Simpleton's Guide to Safe-cracking with Stethoscopes"
},
/obj/effect/turf_decal/bot_white/left,
+/obj/effect/turf_decal/tile/neutral/fourcorners,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/command/nuke_storage)
"dTd" = (
@@ -21110,6 +21134,7 @@
/area/station/service/library)
"dYr" = (
/obj/item/weldingtool/largetank,
+/obj/machinery/camera/directional/west,
/turf/open/floor/engine,
/area/station/maintenance/department/engineering/atmos_aux_port)
"dYs" = (
@@ -21266,6 +21291,13 @@
},
/turf/open/floor/wood,
/area/station/service/library)
+"eag" = (
+/obj/structure/disposalpipe/trunk{
+ dir = 1
+ },
+/obj/structure/disposaloutlet,
+/turf/open/floor/catwalk_floor,
+/area/station/science/ordnance/testlab)
"eak" = (
/obj/machinery/airalarm/directional/east,
/obj/structure/extinguisher_cabinet/directional/south,
@@ -25112,6 +25144,13 @@
},
/turf/open/floor/circuit,
/area/station/science/research/abandoned)
+"eLw" = (
+/obj/machinery/atmospherics/pipe/smart/simple/dark/visible,
+/obj/machinery/camera/directional/west,
+/turf/open/floor/iron/smooth_edge{
+ dir = 8
+ },
+/area/station/maintenance/disposal/incinerator)
"eLB" = (
/obj/machinery/light/directional/east,
/turf/open/openspace,
@@ -28131,6 +28170,7 @@
dir = 4;
pixel_x = -28
},
+/obj/machinery/camera/directional/west,
/turf/open/floor/iron/dark/textured_large,
/area/station/command/heads_quarters/ce)
"foC" = (
@@ -28687,6 +28727,7 @@
dir = 4
},
/obj/structure/cable,
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron,
/area/station/engineering/atmos)
"fuo" = (
@@ -36427,6 +36468,7 @@
dir = 1
},
/obj/machinery/newscaster/directional/south,
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron/dark,
/area/station/hallway/primary/starboard)
"gUG" = (
@@ -37187,6 +37229,7 @@
/obj/effect/turf_decal/stripes/end{
dir = 1
},
+/obj/structure/disposalpipe/segment,
/turf/open/floor/catwalk_floor,
/area/station/science/ordnance/testlab)
"hdS" = (
@@ -38846,7 +38889,8 @@
/area/station/maintenance/department/engineering/engine_aft_port)
"hvv" = (
/obj/machinery/camera/directional/north{
- c_tag = "Courtroom - Entrance"
+ c_tag = "Courtroom - Entrance";
+ dir = 3
},
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{
dir = 1
@@ -41387,6 +41431,10 @@
},
/turf/open/floor/iron/dark,
/area/station/maintenance/department/engineering/atmos_aux_port)
+"hUy" = (
+/obj/machinery/camera/directional/east,
+/turf/open/floor/engine/vacuum,
+/area/station/maintenance/department/engineering/atmos_aux_port)
"hUD" = (
/obj/machinery/camera{
c_tag = "Leisure Hallway - Port Center";
@@ -43097,6 +43145,14 @@
"ilS" = (
/turf/closed/wall,
/area/station/cargo/storage)
+"ilT" = (
+/obj/effect/turf_decal/trimline/blue/filled/line,
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/machinery/camera/directional/south,
+/turf/open/floor/iron/white,
+/area/station/medical/medbay/central)
"ilW" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
/turf/open/floor/wood,
@@ -43551,6 +43607,7 @@
"iqB" = (
/obj/structure/cable,
/obj/machinery/power/apc/auto_name/directional/north,
+/obj/machinery/camera/directional/north,
/turf/open/floor/iron,
/area/station/engineering/main)
"iqG" = (
@@ -45218,6 +45275,11 @@
},
/turf/open/floor/carpet,
/area/station/service/library)
+"iHf" = (
+/obj/effect/turf_decal/tile/neutral,
+/obj/machinery/camera/directional/east,
+/turf/open/floor/iron,
+/area/station/hallway/primary/starboard)
"iHj" = (
/obj/effect/landmark/start/hangover,
/turf/open/floor/iron/dark/side{
@@ -46433,6 +46495,7 @@
dir = 1
},
/obj/item/kirbyplants/random,
+/obj/machinery/camera/directional/west,
/turf/open/floor/wood,
/area/station/hallway/primary/port)
"iVh" = (
@@ -47160,6 +47223,7 @@
/obj/structure/disposalpipe/junction{
dir = 8
},
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron,
/area/station/engineering/atmos/office)
"jbI" = (
@@ -47630,11 +47694,11 @@
/turf/open/floor/iron,
/area/station/hallway/primary/starboard)
"jgT" = (
-/obj/effect/turf_decal/arrows/red{
- pixel_y = 15
+/obj/structure/disposalpipe/segment{
+ dir = 10
},
-/turf/open/floor/engine/hull,
-/area/space/nearstation)
+/turf/open/floor/catwalk_floor,
+/area/station/science/ordnance/testlab)
"jgV" = (
/obj/effect/decal/cleanable/dirt{
icon_state = "dirt-flat-1"
@@ -48250,6 +48314,13 @@
},
/turf/open/floor/iron/dark,
/area/station/ai_monitored/command/storage/eva)
+"jmJ" = (
+/obj/structure/chair/wood/wings,
+/obj/effect/landmark/start/assistant,
+/obj/effect/landmark/start/assistant,
+/obj/machinery/camera/directional/north,
+/turf/open/floor/carpet,
+/area/station/service/library/lounge)
"jmM" = (
/obj/effect/turf_decal/arrows/red,
/obj/effect/turf_decal/stripes/red/line{
@@ -49672,6 +49743,14 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/iron,
/area/station/engineering/break_room)
+"jAf" = (
+/obj/structure/secure_safe/caps_spare{
+ base_icon_state = "floorsafe";
+ icon_state = "floorsafe";
+ pixel_x = -1
+ },
+/turf/open/floor/iron,
+/area/station/command/bridge)
"jAj" = (
/obj/structure/cable/layer1,
/obj/structure/disposalpipe/segment{
@@ -51693,6 +51772,7 @@
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/machinery/camera/directional/north,
/turf/open/floor/iron/dark,
/area/station/hallway/secondary/command)
"jSI" = (
@@ -53237,6 +53317,7 @@
name = "Engineering Deliveries";
req_access = list("engineering")
},
+/obj/machinery/camera/directional/east,
/turf/open/floor/iron/checker,
/area/station/engineering/lobby)
"kgE" = (
@@ -54038,7 +54119,8 @@
/obj/machinery/light_switch/directional/south,
/obj/machinery/camera/directional/north{
c_tag = "Bridge - Vanguard Quarters";
- name = "command camera"
+ name = "command camera";
+ dir = 3
},
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 1
@@ -55409,6 +55491,7 @@
/obj/structure/railing/corner/end/flip{
dir = 8
},
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron/dark,
/area/station/hallway/primary/starboard)
"kAl" = (
@@ -57250,6 +57333,7 @@
/obj/machinery/rnd/production/circuit_imprinter,
/obj/effect/turf_decal/bot,
/obj/machinery/firealarm/directional/south,
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron/dark,
/area/station/engineering/storage_shared)
"kVa" = (
@@ -58112,6 +58196,10 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/turf/open/floor/iron,
/area/station/engineering/transit_tube)
+"ldC" = (
+/obj/machinery/camera/directional/west,
+/turf/open/floor/iron,
+/area/station/engineering/atmos/test_chambers)
"ldD" = (
/obj/machinery/door/poddoor/shutters{
id = "AbandonedMech"
@@ -64167,6 +64255,13 @@
},
/turf/open/floor/iron,
/area/station/science/xenobiology)
+"mmV" = (
+/obj/effect/turf_decal/tile/yellow/half/contrasted{
+ dir = 1
+ },
+/obj/machinery/camera/directional/north,
+/turf/open/floor/iron,
+/area/station/hallway/primary/aft)
"mne" = (
/obj/machinery/power/smes/engineering,
/obj/structure/cable,
@@ -68335,6 +68430,7 @@
dir = 4
},
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4,
+/obj/machinery/camera/directional/north,
/turf/open/floor/iron/white,
/area/station/science/cytology)
"ndp" = (
@@ -69199,6 +69295,7 @@
pixel_y = -25
},
/obj/item/kirbyplants/random,
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron,
/area/station/engineering/atmos/test_chambers)
"nmO" = (
@@ -71397,7 +71494,8 @@
/obj/machinery/camera/directional/north{
c_tag = "Science - Lounge";
name = "science camera";
- network = list("ss13","rd")
+ network = list("ss13","rd");
+ dir = 3
},
/obj/machinery/light/directional/south,
/obj/item/radio/intercom/directional/south,
@@ -76340,6 +76438,7 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 4
},
+/obj/machinery/camera/directional/south,
/turf/open/floor/stone,
/area/station/service/forge)
"oDV" = (
@@ -76905,10 +77004,6 @@
/obj/effect/mapping_helpers/airlock/access/all/medical/psychology,
/turf/open/floor/iron,
/area/station/medical/psychology)
-"oJY" = (
-/obj/structure/falsewall/reinforced,
-/turf/open/floor/plating,
-/area/station/command/secure_bunker)
"oKf" = (
/obj/structure/railing{
dir = 5
@@ -84828,6 +84923,7 @@
},
/obj/structure/drain,
/obj/machinery/shower/directional/east,
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron/checker,
/area/station/engineering/atmos)
"qlm" = (
@@ -85314,6 +85410,7 @@
/obj/effect/turf_decal/bot,
/obj/structure/window/reinforced/spawner/directional/east,
/obj/machinery/light/directional/north,
+/obj/machinery/camera/directional/north,
/turf/open/floor/iron/dark,
/area/station/security/prison)
"qpZ" = (
@@ -87678,7 +87775,7 @@
/turf/open/floor/wood,
/area/station/medical/psychology)
"qLn" = (
-/obj/effect/turf_decal/stripes/end,
+/obj/structure/disposalpipe/segment,
/turf/open/floor/catwalk_floor,
/area/station/science/ordnance/testlab)
"qLp" = (
@@ -90308,6 +90405,7 @@
/area/station/common/arcade)
"rkS" = (
/obj/effect/turf_decal/stripes/red/line,
+/obj/machinery/camera/directional/south,
/turf/open/floor/engine,
/area/station/science/xenobiology)
"rkT" = (
@@ -95482,6 +95580,11 @@
/obj/machinery/light/directional/east,
/turf/open/floor/iron/dark,
/area/station/engineering/storage)
+"slR" = (
+/obj/structure/cable,
+/obj/machinery/camera/directional/north,
+/turf/open/floor/iron/dark,
+/area/station/maintenance/disposal/incinerator)
"slU" = (
/obj/effect/turf_decal/bot,
/obj/structure/closet/athletic_mixed,
@@ -96714,6 +96817,15 @@
/obj/structure/window/spawner/directional/west,
/turf/open/floor/plating,
/area/station/maintenance/port/upper)
+"sxI" = (
+/obj/structure/chair{
+ dir = 8
+ },
+/obj/machinery/camera/directional/east,
+/turf/open/floor/iron/dark/side{
+ dir = 4
+ },
+/area/station/security/prison)
"sxJ" = (
/obj/structure/table,
/obj/item/storage/photo_album,
@@ -97461,6 +97573,7 @@
/obj/effect/turf_decal/stripes{
dir = 8
},
+/obj/structure/disposalpipe/segment,
/turf/open/floor/catwalk_floor,
/area/station/science/ordnance/testlab)
"sFt" = (
@@ -100016,6 +100129,7 @@
"tcz" = (
/obj/effect/turf_decal/bot,
/obj/structure/closet/secure_closet/security/engine,
+/obj/machinery/camera/directional/west,
/turf/open/floor/iron/dark,
/area/station/security/checkpoint/engineering)
"tcI" = (
@@ -111913,6 +112027,11 @@
/obj/effect/spawner/random/trash/graffiti,
/turf/closed/wall,
/area/station/maintenance/port/fore)
+"vpR" = (
+/obj/structure/window/reinforced/spawner/directional/north,
+/obj/machinery/camera/directional/east,
+/turf/open/floor/iron/dark,
+/area/station/science/xenobiology)
"vpS" = (
/turf/open/floor/iron,
/area/station/hallway/secondary/service)
@@ -117133,6 +117252,13 @@
/obj/effect/spawner/random/trash/mess,
/turf/open/floor/plating,
/area/station/maintenance/department/science/central)
+"wms" = (
+/obj/effect/turf_decal/stripes{
+ dir = 1
+ },
+/obj/machinery/camera/directional/north,
+/turf/open/floor/engine,
+/area/station/engineering/supermatter/room)
"wmu" = (
/obj/effect/decal/cleanable/dirt{
icon_state = "dirt-flat-1"
@@ -122371,6 +122497,13 @@
/obj/machinery/portable_atmospherics/scrubber,
/turf/open/floor/plating,
/area/station/maintenance/starboard/fore)
+"xni" = (
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/camera/directional/south,
+/turf/open/floor/iron,
+/area/station/hallway/primary/port)
"xnj" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -122762,6 +122895,7 @@
/obj/machinery/power/apc/auto_name/directional/east,
/obj/structure/cable,
/obj/structure/chair/stool/directional/north,
+/obj/machinery/camera/directional/east,
/turf/open/floor/iron,
/area/station/engineering/break_room)
"xrz" = (
@@ -127006,6 +127140,7 @@
/obj/structure/bed/medical/emergency,
/obj/effect/turf_decal/trimline/blue/filled/line,
/obj/item/radio/intercom/directional/south,
+/obj/machinery/camera/directional/south,
/turf/open/floor/iron/white,
/area/station/command/gateway)
"ygI" = (
@@ -150613,7 +150748,7 @@ tyB
fWT
nKx
uDf
-pLD
+mmV
kTo
hQi
dma
@@ -152368,7 +152503,7 @@ onV
uEi
uEi
wYH
-mIe
+jmJ
nvm
pNh
lZH
@@ -152401,7 +152536,7 @@ jQA
ycI
wif
yeQ
-jQA
+dKA
mIR
mIR
mIR
@@ -153681,7 +153816,7 @@ uZH
vDU
phI
oXz
-feR
+xni
kTb
kTb
kTb
@@ -154211,7 +154346,7 @@ mIR
mIR
mIR
vVX
-pLD
+mmV
kfm
exp
ofE
@@ -156303,7 +156438,7 @@ bgi
dmu
jrA
aar
-aar
+cgs
aar
jrA
xJH
@@ -156518,7 +156653,7 @@ amx
qVG
pzo
kvt
-amx
+bkw
ail
wGh
slp
@@ -158882,7 +159017,7 @@ eiO
kXw
dUl
qBd
-mNE
+wms
frp
frp
frp
@@ -159396,7 +159531,7 @@ cqa
qfz
hoI
qBd
-mNE
+wms
frp
frp
frp
@@ -161957,13 +162092,13 @@ eWt
byi
mhp
tdu
-aar
+hUy
aar
jrA
xJH
jrA
aar
-aar
+hUy
aar
jrA
fZI
@@ -163710,7 +163845,7 @@ fsk
qKN
qKN
kwo
-qKN
+iHf
fsk
pEc
sNz
@@ -164007,7 +164142,7 @@ cnV
sQQ
sQQ
sQQ
-sQQ
+ldC
kdO
sQQ
sQQ
@@ -164390,7 +164525,7 @@ cLU
jUg
wDi
rwX
-dnD
+sxI
dnD
dnD
ouK
@@ -167091,7 +167226,7 @@ fth
jQq
fth
fth
-laI
+slR
cIK
vwS
sQQ
@@ -167339,7 +167474,7 @@ hMe
txl
haM
fRn
-eLa
+eLw
eLa
ezv
oGL
@@ -222328,7 +222463,7 @@ uSJ
wyX
tAc
ekI
-wHw
+vpR
pwo
bSd
fDT
@@ -224533,7 +224668,7 @@ bbG
bwg
gij
bMO
-tVq
+jAf
kJQ
qDo
jiu
@@ -231269,7 +231404,7 @@ qhl
atX
hTf
ved
-qdq
+ilT
pVw
ogu
nCB
@@ -232247,7 +232382,7 @@ vur
rcU
uhZ
uhZ
-xbB
+uhZ
xSc
yig
nNn
@@ -232504,7 +232639,7 @@ iyR
nFF
jCK
dTz
-xbB
+uhZ
jzw
tmU
ijy
@@ -232761,7 +232896,7 @@ ske
bgS
ciM
vhb
-xbB
+uhZ
fCZ
uLh
aTj
@@ -232863,10 +232998,10 @@ lLn
cLr
eQK
lLn
+eQK
+eQK
+eQK
lLn
-lLn
-iZl
-jgT
bTG
iZl
wfG
@@ -233018,7 +233153,7 @@ bug
uhZ
uhZ
nPM
-xbB
+uhZ
mlN
skJ
kMJ
@@ -233117,13 +233252,13 @@ ulx
ulx
ulx
lLn
-nGo
+jgT
hdL
sFo
qLn
+eag
+nGo
iuP
-iZl
-iZl
bTG
iZl
dwJ
@@ -233275,7 +233410,7 @@ cAj
gGr
qit
vhb
-xbB
+uhZ
vzh
tBI
vzh
@@ -233377,10 +233512,10 @@ lLn
lLn
lLn
lLn
+eQK
+eQK
+eQK
lLn
-lLn
-iZl
-jgT
bTG
iZl
wfG
@@ -233532,7 +233667,7 @@ pUg
dhs
hyt
vhb
-xbB
+uhZ
mdx
gVz
qow
@@ -233789,7 +233924,7 @@ vzW
kxk
aFD
vhb
-xbB
+uhZ
rOD
uJF
hrN
@@ -234046,7 +234181,7 @@ jNU
cab
bpn
rXy
-xbB
+uhZ
mTZ
skJ
lYY
@@ -234303,7 +234438,7 @@ uhZ
gdH
uhZ
uhZ
-xbB
+uhZ
vzh
boK
vzh
@@ -234558,8 +234693,8 @@ uhZ
xvI
ePU
cab
-oJY
-qZv
+uhZ
+uhZ
uhZ
bqb
vRt
@@ -234817,7 +234952,7 @@ lJn
req
uhZ
uhZ
-uhZ
+sZz
gOw
jyr
uDb
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index e83a6941c88..78e9bbc0fa0 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -7372,8 +7372,8 @@
/obj/effect/turf_decal/trimline/green/filled/line{
dir = 5
},
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/hallway/floor3/aft)
"bPq" = (
@@ -8715,10 +8715,10 @@
/turf/open/floor/iron/white,
/area/station/medical/storage)
"cgv" = (
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 1
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark/side{
dir = 10
},
@@ -11477,8 +11477,9 @@
/turf/open/floor/iron/white,
/area/station/science/lobby)
"cRe" = (
-/obj/structure/reagent_dispensers/water_cooler,
/obj/machinery/status_display/ai/directional/north,
+/obj/structure/secure_safe/caps_spare,
+/obj/structure/table/reinforced,
/turf/open/floor/iron/dark,
/area/station/command/bridge)
"cRs" = (
@@ -16917,11 +16918,11 @@
/area/station/maintenance/floor4/starboard/aft)
"enl" = (
/obj/effect/turf_decal/delivery,
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 8
},
/obj/machinery/airalarm/directional/east,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark,
/area/station/hallway/secondary/service)
"enp" = (
@@ -34334,7 +34335,6 @@
/turf/open/floor/iron/grimy,
/area/station/security/detectives_office)
"iUP" = (
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 1
},
@@ -34342,6 +34342,7 @@
dir = 1
},
/obj/machinery/newscaster/directional/south,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark/side{
dir = 10
},
@@ -50729,10 +50730,10 @@
/area/station/holodeck/rec_center)
"mZS" = (
/obj/structure/disposalpipe/trunk,
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/trimline/blue/filled/line{
dir = 9
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white,
/area/station/medical/medbay/central)
"mZT" = (
@@ -53609,7 +53610,8 @@
"nKc" = (
/obj/structure/cable,
/obj/machinery/conveyor/inverted{
- dir = 10
+ dir = 10;
+ id = "mining"
},
/turf/open/floor/iron/checker,
/area/station/cargo/miningdock)
@@ -73772,8 +73774,8 @@
/area/station/commons/toilet)
"sZb" = (
/obj/structure/disposalpipe/trunk,
-/obj/machinery/disposal/bin,
/obj/machinery/firealarm/directional/north,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/corner{
dir = 8
},
@@ -73992,7 +73994,8 @@
/obj/structure/cable,
/obj/machinery/bouldertech/refinery/smelter,
/obj/machinery/conveyor/inverted{
- dir = 10
+ dir = 10;
+ id = "mining"
},
/turf/open/floor/iron/checker,
/area/station/cargo/miningdock)
@@ -74666,7 +74669,8 @@
"tkZ" = (
/obj/structure/cable,
/obj/machinery/conveyor/inverted{
- dir = 6
+ dir = 6;
+ id = "mining"
},
/turf/open/floor/iron/checker,
/area/station/cargo/miningdock)
@@ -77446,6 +77450,8 @@
"tVp" = (
/obj/machinery/light_switch/directional/north,
/obj/machinery/status_display/ai/directional/east,
+/obj/structure/table/reinforced/titaniumglass,
+/obj/item/piggy_bank/vault,
/turf/open/floor/circuit,
/area/station/ai_monitored/command/nuke_storage)
"tVq" = (
@@ -79634,10 +79640,10 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 4
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark/side{
dir = 4
},
@@ -80646,6 +80652,10 @@
/obj/machinery/power/apc/auto_name/directional/north,
/obj/structure/cable,
/obj/machinery/status_display/ai/directional/west,
+/obj/structure/table/reinforced/titaniumglass,
+/obj/item/maneki_neko{
+ pixel_y = 4
+ },
/turf/open/floor/circuit,
/area/station/ai_monitored/command/nuke_storage)
"uNp" = (
@@ -87380,9 +87390,9 @@
/obj/effect/turf_decal/trimline/purple/filled/corner{
dir = 4
},
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk,
/obj/machinery/light/cold/no_nightlight/directional/west,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white,
/area/station/science/lobby)
"wvw" = (
@@ -91622,7 +91632,6 @@
/turf/open/floor/pod/light,
/area/station/maintenance/floor2/port/fore)
"xzS" = (
-/obj/structure/secure_safe/caps_spare/directional/south,
/obj/effect/turf_decal/tile/purple/fourcorners,
/turf/open/floor/iron/dark,
/area/station/command/bridge)
diff --git a/_maps/map_files/VoidRaptor/VoidRaptor.dmm b/_maps/map_files/VoidRaptor/VoidRaptor.dmm
index f0f06657a82..6e23381a6af 100644
--- a/_maps/map_files/VoidRaptor/VoidRaptor.dmm
+++ b/_maps/map_files/VoidRaptor/VoidRaptor.dmm
@@ -7185,9 +7185,6 @@
/turf/closed/wall,
/area/station/maintenance/port/greater)
"chg" = (
-/obj/structure/secure_safe/caps_spare{
- pixel_x = -24
- },
/obj/structure/table/reinforced/rglass,
/obj/item/storage/photo_album,
/obj/item/camera{
@@ -15252,6 +15249,11 @@
"esP" = (
/obj/structure/window/reinforced/spawner/directional/south,
/obj/effect/turf_decal/tile/dark_blue/anticorner,
+/obj/structure/secure_safe/caps_spare{
+ base_icon_state = "floorsafe";
+ icon_state = "floorsafe";
+ pixel_y = 5
+ },
/turf/open/floor/iron/dark/smooth_large,
/area/station/command/bridge)
"etd" = (
@@ -26753,12 +26755,20 @@
/turf/open/floor/catwalk_floor/iron_smooth,
/area/station/maintenance/port)
"hIp" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
+/obj/item/storage/briefcase/secure/riches,
/obj/item/storage/backpack/duffelbag/syndie/hitman,
/obj/item/card/id/advanced/silver/reaper,
/obj/item/lazarus_injector,
-/obj/item/gun/ballistic/automatic/pistol/deagle/gold,
-/obj/item/stack/spacecash/c5000,
+/obj/item/gun/energy/disabler,
+/obj/item/gun/ballistic/revolver/russian,
+/obj/item/ammo_box/a357,
+/obj/item/clothing/neck/stethoscope,
+/obj/item/book{
+ desc = "An undeniably handy book.";
+ icon_state = "bookknock";
+ name = "\improper A Simpleton's Guide to Safe-cracking with Stethoscopes"
+ },
/obj/effect/turf_decal/bot,
/turf/open/floor/engine,
/area/station/ai_monitored/command/nuke_storage)
diff --git a/_maps/map_files/generic/CentCom_nova_z2.dmm b/_maps/map_files/generic/CentCom_nova_z2.dmm
index 7a4a72b2015..6790e414524 100644
--- a/_maps/map_files/generic/CentCom_nova_z2.dmm
+++ b/_maps/map_files/generic/CentCom_nova_z2.dmm
@@ -311,7 +311,7 @@
"agV" = (
/obj/structure/closet/wardrobe/grey,
/obj/item/clothing/under/dress/tango,
-/obj/item/clothing/under/dress/redeveninggown,
+/obj/item/clothing/under/dress/eveninggown,
/turf/open/indestructible/hotelwood,
/area/centcom/holding/cafe)
"ahk" = (
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index ae87f6f6357..1e485c5865a 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -3636,11 +3636,11 @@
/obj/effect/turf_decal/trimline/neutral/filled/line{
dir = 1
},
-/obj/machinery/disposal/bin,
/obj/machinery/status_display/evac/directional/north,
/obj/structure/disposalpipe/trunk{
dir = 2
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/commons/dorms)
"awi" = (
@@ -8051,7 +8051,6 @@
/turf/open/floor/iron/dark,
/area/station/security/courtroom/holding)
"bLr" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/trimline/red/filled/line{
dir = 5
},
@@ -8062,6 +8061,7 @@
dir = 8
},
/obj/structure/window/reinforced/spawner/directional/south,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/security/office)
"bLA" = (
@@ -19128,10 +19128,10 @@
dir = 9
},
/obj/machinery/newscaster/directional/north,
-/obj/machinery/disposal/bin,
/obj/structure/disposalpipe/trunk{
dir = 4
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white,
/area/station/science/lab)
"fKg" = (
@@ -21222,11 +21222,11 @@
/obj/effect/turf_decal/trimline/yellow/filled/line{
dir = 9
},
-/obj/machinery/disposal/bin,
/obj/machinery/newscaster/directional/north,
/obj/structure/disposalpipe/trunk{
dir = 4
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/engineering/main)
"gxV" = (
@@ -35117,6 +35117,7 @@
/area/station/engineering/atmos)
"lrZ" = (
/obj/structure/table/glass,
+/obj/structure/secure_safe/caps_spare,
/turf/open/floor/glass,
/area/station/command/meeting_room)
"lse" = (
@@ -43742,6 +43743,10 @@
},
/turf/open/floor/iron,
/area/station/ai_monitored/security/armory)
+"ouQ" = (
+/obj/effect/turf_decal/siding/wood/corner,
+/turf/open/floor/wood,
+/area/station/command/meeting_room)
"ouW" = (
/obj/structure/table/wood,
/obj/item/storage/photo_album/library,
@@ -44635,7 +44640,7 @@
/turf/open/floor/iron,
/area/station/construction/mining/aux_base)
"oQO" = (
-/obj/structure/safe,
+/obj/structure/safe/vault,
/obj/item/clothing/head/costume/bearpelt,
/obj/item/gun/ballistic/revolver/russian,
/obj/item/ammo_box/a357,
@@ -46135,6 +46140,11 @@
},
/turf/open/floor/iron,
/area/station/hallway/secondary/service)
+"ptq" = (
+/obj/effect/turf_decal/siding/wood/corner,
+/obj/structure/disposalpipe/segment,
+/turf/open/floor/wood,
+/area/station/command/meeting_room)
"pts" = (
/obj/machinery/power/smes{
capacity = 9e+006;
@@ -46973,6 +46983,11 @@
},
/turf/open/floor/carpet,
/area/station/cargo/miningdock)
+"pGr" = (
+/obj/machinery/disposal/bin,
+/obj/structure/disposalpipe/trunk,
+/turf/open/floor/wood,
+/area/station/command/meeting_room)
"pGy" = (
/obj/effect/turf_decal/trimline/neutral/filled/line{
dir = 8
@@ -46998,7 +47013,7 @@
/obj/structure/disposalpipe/trunk{
dir = 1
},
-/obj/machinery/disposal/bin,
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/dark,
/area/station/command/bridge)
"pGS" = (
@@ -48423,7 +48438,6 @@
/area/station/medical/chemistry)
"qgy" = (
/obj/structure/table/reinforced,
-/obj/structure/secure_safe/caps_spare/directional/east,
/obj/machinery/cell_charger{
pixel_y = 4
},
@@ -49930,6 +49944,7 @@
/obj/effect/turf_decal/trimline/red/filled/line{
dir = 1
},
+/obj/structure/reagent_dispensers/wall/peppertank/directional/north,
/turf/open/floor/iron,
/area/station/security/checkpoint/science)
"qIc" = (
@@ -54283,13 +54298,13 @@
/turf/open/floor/iron/white,
/area/station/science/xenobiology)
"skb" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/trimline/brown/filled/line{
dir = 10
},
/obj/structure/disposalpipe/trunk{
dir = 1
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron,
/area/station/cargo/storage)
"ski" = (
@@ -64144,10 +64159,6 @@
/area/station/hallway/secondary/entry)
"vxv" = (
/obj/effect/turf_decal/siding/wood,
-/obj/machinery/disposal/bin,
-/obj/structure/disposalpipe/trunk{
- dir = 8
- },
/obj/machinery/light/warm/directional/north,
/turf/open/floor/wood,
/area/station/command/meeting_room)
@@ -68019,9 +68030,6 @@
/obj/effect/turf_decal/siding/wood{
dir = 6
},
-/obj/structure/disposalpipe/segment{
- dir = 9
- },
/turf/open/floor/wood,
/area/station/command/meeting_room)
"wWh" = (
@@ -70448,13 +70456,13 @@
/turf/open/floor/iron/white,
/area/station/medical/medbay/central)
"xSz" = (
-/obj/machinery/disposal/bin,
/obj/effect/turf_decal/trimline/blue/filled/line{
dir = 4
},
/obj/structure/disposalpipe/trunk{
dir = 2
},
+/obj/machinery/disposal/bin/tagger,
/turf/open/floor/iron/white,
/area/station/medical/treatment_center)
"xSD" = (
@@ -154913,8 +154921,8 @@ eOh
pJJ
fWM
qyZ
-joF
-kSZ
+pGr
+ptq
lmG
hHI
gIG
@@ -155170,7 +155178,7 @@ phB
qdY
xNj
qyZ
-kSZ
+ouQ
wWe
imr
iue
diff --git a/_maps/shuttles/nova/slaver_syndie.dmm b/_maps/shuttles/nova/slaver_syndie.dmm
index e9eac52860e..5c760caf9b8 100644
--- a/_maps/shuttles/nova/slaver_syndie.dmm
+++ b/_maps/shuttles/nova/slaver_syndie.dmm
@@ -72,9 +72,7 @@
dir = 4
},
/obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"du" = (
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden{
@@ -107,9 +105,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden/layer2{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"fp" = (
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden{
@@ -141,9 +137,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2{
dir = 1
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"gG" = (
/obj/effect/turf_decal/stripes/line{
@@ -177,9 +171,7 @@
dir = 4
},
/obj/effect/mapping_helpers/airlock/cutaiwire,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"gZ" = (
/obj/effect/turf_decal/trimline/yellow/warning{
@@ -217,9 +209,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden/layer2{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"iA" = (
/obj/effect/turf_decal/bot,
@@ -314,9 +304,7 @@
id = "privateerslavercellarealockdown";
name = "Brig Lockdown"
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"mJ" = (
/obj/effect/decal/cleanable/dirt,
@@ -363,9 +351,7 @@
dir = 8
},
/obj/structure/cable,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"qp" = (
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden{
@@ -448,9 +434,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden/layer2{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"wj" = (
/obj/structure/cable,
@@ -514,9 +498,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden,
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"zh" = (
/obj/machinery/chem_master,
@@ -636,12 +618,11 @@
"Ct" = (
/obj/structure/table/wood,
/obj/effect/turf_decal/siding/wood,
+/obj/machinery/light/directional/north,
/obj/machinery/microwave{
- icon = 'icons/obj/kitchen.dmi';
- pixel_x = -1;
- pixel_y = 8
+ pixel_y = 8;
+ pixel_x = -1
},
-/obj/machinery/light/directional/north,
/turf/open/floor/wood,
/area/shuttle/syndicate/slaver)
"CR" = (
@@ -663,9 +644,7 @@
dir = 4
},
/obj/effect/mapping_helpers/airlock/cutaiwire,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"DO" = (
/obj/structure/table/wood,
@@ -740,9 +719,7 @@
name = "Lockdown"
},
/obj/effect/mapping_helpers/airlock/cutaiwire,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"Hk" = (
/obj/effect/turf_decal/stripes/corner{
@@ -794,9 +771,7 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"JJ" = (
/obj/machinery/atmospherics/pipe/smart/manifold/supply/hidden{
@@ -806,9 +781,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2{
dir = 8
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"JM" = (
/obj/structure/lattice,
@@ -839,9 +812,7 @@
dir = 4
},
/obj/machinery/light/directional/south,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"Lf" = (
/obj/structure/table/optable,
@@ -974,9 +945,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden,
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden/layer2,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"Qx" = (
/obj/effect/turf_decal/stripes/line,
@@ -1000,9 +969,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden/layer2{
dir = 10
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"QO" = (
/obj/effect/turf_decal/trimline/yellow/warning{
@@ -1023,9 +990,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden/layer2{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"Rx" = (
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden{
@@ -1046,9 +1011,7 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"Sf" = (
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden{
@@ -1094,9 +1057,7 @@
port_direction = 8
},
/obj/structure/fans/tiny,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"SE" = (
/obj/effect/decal/cleanable/dirt,
@@ -1124,9 +1085,7 @@
/obj/machinery/atmospherics/pipe/smart/simple/supply/hidden{
dir = 4
},
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"VH" = (
/obj/effect/turf_decal/trimline/yellow/warning{
@@ -1238,9 +1197,7 @@
dir = 4
},
/obj/machinery/light/directional/south,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"YI" = (
/obj/machinery/door/airlock/security/old{
@@ -1255,9 +1212,7 @@
name = "Lockdown"
},
/obj/effect/mapping_helpers/airlock/cutaiwire,
-/turf/open/floor/pod/dark{
- icon = 'icons/turf/floors.dmi'
- },
+/turf/open/floor/pod/dark,
/area/shuttle/syndicate/slaver)
"Zp" = (
/obj/structure/chair{
diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm
index 3b0bcb2ce96..8597092fbf6 100644
--- a/code/__DEFINES/_flags.dm
+++ b/code/__DEFINES/_flags.dm
@@ -127,6 +127,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define NO_DEATH_MESSAGE (1<<14)
/// This area should have extra shielding from certain event effects
#define EVENT_PROTECTED (1<<15)
+/// This Area Doesn't have Flood or Bomb Admin Messages, but will still log
+#define QUIET_LOGS (1<<16)
/*
These defines are used specifically with the atom/pass_flags bitmask
diff --git a/code/__DEFINES/alerts.dm b/code/__DEFINES/alerts.dm
index 3d09a3e5a22..e6f4feb259a 100644
--- a/code/__DEFINES/alerts.dm
+++ b/code/__DEFINES/alerts.dm
@@ -47,7 +47,6 @@
#define ALERT_HACKING_APC "hackingapc"
/** MODsuit/Mech related */
-#define ALERT_MODSUIT_CHARGE "mod_charge"
#define ALERT_MECH_DAMAGE "mech_damage"
/** Food related */
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index e3e67c82b57..d5533dcac54 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -36,6 +36,9 @@
///How long pirates will wait for a response before attacking
#define RESPONSE_MAX_TIME 2 MINUTES
+/// How long till a spessman should come back after being captured and sent to the holding facility (which some antags use)
+#define COME_BACK_FROM_CAPTURE_TIME 6 MINUTES
+
//ERT Types
#define ERT_BLUE "Blue"
#define ERT_RED "Red"
diff --git a/code/__DEFINES/blackmarket.dm b/code/__DEFINES/blackmarket.dm
index 5494c371db7..c3b8ad0bf46 100644
--- a/code/__DEFINES/blackmarket.dm
+++ b/code/__DEFINES/blackmarket.dm
@@ -7,4 +7,5 @@
#define SHIPPING_METHOD_TELEPORT "Teleport"
// Throws the item from somewhere at the station.
#define SHIPPING_METHOD_LAUNCH "Launch"
-
+// Sends a supply pod to the buyer's location, showy.
+#define SHIPPING_METHOD_SUPPLYPOD "Supply Pod"
diff --git a/code/__DEFINES/cargo.dm b/code/__DEFINES/cargo.dm
index 63d5682ef0f..1d34ed6b3cf 100644
--- a/code/__DEFINES/cargo.dm
+++ b/code/__DEFINES/cargo.dm
@@ -64,3 +64,10 @@
#define SUPPLY_PACK_UNCOMMON_DISCOUNTABLE "uncommon_discount"
///Discount category for the silly, overpriced, joke content, sometimes useful or plain bad.
#define SUPPLY_PACK_RARE_DISCOUNTABLE "rare_discount"
+
+///Standard export define for not selling the item.
+#define EXPORT_NOT_SOLD 0
+///Sell the item
+#define EXPORT_SOLD 1
+///Sell the item, but for the love of god, don't delete it, we're handling it in a fancier way.
+#define EXPORT_SOLD_DONT_DELETE 2
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 33b7853cfd0..e97653e5022 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -385,3 +385,8 @@ GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
#define SHOVE_BLOCKED (1<<5)
///If the obstacle is an object at the border of the turf (so no signal from being sent to the other turf)
#define SHOVE_DIRECTIONAL_BLOCKED (1<<6)
+
+///Deathmatch lobby current status
+#define DEATHMATCH_NOT_PLAYING 0
+#define DEATHMATCH_PRE_PLAYING 1
+#define DEATHMATCH_PLAYING 2
diff --git a/code/__DEFINES/computers.dm b/code/__DEFINES/computers.dm
index ba3294ae683..c9cce7a7114 100644
--- a/code/__DEFINES/computers.dm
+++ b/code/__DEFINES/computers.dm
@@ -11,3 +11,6 @@
#define CATEGORY_BEPIS "Bepis Tech"
#define CATEGORY_BITRUNNING_FLAIR "Misc"
#define CATEGORY_BITRUNNING_TECH "Tech"
+
+///Helper macro for record computers' preview views, used to ensure consistency in all use cases.
+#define USER_PREVIEW_ASSIGNED_VIEW(user_ckey) "preview_[user_ckey]_[REF(src)]_records"
diff --git a/code/__DEFINES/dcs/signals/signals_beam.dm b/code/__DEFINES/dcs/signals/signals_beam.dm
index edfbc7c4371..12d58b70c9a 100644
--- a/code/__DEFINES/dcs/signals/signals_beam.dm
+++ b/code/__DEFINES/dcs/signals/signals_beam.dm
@@ -1,3 +1,12 @@
/// Called before beam is redrawn
#define COMSIG_BEAM_BEFORE_DRAW "beam_before_draw"
#define BEAM_CANCEL_DRAW (1 << 0)
+
+/// Sent to a beam when an atom enters any turf the beam covers: (obj/effect/ebeam/hit_beam, atom/movable/entered)
+#define COMSIG_BEAM_ENTERED "beam_entered"
+
+/// Sent to a beam when an atom exits any turf the beam covers: (obj/effect/ebeam/hit_beam, atom/movable/exited)
+#define COMSIG_BEAM_EXITED "beam_exited"
+
+/// Sent to a beam when any turf the beam covers changes: (list/datum/callback/post_change_callbacks)
+#define COMSIG_BEAM_TURFS_CHANGED "beam_turfs_changed"
diff --git a/code/__DEFINES/dcs/signals/signals_blackmarket.dm b/code/__DEFINES/dcs/signals/signals_blackmarket.dm
new file mode 100644
index 00000000000..86e3d9277a1
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_blackmarket.dm
@@ -0,0 +1,2 @@
+///From /datum/market_item/spawn_item(): (uplink, shipping_method, shipping_loc)
+#define COMSIG_MARKET_ITEM_SPAWNED "market_item_spawned"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index b1914cc966b..924488af73e 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -13,6 +13,9 @@
/// Called when an organ gets surgically removed (mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
#define COMSIG_ORGAN_SURGICALLY_REMOVED "organ_surgically_removed"
+///Called when movement intent is toggled.
+#define COMSIG_MOVE_INTENT_TOGGLED "move_intent_toggled"
+
///from base of mob/update_transform()
#define COMSIG_LIVING_POST_UPDATE_TRANSFORM "living_post_update_transform"
@@ -171,6 +174,9 @@
///From obj/item/toy/crayon/spraycan
#define COMSIG_LIVING_MOB_PAINTED "living_mob_painted"
+///From obj/closet/supplypod/return_victim: (turf/destination)
+#define COMSIG_LIVING_RETURN_FROM_CAPTURE "living_return_from_capture"
+
///From mob/living/proc/wabbajack(): (randomize_type)
#define COMSIG_LIVING_PRE_WABBAJACKED "living_mob_wabbajacked"
/// Return to stop the rest of the wabbajack from triggering.
diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm
index 14c89e236e2..9cf7bba2dd7 100644
--- a/code/__DEFINES/icon_smoothing.dm
+++ b/code/__DEFINES/icon_smoothing.dm
@@ -217,6 +217,8 @@ DEFINE_BITFIELD(smoothing_junction, list(
#define SMOOTH_GROUP_SHUTTERS S_OBJ(75)
#define SMOOTH_GROUP_WATER S_OBJ(76) ///obj/effect/abstract/liquid_turf
+
+#define SMOOTH_GROUP_WIREWEED S_OBJ(77)
//NOVA EDIT END
/// Performs the work to set smoothing_groups and canSmoothWith.
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 265bcf56dee..1857c95c020 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -264,6 +264,22 @@ DEFINE_BITFIELD(departments_bitflags, list(
/// This job is a head of staff.
#define JOB_HEAD_OF_STAFF (1<<12)
+DEFINE_BITFIELD(job_flags, list(
+ "JOB_ANNOUNCE_ARRIVAL" = JOB_ANNOUNCE_ARRIVAL,
+ "JOB_CREW_MANIFEST" = JOB_CREW_MANIFEST,
+ "JOB_EQUIP_RANK" = JOB_EQUIP_RANK,
+ "JOB_CREW_MEMBER" = JOB_CREW_MEMBER,
+ "JOB_NEW_PLAYER_JOINABLE" = JOB_NEW_PLAYER_JOINABLE,
+ "JOB_BOLD_SELECT_TEXT" = JOB_BOLD_SELECT_TEXT,
+ "JOB_REOPEN_ON_ROUNDSTART_LOSS" = JOB_REOPEN_ON_ROUNDSTART_LOSS,
+ "JOB_ASSIGN_QUIRKS" = JOB_ASSIGN_QUIRKS,
+ "JOB_CAN_BE_INTERN" = JOB_CAN_BE_INTERN,
+ "JOB_CANNOT_OPEN_SLOTS" = JOB_CANNOT_OPEN_SLOTS,
+ "JOB_HIDE_WHEN_EMPTY" = JOB_HIDE_WHEN_EMPTY,
+ "JOB_LATEJOIN_ONLY" = JOB_LATEJOIN_ONLY,
+ "JOB_HEAD_OF_STAFF" = JOB_HEAD_OF_STAFF,
+))
+
/// Combination flag for jobs which are considered regular crew members of the station.
#define STATION_JOB_FLAGS (JOB_ANNOUNCE_ARRIVAL|JOB_CREW_MANIFEST|JOB_EQUIP_RANK|JOB_CREW_MEMBER|JOB_NEW_PLAYER_JOINABLE|JOB_REOPEN_ON_ROUNDSTART_LOSS|JOB_ASSIGN_QUIRKS|JOB_CAN_BE_INTERN)
/// Combination flag for jobs which are considered heads of staff.
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index e007eaadc6d..dace178f311 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -79,7 +79,7 @@
#define SPAN_CLOWN "clown"
#define SPAN_SINGING "singing"
#define SPAN_TAPE_RECORDER "tape_recorder"
-#define SPAN_HELIUM "small"
+#define SPAN_SMALL_VOICE "small"
//bitflag #defines for return value of the radio() proc.
/// Makes the message use italics
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 5261217466a..af42927d711 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -951,6 +951,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define STATION_TRAIT_UNNATURAL_ATMOSPHERE "station_trait_unnatural_atmosphere"
#define STATION_TRAIT_VENDING_SHORTAGE "station_trait_vending_shortage"
+///Deathmatch traits
+#define TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS "deathmath_explosive_implants"
+
/// This atom is currently spinning.
#define TRAIT_SPINNING "spinning"
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index 7f4fbd92936..dcbfab7fe5f 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -88,6 +88,8 @@
#define MAFIA_TRAIT "mafia"
/// Trait associated with ctf
#define CTF_TRAIT "ctf"
+/// Trait associated with deathmatch
+#define DEATHMATCH_TRAIT "deathmatch"
/// Trait associated with highlander
#define HIGHLANDER_TRAIT "highlander"
/// Trait given from playing pretend with baguettes
diff --git a/code/__DEFINES/~nova_defines/signals.dm b/code/__DEFINES/~nova_defines/signals.dm
index e3bd44ff147..d7a536dfbd8 100644
--- a/code/__DEFINES/~nova_defines/signals.dm
+++ b/code/__DEFINES/~nova_defines/signals.dm
@@ -84,3 +84,16 @@
/// Whenever we need to get the soul of the mob inside of the soulcatcher.
#define COMSIG_SOULCATCHER_SCAN_BODY "soulcatcher_scan_body"
+
+// CORRUPTION SIGNALS
+
+/// From /obj/structure/fleshmind/structure/proc/activate_ability() (src)
+#define COMSIG_CORRUPTION_STRUCTURE_ABILITY_TRIGGERED "corruption_structure_ability_triggered"
+
+/// From /mob/living/simple_animal/hostile/fleshmind/phaser/proc/phase_move_to(atom/target, nearby = FALSE)
+#define COMSIG_PHASER_PHASE_MOVE "phaser_phase_move"
+/// from /mob/living/simple_animal/hostile/fleshmind/phaser/proc/enter_nearby_closet()
+#define COMSIG_PHASER_ENTER_CLOSET "phaser_enter_closet"
+
+/// from /obj/structure/fleshmind/structure/core/proc/rally_troops()
+#define COMSIG_FLESHMIND_CORE_RALLY "fleshmind_core_rally"
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 0210c44ee57..f9957f4c739 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -192,3 +192,6 @@
/// Orders mobs by health
/proc/cmp_mob_health(mob/living/mob_a, mob/living/mob_b)
return mob_b.health - mob_a.health
+
+/proc/cmp_deathmatch_mods(datum/deathmatch_modifier/a, datum/deathmatch_modifier/b)
+ return sorttext(b.name, a.name)
diff --git a/code/__HELPERS/logging/attack.dm b/code/__HELPERS/logging/attack.dm
index a8ed72def27..16fd656137f 100644
--- a/code/__HELPERS/logging/attack.dm
+++ b/code/__HELPERS/logging/attack.dm
@@ -79,6 +79,6 @@
log_game(bomb_message)
GLOB.bombers += bomb_message
-
- if(message_admins)
+ var/area/bomb_area = get_area(bomb)
+ if(message_admins && !(bomb_area.area_flags & QUIET_LOGS)) // Don't spam the logs with deathmatch bombs
message_admins("[user ? "[ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(user)] " : ""][details][bomb ? " [bomb.name] at [ADMIN_VERBOSEJMP(bomb)]": ""][additional_details ? " [additional_details]" : ""].")
diff --git a/code/_globalvars/lists/quirks.dm b/code/_globalvars/lists/quirks.dm
index 3d15865a527..7ee2d1e0794 100644
--- a/code/_globalvars/lists/quirks.dm
+++ b/code/_globalvars/lists/quirks.dm
@@ -26,3 +26,8 @@ GLOBAL_LIST_INIT(limb_choice_transhuman, list(
"Left Leg" = /obj/item/bodypart/leg/left/robot,
"Right Leg" = /obj/item/bodypart/leg/right/robot,
))
+
+GLOBAL_LIST_INIT(side_choice_hemiplegic, list(
+ "Left Side" = /datum/brain_trauma/severe/paralysis/hemiplegic/left,
+ "Right Side" = /datum/brain_trauma/severe/paralysis/hemiplegic/right,
+))
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index f9fc6bb049d..33cc2de2d3e 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -100,6 +100,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"STATION_TRAIT_UNNATURAL_ATMOSPHERE" = STATION_TRAIT_UNNATURAL_ATMOSPHERE,
"STATION_TRAIT_VENDING_SHORTAGE" = STATION_TRAIT_VENDING_SHORTAGE,
),
+ /datum/deathmatch_lobby = list(
+ "TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS" = TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS,
+ ),
/datum/wound = list(
"TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED,
),
diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm
index 92defc66101..9d4343c132e 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/action_button.dm
@@ -230,7 +230,7 @@
return
for(var/datum/action/action as anything in take_from.actions)
- if(!action.show_to_observers)
+ if(!action.show_to_observers || !action.owner_has_control)
continue
action.GiveAction(src)
RegisterSignal(take_from, COMSIG_MOB_GRANTED_ACTION, PROC_REF(on_observing_action_granted))
@@ -251,7 +251,7 @@
/mob/proc/on_observing_action_granted(mob/living/source, datum/action/action)
SIGNAL_HANDLER
- if(!action.show_to_observers)
+ if(!action.show_to_observers || !action.owner_has_control)
return
action.GiveAction(src)
diff --git a/code/controllers/subsystem/blackmarket.dm b/code/controllers/subsystem/blackmarket.dm
index bdd342cbf3d..ea70b5c7229 100644
--- a/code/controllers/subsystem/blackmarket.dm
+++ b/code/controllers/subsystem/blackmarket.dm
@@ -5,9 +5,10 @@ SUBSYSTEM_DEF(blackmarket)
/// Descriptions for each shipping methods.
var/shipping_method_descriptions = list(
- SHIPPING_METHOD_LAUNCH="Launches the item at the station from space, cheap but you might not receive your item at all.",
- SHIPPING_METHOD_LTSRBT="Long-To-Short-Range-Bluespace-Transceiver, a machine that receives items outside the station and then teleports them to the location of the uplink.",
- SHIPPING_METHOD_TELEPORT="Teleports the item in a random area in the station, you get 60 seconds to get there first though."
+ SHIPPING_METHOD_LAUNCH = "Launches the item at the station from space, cheap but you might not receive your item at all.",
+ SHIPPING_METHOD_LTSRBT = "Long-To-Short-Range-Bluespace-Transceiver, a machine that receives items outside the station and then teleports them to the location of the uplink.",
+ SHIPPING_METHOD_TELEPORT = "Teleports the item in a random area in the station, you get 60 seconds to get there first though.",
+ SHIPPING_METHOD_SUPPLYPOD = "Ships the item inside a supply pod at your exact location. Showy, speedy and expensive.",
)
/// List of all existing markets.
@@ -42,11 +43,7 @@ SUBSYSTEM_DEF(blackmarket)
var/datum/market_purchase/purchase = queued_purchases[1]
queued_purchases.Cut(1,2)
- // Uh oh, uplink is gone. We will just keep the money and you will not get your order.
- if(!purchase.uplink || QDELETED(purchase.uplink))
- queued_purchases -= purchase
- qdel(purchase)
- continue
+ var/mob/buyer = recursive_loc_check(purchase.uplink.loc, /mob)
switch(purchase.method)
// Find a ltsrbt pad and make it handle the shipping.
@@ -54,65 +51,71 @@ SUBSYSTEM_DEF(blackmarket)
if(!telepads.len)
continue
// Prioritize pads that don't have a cooldown active.
- var/free_pad_found = FALSE
- for(var/obj/machinery/ltsrbt/pad in telepads)
+ var/obj/machinery/ltsrbt/free_pad_found
+ for(var/obj/machinery/ltsrbt/pad as anything in telepads)
if(pad.recharge_cooldown)
continue
pad.add_to_queue(purchase)
- queued_purchases -= purchase
- free_pad_found = TRUE
+ free_pad_found = pad
break
- if(free_pad_found)
+ if(isnull(free_pad_found))
continue
- var/obj/machinery/ltsrbt/pad = pick(telepads)
-
- to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting that the order is being processed by [pad]."))
+ to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting that the order is being processed by [free_pad_found]."))
- queued_purchases -= purchase
- pad.add_to_queue(purchase)
// Get random area, throw it somewhere there.
if(SHIPPING_METHOD_TELEPORT)
var/turf/targetturf = get_safe_random_station_turf()
// This shouldn't happen.
if (!targetturf)
continue
+ queued_purchases -= purchase
- to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds."))
+ to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds."))
// do_teleport does not want to teleport items from nullspace, so it just forceMoves and does sparks.
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket,fake_teleport), purchase.entry.spawn_item(), targetturf), 60 SECONDS)
- queued_purchases -= purchase
- qdel(purchase)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket,fake_teleport), purchase, targetturf), 60 SECONDS)
+
// Get the current location of the uplink if it exists, then throws the item from space at the station from a random direction.
if(SHIPPING_METHOD_LAUNCH)
var/startSide = pick(GLOB.cardinals)
var/turf/T = get_turf(purchase.uplink)
var/pickedloc = spaceDebrisStartLoc(startSide, T.z)
- var/atom/movable/item = purchase.entry.spawn_item(pickedloc)
+ var/atom/movable/item = purchase.entry.spawn_item(pickedloc, purchase)
item.throw_at(purchase.uplink, 3, 3, spin = FALSE)
- to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)]."))
+ to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)]."))
+ qdel(purchase)
- queued_purchases -= purchase
+ if(SHIPPING_METHOD_SUPPLYPOD)
+ var/obj/structure/closet/supplypod/back_to_station/pod = new()
+ purchase.entry.spawn_item(pod, purchase)
+ new /obj/effect/pod_landingzone(get_turf(purchase.uplink), pod)
+
+ to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting the order is being launched at your location. Right here, right now!"))
qdel(purchase)
if(MC_TICK_CHECK)
break
/// Used to make a teleportation effect as do_teleport does not like moving items from nullspace.
-/datum/controller/subsystem/blackmarket/proc/fake_teleport(atom/movable/item, turf/target)
- item.forceMove(target)
+/datum/controller/subsystem/blackmarket/proc/fake_teleport(datum/market_purchase/purchase, turf/target)
+ // Oopsie, whoopsie, the item is gone. So long, and thanks for all the money.
+ if(QDELETED(purchase))
+ return
+ var/atom/movable/thing = purchase.entry.spawn_item(target, purchase)
var/datum/effect_system/spark_spread/sparks = new
sparks.set_up(5, 1, target)
- sparks.attach(item)
+ sparks.attach(thing)
sparks.start()
+ qdel(purchase)
/// Used to add /datum/market_purchase to queued_purchases var. Returns TRUE when queued.
-/datum/controller/subsystem/blackmarket/proc/queue_item(datum/market_purchase/P)
- if(P.method == SHIPPING_METHOD_LTSRBT && !telepads.len)
+/datum/controller/subsystem/blackmarket/proc/queue_item(datum/market_purchase/purchase)
+ if((purchase.method == SHIPPING_METHOD_LTSRBT && !telepads.len) || isnull(purchase.uplink))
+ qdel(purchase)
return FALSE
- queued_purchases += P
+ queued_purchases += purchase
return TRUE
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
index 6c0db374f8a..e450a853607 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
@@ -80,11 +80,9 @@
if (is_banned_from(creature.ckey, list(antag_flag_override || antag_flag, ROLE_SYNDICATE)))
trimmed_list.Remove(creature)
continue
-
if (isnull(creature.mind))
continue
-
- if (restrict_ghost_roles && (creature.mind.assigned_role.title in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
+ if (restrict_ghost_roles && !(creature.mind.assigned_role.job_flags & JOB_CREW_MEMBER)) // Are they not playing a station role?
trimmed_list.Remove(creature)
continue
if (creature.mind.assigned_role.title in restricted_roles) // Does their job allow it?
diff --git a/code/controllers/subsystem/polling.dm b/code/controllers/subsystem/polling.dm
index 110968cfcae..1495e48c7ab 100644
--- a/code/controllers/subsystem/polling.dm
+++ b/code/controllers/subsystem/polling.dm
@@ -68,7 +68,7 @@ SUBSYSTEM_DEF(polling)
// Start firing
total_polls++
- if(!jump_target && isatom(alert_pic))
+ if(isnull(jump_target) && isatom(alert_pic))
jump_target = alert_pic
var/datum/candidate_poll/new_poll = new(role_name_text, question, poll_time, ignore_category, jump_target, custom_response_messages)
@@ -129,13 +129,15 @@ SUBSYSTEM_DEF(polling)
break
// Image to display
- var/image/poll_image = image('icons/effects/effects.dmi', icon_state = "static")
- if(alert_pic)
- if(!ispath(alert_pic))
- var/mutable_appearance/picture_source = alert_pic
- poll_image = picture_source
- else
- poll_image = image(alert_pic)
+ var/image/poll_image
+ if(ispath(alert_pic, /atom))
+ poll_image = image(alert_pic)
+ else if(isatom(alert_pic))
+ poll_image = new /mutable_appearance(alert_pic)
+ else if(!isnull(alert_pic))
+ poll_image = alert_pic
+ else
+ poll_image = image('icons/effects/effects.dmi', icon_state = "static")
if(poll_image)
poll_image.layer = FLOAT_LAYER
diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm
index b525f66576c..91f624972ad 100644
--- a/code/datums/ai/_ai_controller.dm
+++ b/code/datums/ai/_ai_controller.dm
@@ -34,7 +34,7 @@ multiple modular subtrees with behaviors
///Stored arguments for behaviors given during their initial creation
var/list/behavior_args = list()
///Tracks recent pathing attempts, if we fail too many in a row we fail our current plans.
- var/pathing_attempts
+ var/consecutive_pathing_attempts
///Can the AI remain in control if there is a client?
var/continue_processing_when_client = FALSE
///distance to give up on target
diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm
index af29e83f1a4..3f455b2acd0 100644
--- a/code/datums/ai/movement/_ai_movement.dm
+++ b/code/datums/ai/movement/_ai_movement.dm
@@ -8,22 +8,25 @@
//Override this to setup the moveloop you want to use
/datum/ai_movement/proc/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance)
SHOULD_CALL_PARENT(TRUE)
- controller.pathing_attempts = 0
+ controller.consecutive_pathing_attempts = 0
controller.set_blackboard_key(BB_CURRENT_MIN_MOVE_DISTANCE, min_distance)
moving_controllers[controller] = current_movement_target
/datum/ai_movement/proc/stop_moving_towards(datum/ai_controller/controller)
- controller.pathing_attempts = 0
+ controller.consecutive_pathing_attempts = 0
moving_controllers -= controller
// We got deleted as we finished an action
if(!QDELETED(controller.pawn))
SSmove_manager.stop_looping(controller.pawn, SSai_movement)
/datum/ai_movement/proc/increment_pathing_failures(datum/ai_controller/controller)
- controller.pathing_attempts++
- if(controller.pathing_attempts >= max_pathing_attempts)
+ controller.consecutive_pathing_attempts++
+ if(controller.consecutive_pathing_attempts >= max_pathing_attempts)
controller.CancelActions()
+/datum/ai_movement/proc/reset_pathing_failures(datum/ai_controller/controller)
+ controller.consecutive_pathing_attempts = 0
+
///Should the movement be allowed to happen? return TRUE if it can, FALSE otherwise
/datum/ai_movement/proc/allowed_to_move(datum/move_loop/source)
SHOULD_BE_PURE(TRUE)
@@ -68,7 +71,8 @@
//Anything to do post movement
/datum/ai_movement/proc/post_move(datum/move_loop/source, succeeded)
SIGNAL_HANDLER
- if(succeeded != FALSE)
- return
var/datum/ai_controller/controller = source.extra_info
+ if(succeeded != MOVELOOP_FAILURE)
+ reset_pathing_failures(controller)
+ return
increment_pathing_failures(controller)
diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm
index 6024b7e7562..825feefabfa 100644
--- a/code/datums/ai/movement/ai_movement_jps.dm
+++ b/code/datums/ai/movement/ai_movement_jps.dm
@@ -51,3 +51,6 @@
/datum/ai_movement/jps/bot/travel_to_beacon
maximum_length = AI_BOT_PATH_LENGTH
+
+/datum/ai_movement/jps/modsuit
+ maximum_length = MOD_AI_RANGE
diff --git a/code/datums/ai/objects/mod.dm b/code/datums/ai/objects/mod.dm
index 2bb555d281b..67d8121a4e1 100644
--- a/code/datums/ai/objects/mod.dm
+++ b/code/datums/ai/objects/mod.dm
@@ -5,7 +5,7 @@
BB_MOD_IMPLANT,
)
max_target_distance = MOD_AI_RANGE //a little spicy but its one specific item that summons it, and it doesn't run otherwise
- ai_movement = /datum/ai_movement/jps
+ ai_movement = /datum/ai_movement/jps/modsuit
///ID card generated from the suit's required access. Used for pathing.
var/obj/item/card/id/advanced/id_card
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 152de6ec1e1..52b649990d7 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -163,16 +163,16 @@
Pixel_y = round(cos(Angle)+32*cos(Angle)*(N+16)/32)
//Position the effect so the beam is one continous line
- var/a
+ var/final_x = segment.x
+ var/final_y = segment.y
if(abs(Pixel_x)>32)
- a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1)
- segment.x += a
+ final_x += Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1)
Pixel_x %= 32
if(abs(Pixel_y)>32)
- a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1)
- segment.y += a
+ final_y += Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1)
Pixel_y %= 32
+ segment.forceMove(locate(final_x, final_y, segment.z))
segment.pixel_x = origin_px + Pixel_x
segment.pixel_y = origin_py + Pixel_y
CHECK_TICK
@@ -194,6 +194,7 @@
return
var/mutable_appearance/emissive_overlay = emissive_appearance(icon, icon_state, src)
emissive_overlay.transform = transform
+ emissive_overlay.alpha = alpha
. += emissive_overlay
/obj/effect/ebeam/Destroy()
@@ -202,9 +203,69 @@
/obj/effect/ebeam/singularity_pull()
return
+
/obj/effect/ebeam/singularity_act()
return
+/// A beam subtype used for advanced beams, to react to atoms entering the beam
+/obj/effect/ebeam/reacting
+ /// If TRUE, atoms that exist in the beam's loc when inited count as "entering" the beam
+ var/react_on_init = FALSE
+
+/obj/effect/ebeam/reacting/Initialize(mapload, beam_owner)
+ . = ..()
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ COMSIG_ATOM_EXITED = PROC_REF(on_exited),
+ COMSIG_TURF_CHANGE = PROC_REF(on_turf_change),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
+ if(!isturf(loc) || isnull(owner) || mapload || !react_on_init)
+ return
+
+ for(var/atom/movable/existing as anything in loc)
+ beam_entered(existing)
+
+/obj/effect/ebeam/reacting/proc/on_entered(datum/source, atom/movable/entered)
+ SIGNAL_HANDLER
+
+ if(isnull(owner))
+ return
+
+ beam_entered(entered)
+
+/obj/effect/ebeam/reacting/proc/on_exited(datum/source, atom/movable/exited)
+ SIGNAL_HANDLER
+
+ if(isnull(owner))
+ return
+
+ beam_exited(exited)
+
+/obj/effect/ebeam/reacting/proc/on_turf_change(datum/source, path, new_baseturfs, flags, list/datum/callback/post_change_callbacks)
+ SIGNAL_HANDLER
+
+ if(isnull(owner))
+ return
+
+ beam_turfs_changed(post_change_callbacks)
+
+/// Some atom entered the beam's line
+/obj/effect/ebeam/reacting/proc/beam_entered(atom/movable/entered)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(owner, COMSIG_BEAM_ENTERED, src, entered)
+
+/// Some atom exited the beam's line
+/obj/effect/ebeam/reacting/proc/beam_exited(atom/movable/exited)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(owner, COMSIG_BEAM_EXITED, src, exited)
+
+/// Some turf the beam covers has changed to a new turf type
+/obj/effect/ebeam/reacting/proc/beam_turfs_changed(list/datum/callback/post_change_callbacks)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(owner, COMSIG_BEAM_TURFS_CHANGED, post_change_callbacks)
+
/**
* This is what you use to start a beam. Example: origin.Beam(target, args). **Store the return of this proc if you don't set maxdist or time, you need it to delete the beam.**
*
@@ -221,5 +282,3 @@
var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type, beam_color, emissive, override_origin_pixel_x, override_origin_pixel_y, override_target_pixel_x, override_target_pixel_y )
INVOKE_ASYNC(newbeam, TYPE_PROC_REF(/datum/beam/, Start))
return newbeam
-
-
diff --git a/code/datums/components/echolocation.dm b/code/datums/components/echolocation.dm
index 5e4f7528415..13ef8f4ed24 100644
--- a/code/datums/components/echolocation.dm
+++ b/code/datums/components/echolocation.dm
@@ -4,11 +4,11 @@
/// Time between echolocations.
var/cooldown_time = 1.8 SECONDS
/// Time for the image to start fading out.
- var/image_expiry_time = 1.5 SECONDS
+ var/image_expiry_time = 1.4 SECONDS
/// Time for the image to fade in.
- var/fade_in_time = 0.5 SECONDS
+ var/fade_in_time = 0.4 SECONDS
/// Time for the image to fade out and delete itself.
- var/fade_out_time = 0.5 SECONDS
+ var/fade_out_time = 0.4 SECONDS
/// Are images static? If yes, spawns them on the turf and makes them not change location. Otherwise they change location and pixel shift with the original.
var/images_are_static = TRUE
/// With mobs that have this echo group in their echolocation receiver trait, we share echo images.
@@ -32,7 +32,7 @@
/// Cooldown for the echolocation.
COOLDOWN_DECLARE(cooldown_last)
-/datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, blocking_trait, echo_group, echo_icon, color_path)
+/datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, blocking_trait, echo_group, echo_icon = "echo", color_path)
. = ..()
var/mob/living/echolocator = parent
if(!istype(echolocator))
diff --git a/code/datums/components/food/ghost_edible.dm b/code/datums/components/food/ghost_edible.dm
index fb8a0c55559..0e3f3b3dd66 100644
--- a/code/datums/components/food/ghost_edible.dm
+++ b/code/datums/components/food/ghost_edible.dm
@@ -51,7 +51,7 @@
if (!prob(munch_chance))
return
playsound(atom_parent.loc,'sound/items/eatfood.ogg', vol = rand(10,50), vary = TRUE)
- atom_parent.reagents.remove_any(bite_consumption)
+ atom_parent.reagents.remove_all(bite_consumption)
if (atom_parent.reagents.total_volume <= 0)
atom_parent.visible_message(span_notice("[atom_parent] disappears completely!"))
new /obj/item/ectoplasm(atom_parent.loc)
diff --git a/code/datums/components/food/ice_cream_holder.dm b/code/datums/components/food/ice_cream_holder.dm
index f3db67efab5..4161c749703 100644
--- a/code/datums/components/food/ice_cream_holder.dm
+++ b/code/datums/components/food/ice_cream_holder.dm
@@ -58,6 +58,7 @@
RegisterSignal(owner, COMSIG_ITEM_ATTACK_ATOM, PROC_REF(on_item_attack_obj))
RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
+ RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
if(change_name)
RegisterSignal(owner, COMSIG_ATOM_UPDATE_NAME, PROC_REF(on_update_name))
if(!change_desc)
@@ -105,20 +106,27 @@
else
source.desc = replacetext(replacetext("[flavour.desc_prefix] [flavour.desc]", "$CONE_NAME", initial(source.name)), "$CUSTOM_NAME", key)
else /// Many flavours.
- source.desc = "A delicious [initial(source.name)] filled with scoops of [english_list(scoops)] icecream. That's as many as [scoops_len] scoops!"
+ source.desc = "A delicious [initial(source.name)] filled with scoops of [english_list(scoops)] ice cream. That's as many as [scoops_len] scoops!"
+
+/datum/component/ice_cream_holder/proc/on_examine(atom/source, mob/mob, list/examine_list)
+ SIGNAL_HANDLER
+ if(length(scoops) < max_scoops)
+ examine_list += span_tinynoticeital("you could use a ice cream vat to fill it with yummy ice cream...")
/datum/component/ice_cream_holder/proc/on_examine_more(atom/source, mob/mob, list/examine_list)
SIGNAL_HANDLER
var/scoops_len = length(scoops)
+ if(!scoops_len)
+ return
if(scoops_len == 1 || length(unique_list(scoops)) == 1) /// Only one flavour.
var/key = scoops[1]
var/datum/ice_cream_flavour/flavour = GLOB.ice_cream_flavours[LAZYACCESS(special_scoops, key) || key]
if(flavour?.desc) //I scream.
- examine_list += "[source.p_Theyre()] filled with scoops of [flavour ? flavour.name : "broken, unhappy"] icecream."
+ examine_list += "[source.p_Theyre()] filled with scoops of [flavour ? flavour.name : "broken, unhappy"] ice cream."
else
examine_list += replacetext(replacetext("[source.p_Theyre()] [flavour.desc]", "$CONE_NAME", initial(source.name)), "$CUSTOM_NAME", key)
else /// Many flavours.
- examine_list += "[source.p_Theyre()] filled with scoops of [english_list(scoops)] icecream. That's as many as [scoops_len] scoops!"
+ examine_list += "[source.p_Theyre()] filled with scoops of [english_list(scoops)] ice cream. That's as many as [scoops_len] scoops!"
/datum/component/ice_cream_holder/proc/on_update_overlays(atom/source, list/new_overlays)
SIGNAL_HANDLER
@@ -330,14 +338,14 @@ GLOBAL_LIST_INIT_TYPED(ice_cream_flavours, /datum/ice_cream_flavour, init_ice_cr
/datum/ice_cream_flavour/custom
name = ICE_CREAM_CUSTOM
color = COLOR_STARLIGHT //has its own mutable appearance overlay it will be overwritten with anyways.
- desc = "filled with artisanal icecream. Made with real $CUSTOM_NAME. Ain't that something."
+ desc = "filled with artisanal ice cream. Made with real $CUSTOM_NAME. Ain't that something."
ingredients = list(/datum/reagent/consumable/milk, /datum/reagent/consumable/ice)
ingredients_text = "optional flavorings"
takes_custom_ingredients = TRUE
/datum/ice_cream_flavour/custom/korta
name = ICE_CREAM_KORTA_CUSTOM
- desc = "filled with artisanal lizard-friendly icecream. Made with real $CUSTOM_NAME. Ain't that something."
+ desc = "filled with artisanal lizard-friendly ice cream. Made with real $CUSTOM_NAME. Ain't that something."
ingredients = list(/datum/reagent/consumable/korta_milk, /datum/reagent/consumable/ice)
ingredients_text = "optional flavorings"
@@ -354,7 +362,7 @@ GLOBAL_LIST_INIT_TYPED(ice_cream_flavours, /datum/ice_cream_flavour, init_ice_cr
/datum/ice_cream_flavour/bland
name = ICE_CREAM_BLAND
color = COLOR_ICECREAM_CUSTOM
- desc = "filled with anemic, flavorless icecream. You wonder why this was ever scooped..."
+ desc = "filled with anemic, flavorless ice cream. You wonder why this was ever scooped..."
hidden = TRUE
#undef SWEETENER_PER_SCOOP
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index 53aa87b74bb..7f916f47e4b 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -459,6 +459,9 @@
keytype = /obj/item/key/lasso
vehicle_move_delay = 4
+/datum/component/riding/creature/goliath/deathmatch
+ keytype = null
+
/datum/component/riding/creature/goliath/Initialize(mob/living/riding_mob, force, ride_check_flags, potion_boost)
. = ..()
var/mob/living/basic/mining/goliath/goliath = parent
diff --git a/code/datums/components/riding/riding_vehicle.dm b/code/datums/components/riding/riding_vehicle.dm
index bacf012aa5a..8935634cb42 100644
--- a/code/datums/components/riding/riding_vehicle.dm
+++ b/code/datums/components/riding/riding_vehicle.dm
@@ -169,6 +169,8 @@
/datum/component/riding/vehicle/scooter/skateboard
vehicle_move_delay = 1.5
ride_check_flags = RIDER_NEEDS_LEGS | UNBUCKLE_DISABLED_RIDER
+ ///If TRUE, the vehicle will be slower (but safer) to ride on walk intent.
+ var/can_slow_down = TRUE
/datum/component/riding/vehicle/scooter/skateboard/handle_specials()
. = ..()
@@ -177,8 +179,82 @@
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
+/datum/component/riding/vehicle/scooter/skateboard/RegisterWithParent()
+ . = ..()
+ if(can_slow_down)
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+ var/obj/vehicle/ridden/scooter/skateboard/board = parent
+ if(istype(board))
+ board.can_slow_down = can_slow_down
+
+/datum/component/riding/vehicle/scooter/skateboard/proc/on_examine(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+ examine_list += span_notice("Going slow and nice at [EXAMINE_HINT("walk")] speed will prevent crashing into things.")
+
+/datum/component/riding/vehicle/scooter/skateboard/vehicle_mob_buckle(datum/source, mob/living/rider, force = FALSE)
+ . = ..()
+ if(can_slow_down)
+ RegisterSignal(rider, COMSIG_MOVE_INTENT_TOGGLED, PROC_REF(toggle_move_delay))
+ toggle_move_delay(rider)
+
+/datum/component/riding/vehicle/scooter/skateboard/handle_unbuckle(mob/living/rider)
+ . = ..()
+ if(can_slow_down)
+ toggle_move_delay(rider)
+ UnregisterSignal(rider, COMSIG_MOVE_INTENT_TOGGLED)
+
+/datum/component/riding/vehicle/scooter/skateboard/proc/toggle_move_delay(mob/living/rider)
+ SIGNAL_HANDLER
+ vehicle_move_delay = initial(vehicle_move_delay)
+ if(rider.move_intent == MOVE_INTENT_WALK)
+ vehicle_move_delay += 0.6
+
+/datum/component/riding/vehicle/scooter/skateboard/pro
+ vehicle_move_delay = 1
+
+///This one lets the rider ignore gravity, move in zero g and son on, but only on ground turfs or at most one z-level above them.
+/datum/component/riding/vehicle/scooter/skateboard/hover
+ vehicle_move_delay = 1
+ override_allow_spacemove = TRUE
+
+/datum/component/riding/vehicle/scooter/skateboard/hover/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_ATOM_HAS_GRAVITY, PROC_REF(check_grav))
+ RegisterSignal(parent, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(check_drifting))
+ hover_check()
+
+///Makes sure that the vehicle is grav-less if capable of zero-g movement. Forced gravity will honestly screw this.
+/datum/component/riding/vehicle/scooter/skateboard/hover/proc/check_grav(datum/source, turf/gravity_turf, list/gravs)
+ SIGNAL_HANDLER
+ if(override_allow_spacemove)
+ gravs += 0
+
+///Makes sure the vehicle isn't drifting while it can be maneuvered.
+/datum/component/riding/vehicle/scooter/skateboard/hover/proc/check_drifting(datum/source, movement_dir, continuous_move)
+ SIGNAL_HANDLER
+ if(override_allow_spacemove)
+ return COMSIG_MOVABLE_STOP_SPACEMOVE
+
+/datum/component/riding/vehicle/scooter/skateboard/hover/vehicle_moved(atom/movable/source, oldloc, dir, forced)
+ . = ..()
+ hover_check(TRUE)
+
+///Makes sure that the hoverboard can move in zero-g in (open) space but only there's a ground turf on the z-level below.
+/datum/component/riding/vehicle/scooter/skateboard/hover/proc/hover_check(is_moving = FALSE)
+ var/atom/movable/movable = parent
+ if(!isopenspaceturf(movable.loc))
+ override_allow_spacemove = TRUE
+ return
+ var/turf/open/our_turf = movable.loc
+ var/turf/turf_below = GET_TURF_BELOW(our_turf)
+ if(our_turf.zPassOut(DOWN) && (isnull(turf_below) || (isopenspaceturf(turf_below) && turf_below.zPassIn(DOWN) && turf_below.zPassOut(DOWN))))
+ override_allow_spacemove = FALSE
+ if(turf_below)
+ our_turf.zFall(movable, falling_from_move = is_moving)
+
/datum/component/riding/vehicle/scooter/skateboard/wheelys
vehicle_move_delay = 1.75 // NOVA EDIT - ORIGINAL: 0
+ can_slow_down = FALSE
/datum/component/riding/vehicle/scooter/skateboard/wheelys/handle_specials()
. = ..()
diff --git a/code/datums/components/shrink.dm b/code/datums/components/shrink.dm
index 67cd3d39e23..d2615ea2f77 100644
--- a/code/datums/components/shrink.dm
+++ b/code/datums/components/shrink.dm
@@ -15,6 +15,7 @@
if(isliving(parent_atom))
var/mob/living/L = parent_atom
ADD_TRAIT(L, TRAIT_UNDENSE, SHRUNKEN_TRAIT)
+ RegisterSignal(L, COMSIG_MOB_SAY, PROC_REF(handle_shrunk_speech))
L.add_movespeed_modifier(/datum/movespeed_modifier/shrink_ray)
if(iscarbon(L))
var/mob/living/carbon/C = L
@@ -30,6 +31,10 @@
span_userdanger("Everything grows bigger!"))
QDEL_IN(src, shrink_time)
+/datum/component/shrink/proc/handle_shrunk_speech(mob/living/little_guy, list/speech_args)
+ SIGNAL_HANDLER
+ speech_args[SPEECH_SPANS] |= SPAN_SMALL_VOICE
+
/datum/component/shrink/Destroy()
var/atom/parent_atom = parent
parent_atom.transform = parent_atom.transform.Scale(2,2)
@@ -38,6 +43,7 @@
var/mob/living/L = parent_atom
L.remove_movespeed_modifier(/datum/movespeed_modifier/shrink_ray)
REMOVE_TRAIT(L, TRAIT_UNDENSE, SHRUNKEN_TRAIT)
+ UnregisterSignal(L, COMSIG_MOB_SAY)
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.physiology.damage_resistance += 100
diff --git a/code/datums/components/tameable.dm b/code/datums/components/tameable.dm
index 67325b489d3..43f48005bf8 100644
--- a/code/datums/components/tameable.dm
+++ b/code/datums/components/tameable.dm
@@ -10,10 +10,8 @@
var/bonus_tame_chance
///Current chance to tame on interaction
var/current_tame_chance
- ///For effects once soemthing is tamed
- var/datum/callback/after_tame
-/datum/component/tameable/Initialize(food_types, tame_chance, bonus_tame_chance, datum/callback/after_tame, unique = TRUE)
+/datum/component/tameable/Initialize(food_types, tame_chance, bonus_tame_chance, unique = TRUE)
if(!isatom(parent)) //yes, you could make a tameable toolbox.
return COMPONENT_INCOMPATIBLE
@@ -24,18 +22,12 @@
src.current_tame_chance = tame_chance
if(bonus_tame_chance)
src.bonus_tame_chance = bonus_tame_chance
- if(after_tame)
- src.after_tame = after_tame
src.unique = unique
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(try_tame))
RegisterSignal(parent, COMSIG_SIMPLEMOB_SENTIENCEPOTION, PROC_REF(on_tame)) //Instantly succeeds
RegisterSignal(parent, COMSIG_SIMPLEMOB_TRANSFERPOTION, PROC_REF(on_tame)) //Instantly succeeds
-/datum/component/tameable/Destroy(force)
- after_tame = null
- return ..()
-
/datum/component/tameable/proc/try_tame(datum/source, obj/item/food, mob/living/attacker, params)
SIGNAL_HANDLER
if(!is_type_in_list(food, food_types))
@@ -70,9 +62,9 @@
return living_parent.faction.Find(REF(potential_friend))
///Ran once taming succeeds
-/datum/component/tameable/proc/on_tame(atom/source, mob/living/tamer, atom/food, inform_tamer = FALSE)
+/datum/component/tameable/proc/on_tame(atom/source, mob/living/tamer, obj/item/food, inform_tamer = FALSE)
SIGNAL_HANDLER
- after_tame?.Invoke(tamer, food)//Run custom behavior if needed
+ source.tamed(tamer, food)//Run custom behavior if needed
if(isliving(parent) && isliving(tamer))
INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living, befriend), tamer)
diff --git a/code/datums/elements/chewable.dm b/code/datums/elements/chewable.dm
index 21d546be6aa..d520597f431 100644
--- a/code/datums/elements/chewable.dm
+++ b/code/datums/elements/chewable.dm
@@ -52,7 +52,7 @@
var/metabolism_amount = metabolization_amount * seconds_per_tick
if (!reagents.trans_to(item.loc, metabolism_amount, methods = INGEST))
- reagents.remove_any(metabolism_amount)
+ reagents.remove_all(metabolism_amount)
/datum/element/chewable/proc/on_dropped(datum/source)
SIGNAL_HANDLER
diff --git a/code/datums/elements/gags_recolorable.dm b/code/datums/elements/gags_recolorable.dm
index bf37a4ba973..93b21c5d31d 100644
--- a/code/datums/elements/gags_recolorable.dm
+++ b/code/datums/elements/gags_recolorable.dm
@@ -10,7 +10,7 @@
/datum/element/gags_recolorable/proc/on_examine(atom/source, mob/user, list/examine_text)
SIGNAL_HANDLER
- examine_text += span_notice("You could recolor [source.p_them()] with a spraycan...")
+ examine_text += span_notice("Now utilising PPP recolouring technology, capable of absorbing paint and pigments for changing it's colours!")
/datum/element/gags_recolorable/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
SIGNAL_HANDLER
diff --git a/code/datums/elements/inverted_movement.dm b/code/datums/elements/inverted_movement.dm
new file mode 100644
index 00000000000..4de935e81da
--- /dev/null
+++ b/code/datums/elements/inverted_movement.dm
@@ -0,0 +1,17 @@
+/datum/element/inverted_movement
+
+/datum/element/inverted_movement/Attach(datum/target)
+ . = ..()
+ if(!isliving(target))
+ return ELEMENT_INCOMPATIBLE
+ RegisterSignal(target, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(invert_movement))
+
+/datum/element/inverted_movement/Detach(datum/source)
+ UnregisterSignal(source, COMSIG_MOB_CLIENT_PRE_MOVE)
+ return ..()
+
+/datum/element/inverted_movement/proc/invert_movement(mob/living/source, move_args)
+ SIGNAL_HANDLER
+ var/new_direct = REVERSE_DIR(move_args[MOVE_ARG_DIRECTION])
+ move_args[MOVE_ARG_DIRECTION] = new_direct
+ move_args[MOVE_ARG_NEW_LOC] = get_step(source, new_direct)
diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm
index 9d671f9cd93..6d0d2935dc1 100644
--- a/code/datums/elements/strippable.dm
+++ b/code/datums/elements/strippable.dm
@@ -51,7 +51,7 @@
if (!isnull(should_strip_proc_path) && !call(source, should_strip_proc_path)(user))
return
- var/datum/strip_menu/strip_menu
+ var/datum/strip_menu/strip_menu = LAZYACCESS(strip_menus, source)
if (isnull(strip_menu))
strip_menu = new(source, src)
diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
index 6e71e2ee486..0ab5d2444af 100644
--- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
+++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
@@ -333,6 +333,42 @@ NOVA EDIT END */
name = "Sundress (Worn)"
icon_file = 'icons/mob/clothing/under/dress.dmi'
+/datum/greyscale_config/striped_dress
+ name = "Striped dress"
+ icon_file = 'icons/obj/clothing/under/dress.dmi'
+ json_config = 'code/datums/greyscale/json_configs/striped_dress.json'
+
+/datum/greyscale_config/striped_dress/worn
+ name = "Striped dress (Worn)"
+ icon_file = 'icons/mob/clothing/under/dress.dmi'
+
+/datum/greyscale_config/evening_dress
+ name = "Evening gown"
+ icon_file = 'icons/obj/clothing/under/dress.dmi'
+ json_config = 'code/datums/greyscale/json_configs/eveningdress.json'
+
+/datum/greyscale_config/evening_dress/worn
+ name = "Evening gown (Worn)"
+ icon_file = 'icons/mob/clothing/under/dress.dmi'
+
+/datum/greyscale_config/cardiganskirt
+ name = "Cardigan Skirt"
+ icon_file = 'icons/obj/clothing/under/dress.dmi'
+ json_config = 'code/datums/greyscale/json_configs/cardiganskirt.json'
+
+/datum/greyscale_config/cardiganskirt/worn
+ name = "Cardigan Skirt(Worn)"
+ icon_file = 'icons/mob/clothing/under/dress.dmi'
+
+/datum/greyscale_config/sailor_dress
+ name = "Sailor Dress"
+ icon_file = 'icons/obj/clothing/under/dress.dmi'
+ json_config = 'code/datums/greyscale/json_configs/sailor_dress.json'
+
+/datum/greyscale_config/sailor_dress/worn
+ name = "Sailor Dress(Worn)"
+ icon_file = 'icons/mob/clothing/under/dress.dmi'
+
// SHIRTS, PANTS AND SHORTS
/datum/greyscale_config/slacks
name = "Slacks"
@@ -752,3 +788,12 @@ NOVA EDIT END */
/datum/greyscale_config/messyworn_shirt_graphic/worn
name = "Messy Shirt (Graphic (Worn)"
icon_file = 'icons/mob/clothing/suits/costume.dmi'
+
+/datum/greyscale_config/gi
+ name = "Gi"
+ icon_file = 'icons/obj/clothing/under/costume.dmi'
+ json_config = 'code/datums/greyscale/json_configs/gi.json'
+
+/datum/greyscale_config/gi/worn
+ name = "Gi (Worn)"
+ icon_file = 'icons/mob/clothing/under/costume.dmi'
diff --git a/code/datums/greyscale/json_configs/cardiganskirt.json b/code/datums/greyscale/json_configs/cardiganskirt.json
new file mode 100644
index 00000000000..97a87b6405c
--- /dev/null
+++ b/code/datums/greyscale/json_configs/cardiganskirt.json
@@ -0,0 +1,21 @@
+{
+ "cardiganskirt": [
+ {
+ "type": "icon_state",
+ "icon_state": "cardiganskirt_skirt",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "cardiganskirt_cardigan",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "cardiganskirt_collar",
+ "blend_mode": "overlay"
+ }
+ ]
+}
diff --git a/code/datums/greyscale/json_configs/eveningdress.json b/code/datums/greyscale/json_configs/eveningdress.json
new file mode 100644
index 00000000000..d08dc95bbbe
--- /dev/null
+++ b/code/datums/greyscale/json_configs/eveningdress.json
@@ -0,0 +1,10 @@
+{
+ "evening_gown": [
+ {
+ "type": "icon_state",
+ "icon_state": "evening_gown",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ]
+}
diff --git a/code/datums/greyscale/json_configs/gi.json b/code/datums/greyscale/json_configs/gi.json
new file mode 100644
index 00000000000..7534552293b
--- /dev/null
+++ b/code/datums/greyscale/json_configs/gi.json
@@ -0,0 +1,36 @@
+{
+ "martial_arts_gi": [
+ {
+ "type": "icon_state",
+ "icon_state": "gi_base",
+ "blend_mode": "overlay",
+ "color_ids": [1]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "gi_belt",
+ "blend_mode": "overlay",
+ "color_ids": [2]
+ }
+ ],
+
+ "martial_arts_gi_goku": [
+ {
+ "type": "icon_state",
+ "icon_state": "gi_base",
+ "blend_mode": "overlay",
+ "color_ids": [1]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "gi_belt",
+ "blend_mode": "overlay",
+ "color_ids": [2]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "gi_goku",
+ "blend_mode": "overlay"
+ }
+ ]
+}
diff --git a/code/datums/greyscale/json_configs/sailor_dress.json b/code/datums/greyscale/json_configs/sailor_dress.json
new file mode 100644
index 00000000000..86787f4a251
--- /dev/null
+++ b/code/datums/greyscale/json_configs/sailor_dress.json
@@ -0,0 +1,27 @@
+{
+ "sailor_dress": [
+ {
+ "type": "icon_state",
+ "icon_state": "sailor_dress_skirt",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sailor_dress_jacket",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sailor_dress_stripes",
+ "blend_mode": "overlay",
+ "color_ids": [ 3 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sailor_dress_badge",
+ "blend_mode": "overlay"
+ }
+ ]
+}
diff --git a/code/datums/greyscale/json_configs/striped_dress.json b/code/datums/greyscale/json_configs/striped_dress.json
new file mode 100644
index 00000000000..e4b95329e46
--- /dev/null
+++ b/code/datums/greyscale/json_configs/striped_dress.json
@@ -0,0 +1,22 @@
+{
+ "stripeddress": [
+ {
+ "type": "icon_state",
+ "icon_state": "stripeddress_jacket",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "stripeddress_base",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "stripeddress_stripes",
+ "blend_mode": "overlay",
+ "color_ids": [ 3 ]
+ }
+ ]
+}
diff --git a/code/datums/quirks/negative_quirks/hemiplegic.dm b/code/datums/quirks/negative_quirks/hemiplegic.dm
index 459b880fad2..b82ad434dfb 100644
--- a/code/datums/quirks/negative_quirks/hemiplegic.dm
+++ b/code/datums/quirks/negative_quirks/hemiplegic.dm
@@ -12,10 +12,19 @@
/obj/item/reagent_containers/cup/glass/drinkingglass/filled/half_full,
)
-/datum/quirk/hemiplegic/add(client/client_source)
+/datum/quirk_constant_data/hemiplegic
+ associated_typepath = /datum/quirk/hemiplegic
+ customization_options = list(/datum/preference/choiced/hemiplegic)
+
+/datum/quirk/hemiplegic/add_unique(client/client_source)
+ var/datum/brain_trauma/severe/paralysis/hemiplegic/side_choice = GLOB.side_choice_hemiplegic[client_source?.prefs?.read_preference(/datum/preference/choiced/hemiplegic)]
+ if(isnull(side_choice)) // Client gone or they chose a random side
+ side_choice = GLOB.side_choice_hemiplegic[pick(GLOB.side_choice_hemiplegic)]
+
var/mob/living/carbon/human/human_holder = quirk_holder
- var/trauma_type = pick(/datum/brain_trauma/severe/paralysis/hemiplegic/left, /datum/brain_trauma/severe/paralysis/hemiplegic/right)
- human_holder.gain_trauma(trauma_type, TRAUMA_RESILIENCE_ABSOLUTE)
+
+ medical_record_text = "Patient has an untreatable impairment in motor function on the [side_choice::paralysis_type] half of their body."
+ human_holder.gain_trauma(side_choice, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/hemiplegic/remove()
var/mob/living/carbon/human/human_holder = quirk_holder
diff --git a/code/datums/status_effects/buffs/food/chilling.dm b/code/datums/status_effects/buffs/food/chilling.dm
new file mode 100644
index 00000000000..8b1d60fbcc1
--- /dev/null
+++ b/code/datums/status_effects/buffs/food/chilling.dm
@@ -0,0 +1,13 @@
+///food effect applied by ice cream and frozen treats
+/datum/status_effect/food/chilling
+ alert_type = /atom/movable/screen/alert/status_effect/icecream_chilling //different path, so we sprite one state and not five.
+
+/datum/status_effect/food/chilling/tick(seconds_between_ticks)
+ var/minimum_temp = (BODYTEMP_HEAT_DAMAGE_LIMIT - 12 * strength)
+ if(owner.bodytemperature >= minimum_temp)
+ owner.adjust_bodytemperature(-2.75 * strength * seconds_between_ticks, min_temp = minimum_temp)
+
+/atom/movable/screen/alert/status_effect/icecream_chilling
+ desc = "Nothing beats a cup of ice cream during hot, plasma-floody day..."
+ icon_state = "food_icecream"
+
diff --git a/code/datums/status_effects/buffs/food_traits.dm b/code/datums/status_effects/buffs/food/food_traits.dm
similarity index 51%
rename from code/datums/status_effects/buffs/food_traits.dm
rename to code/datums/status_effects/buffs/food/food_traits.dm
index ebe22116dd0..dfd0b888aa0 100644
--- a/code/datums/status_effects/buffs/food_traits.dm
+++ b/code/datums/status_effects/buffs/food/food_traits.dm
@@ -1,8 +1,7 @@
/datum/status_effect/food/trait/shockimmune
- alert_type = /atom/movable/screen/alert/status_effect/food_trait_shockimmune
+ alert_type = /atom/movable/screen/alert/status_effect/food/trait_shockimmune
trait = TRAIT_SHOCKIMMUNE
-/atom/movable/screen/alert/status_effect/food_trait_shockimmune
+/atom/movable/screen/alert/status_effect/food/trait_shockimmune
name = "Grounded"
desc = "That meal made me feel like a superconductor..."
- icon_state = "food_buff_4"
diff --git a/code/datums/status_effects/buffs/food/haste.dm b/code/datums/status_effects/buffs/food/haste.dm
new file mode 100644
index 00000000000..8dbe7a7762e
--- /dev/null
+++ b/code/datums/status_effects/buffs/food/haste.dm
@@ -0,0 +1,27 @@
+///Haste makes the eater move and act faster
+/datum/status_effect/food/haste
+
+/datum/status_effect/food/haste/on_apply()
+ var/datum/movespeed_modifier/food_haste/speed_mod = new()
+ speed_mod.multiplicative_slowdown = -0.04 * strength
+ owner.add_movespeed_modifier(speed_mod, update = TRUE)
+ var/datum/actionspeed_modifier/status_effect/food_haste/action_mod = new()
+ action_mod.multiplicative_slowdown = -0.06 * strength
+ owner.add_actionspeed_modifier(action_mod, update = TRUE)
+ return ..()
+
+/datum/status_effect/food/haste/be_replaced()
+ owner.remove_movespeed_modifier(/datum/movespeed_modifier/food_haste)
+ owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/status_effect/food_haste)
+ return ..()
+
+/datum/status_effect/food/haste/on_remove()
+ owner.remove_movespeed_modifier(/datum/movespeed_modifier/food_haste, update = TRUE)
+ owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/status_effect/food_haste, update = TRUE)
+ return ..()
+
+/datum/movespeed_modifier/food_haste
+ multiplicative_slowdown = -0.04
+
+/datum/actionspeed_modifier/status_effect/food_haste
+ multiplicative_slowdown = -0.06
diff --git a/code/datums/status_effects/buffs/food_haste.dm b/code/datums/status_effects/buffs/food_haste.dm
deleted file mode 100644
index 9daf859fb19..00000000000
--- a/code/datums/status_effects/buffs/food_haste.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-/// Haste makes the eater move faster
-/datum/status_effect/food/haste
- var/datum/movespeed_modifier/food_haste/modifier
-
-/datum/status_effect/food/haste/on_apply()
- modifier = new()
- modifier.multiplicative_slowdown = -0.04 * strength
- owner.add_movespeed_modifier(modifier, update = TRUE)
- return ..()
-
-/datum/status_effect/food/haste/be_replaced()
- owner.remove_movespeed_modifier(modifier, update = TRUE)
- return ..()
-
-/datum/status_effect/food/haste/on_remove()
- owner.remove_movespeed_modifier(modifier, update = TRUE)
- return ..()
-
-/datum/movespeed_modifier/food_haste
- multiplicative_slowdown = -0.1
diff --git a/code/datums/status_effects/food_effects.dm b/code/datums/status_effects/food_effects.dm
index e41ef67ad10..deba7bf750b 100644
--- a/code/datums/status_effects/food_effects.dm
+++ b/code/datums/status_effects/food_effects.dm
@@ -3,37 +3,24 @@
id = "food_buff"
duration = 5 MINUTES // Same as food mood buffs
status_type = STATUS_EFFECT_REPLACE // Only one food buff allowed
+ alert_type = /atom/movable/screen/alert/status_effect/food
/// Buff power
var/strength
/datum/status_effect/food/on_creation(mob/living/new_owner, timeout_mod = 1, strength = 1)
src.strength = strength
//Generate alert when not specified
- if(alert_type == /atom/movable/screen/alert/status_effect)
- alert_type = "/atom/movable/screen/alert/status_effect/food/buff_[strength]"
if(isnum(timeout_mod))
duration *= timeout_mod
. = ..()
+ if(istype(linked_alert, /atom/movable/screen/alert/status_effect/food))
+ linked_alert.icon_state = "[linked_alert.base_icon_state]_[strength]"
/atom/movable/screen/alert/status_effect/food
name = "Hand-crafted meal"
desc = "Eating it made me feel better."
icon_state = "food_buff_1"
-
-/atom/movable/screen/alert/status_effect/food/buff_1
- icon_state = "food_buff_1"
-
-/atom/movable/screen/alert/status_effect/food/buff_2
- icon_state = "food_buff_2"
-
-/atom/movable/screen/alert/status_effect/food/buff_3
- icon_state = "food_buff_3"
-
-/atom/movable/screen/alert/status_effect/food/buff_4
- icon_state = "food_buff_4"
-
-/atom/movable/screen/alert/status_effect/food/buff_5
- icon_state = "food_buff_5"
+ base_icon_state = "food_buff"
/// Makes you gain a trait
/datum/status_effect/food/trait
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index f57fdf7f4af..c26ddd524ce 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -76,7 +76,7 @@
var/obj/machinery/door/airlock/airlock = holder
if(!HAS_SILICON_ACCESS(user) && !isdrone(user) && airlock.isElectrified())
var/mob/living/carbon/carbon_user = user
- if (!istype(carbon_user) || carbon_user.should_electrocute(src))
+ if (!istype(carbon_user) || carbon_user.should_electrocute(get_area(airlock)))
return FALSE
if(airlock.is_secure())
return FALSE
diff --git a/code/datums/wires/collar_bomb.dm b/code/datums/wires/collar_bomb.dm
new file mode 100644
index 00000000000..574a3d52dbc
--- /dev/null
+++ b/code/datums/wires/collar_bomb.dm
@@ -0,0 +1,33 @@
+/datum/wires/collar_bomb
+ proper_name = "Collar Bomb"
+ randomize = TRUE // Only one wire, no need for blueprints
+ holder_type = /obj/item/clothing/neck/collar_bomb
+ wires = list(WIRE_ACTIVATE)
+
+/datum/wires/collar_bomb/interactable(mob/user)
+ . = ..()
+ if(user.get_item_by_slot(ITEM_SLOT_NECK) == holder)
+ return FALSE
+
+/datum/wires/collar_bomb/on_pulse(wire, mob/user)
+ var/obj/item/clothing/neck/collar_bomb/collar = holder
+ if(collar.active)
+ return ..()
+ collar.explosive_countdown(ticks_left = 5)
+ if(!ishuman(collar.loc))
+ return ..()
+ var/mob/living/carbon/human/brian = collar.loc
+ if(brian.get_item_by_slot(ITEM_SLOT_NECK) != collar)
+ return ..()
+ var/mob/living/triggerer = user
+ var/obj/item/assembly/assembly
+ if(isnull(triggerer))
+ assembly = assemblies[colors[1]]
+ if(assembly)
+ triggerer = get_mob_by_key(assembly.fingerprintslast)
+ brian.investigate_log("has had their [collar] triggered [triggerer ? "by [user || assembly][assembly ? " last touched by triggerer" : ""]" : ""].", INVESTIGATE_DEATHS)
+ return ..()
+
+///I'd rather not get people killed by EMP here.
+/datum/wires/collar_bomb/emp_pulse()
+ return
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index 499707e90e0..873d092e4f6 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -24,12 +24,21 @@
break
..()
+/datum/wires/vending/interact(mob/user)
+ var/obj/machinery/vending/vending_machine = holder
+ if (!HAS_SILICON_ACCESS(user) && vending_machine.seconds_electrified && vending_machine.shock(user, 100))
+ return
+
+ return ..()
+
/datum/wires/vending/interactable(mob/user)
if(!..())
return FALSE
var/obj/machinery/vending/vending_machine = holder
- if(!HAS_SILICON_ACCESS(user) && vending_machine.seconds_electrified && vending_machine.shock(user, 100))
- return FALSE
+ if(!HAS_SILICON_ACCESS(user) && vending_machine.seconds_electrified)
+ var/mob/living/carbon/carbon_user = user
+ if (!istype(carbon_user) || carbon_user.should_electrocute(get_area(vending_machine)))
+ return FALSE
if(vending_machine.panel_open)
return TRUE
@@ -57,6 +66,7 @@
vending_machine.extended_inventory = !vending_machine.extended_inventory
if(WIRE_SHOCK)
vending_machine.seconds_electrified = MACHINE_DEFAULT_ELECTRIFY_TIME
+ vending_machine.shock(usr, 100)
if(WIRE_IDSCAN)
vending_machine.scan_id = !vending_machine.scan_id
if(WIRE_SPEAKER)
@@ -74,7 +84,11 @@
if(WIRE_CONTRABAND)
vending_machine.extended_inventory = FALSE
if(WIRE_SHOCK)
- vending_machine.seconds_electrified = mend ? MACHINE_NOT_ELECTRIFIED : MACHINE_ELECTRIFIED_PERMANENT
+ if (mend)
+ vending_machine.seconds_electrified = MACHINE_NOT_ELECTRIFIED
+ else
+ vending_machine.seconds_electrified = MACHINE_ELECTRIFIED_PERMANENT
+ vending_machine.shock(usr, 100)
if(WIRE_IDSCAN)
vending_machine.scan_id = mend
if(WIRE_SPEAKER)
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index 4a7d113ba65..3956d008ea7 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -794,6 +794,10 @@
/atom/proc/setClosed()
return
+///Called after the atom is 'tamed' for type-specific operations, Usually called by the tameable component but also other things.
+/atom/proc/tamed(mob/living/tamer, obj/item/food)
+ return
+
/**
* Used to attempt to charge an object with a payment component.
*
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index e0588e46932..43e37456a72 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -433,7 +433,7 @@
if(z_move_flags & ZMOVE_CAN_FLY_CHECKS && !(movement_type & (FLYING|FLOATING)) && has_gravity(start))
if(z_move_flags & ZMOVE_FEEDBACK)
if(rider)
- to_chat(rider, span_warning("[src] is is not capable of flight."))
+ to_chat(rider, span_warning("[src] [p_are()] incapable of flight."))
else
to_chat(src, span_warning("You are not Superman."))
return FALSE
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 7c7deb058f2..8085c989d52 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -1030,6 +1030,11 @@ GLOBAL_LIST_EMPTY(possible_items)
var/payout_bonus = 0
var/area/dropoff = null
+/datum/objective/contract/is_valid_target(datum/mind/possible_target)
+ if(HAS_TRAIT(possible_target, TRAIT_HAS_BEEN_KIDNAPPED))
+ return FALSE
+ return ..()
+
// Generate a random valid area on the station that the dropoff will happen.
/datum/objective/contract/proc/generate_dropoff()
var/found = FALSE
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 58bf0c75b1f..da807f52764 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -50,7 +50,7 @@
/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
. = ..()
- if(!user.can_perform_action(src, NEED_DEXTERITY|ALLOW_SILICON_REACH)) //prevents monkeys from using camera consoles
+ if(!user.client) //prevents errors by trying to pass clients that don't exist.
return
// Update UI
ui = SStgui.try_update_ui(user, src, ui)
diff --git a/code/game/machinery/computer/records/medical.dm b/code/game/machinery/computer/records/medical.dm
index 13b2877dd9b..259c78d976a 100644
--- a/code/game/machinery/computer/records/medical.dm
+++ b/code/game/machinery/computer/records/medical.dm
@@ -31,7 +31,7 @@
return
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
- create_character_preview_view(user)
+ character_preview_view = create_character_preview_view(user)
ui = new(user, src, "MedicalRecords")
ui.set_autoupdate(FALSE)
ui.open()
diff --git a/code/game/machinery/computer/records/records.dm b/code/game/machinery/computer/records/records.dm
index 4467112b219..cba8d47c7d0 100644
--- a/code/game/machinery/computer/records/records.dm
+++ b/code/game/machinery/computer/records/records.dm
@@ -13,11 +13,17 @@
if(!has_access)
return data
- data["assigned_view"] = "preview_[user.ckey]_[REF(src)]_records"
+ data["assigned_view"] = USER_PREVIEW_ASSIGNED_VIEW(user.ckey)
data["station_z"] = !!(z && is_station_level(z))
return data
+/obj/machinery/computer/records/ui_close(mob/user)
+ . = ..()
+ user.client?.screen_maps -= USER_PREVIEW_ASSIGNED_VIEW(user.ckey)
+ if((LAZYLEN(open_uis) <= 1) && character_preview_view) //only delete the preview if we're the last one to close the console.
+ QDEL_NULL(character_preview_view)
+
/obj/machinery/computer/records/ui_act(action, list/params, datum/tgui/ui)
. = ..()
if(.)
@@ -107,13 +113,14 @@
/// Creates a character preview view for the UI.
/obj/machinery/computer/records/proc/create_character_preview_view(mob/user)
- var/assigned_view = "preview_[user.ckey]_[REF(src)]_records"
+ var/assigned_view = USER_PREVIEW_ASSIGNED_VIEW(user.ckey)
if(user.client?.screen_maps[assigned_view])
return
var/atom/movable/screen/map_view/char_preview/new_view = new(null, src)
new_view.generate_view(assigned_view)
new_view.display_to(user)
+ return new_view
/// Takes a record and updates the character preview view to match it.
/obj/machinery/computer/records/proc/update_preview(mob/user, assigned_view, datum/record/crew/target)
diff --git a/code/game/machinery/gulag_teleporter.dm b/code/game/machinery/gulag_teleporter.dm
index 2fffb98d56c..1a9194fc927 100644
--- a/code/game/machinery/gulag_teleporter.dm
+++ b/code/game/machinery/gulag_teleporter.dm
@@ -135,27 +135,30 @@ The console is located at computer/gulag_teleporter.dm
if(!locked)
open_machine()
-// strips and stores all occupant's items
-/obj/machinery/gulag_teleporter/proc/strip_occupant()
- if(linked_reclaimer)
- linked_reclaimer.stored_items[occupant] = list()
- var/mob/living/mob_occupant = occupant
- for(var/obj/item/W in mob_occupant)
- if(!is_type_in_typecache(W, telegulag_required_items))
- if(mob_occupant.temporarilyRemoveItemFromInventory(W))
- if(istype(W, /obj/item/restraints/handcuffs))
- W.forceMove(get_turf(src))
- continue
- if(linked_reclaimer)
- linked_reclaimer.stored_items[mob_occupant] += W
- W.forceMove(linked_reclaimer)
- else
- W.forceMove(src)
+/// Strips the occupant of any items that are not allowed to be teleported to the gulag.
+/// Will either place those items in the linked_reclaimer or on the ground if the reclaimer is deleted.
+/obj/machinery/gulag_teleporter/proc/strip_prisoner(mob/living/carbon/human/victim)
+ if(!QDELETED(linked_reclaimer))
+ linked_reclaimer.stored_items[victim] = list()
+
+ for(var/obj/item/thing in victim)
+ if(is_type_in_typecache(thing, telegulag_required_items))
+ continue
+
+ if(!victim.temporarilyRemoveItemFromInventory(thing))
+ continue
+
+ if(QDELETED(linked_reclaimer) || istype(thing, /obj/item/restraints/handcuffs))
+ thing.forceMove(get_turf(src))
+ continue
+
+ linked_reclaimer.stored_items[victim] += thing
+ thing.forceMove(linked_reclaimer)
/obj/machinery/gulag_teleporter/proc/handle_prisoner(obj/item/id, datum/record/crew/target)
if(!ishuman(occupant))
return
- strip_occupant()
+ strip_prisoner(occupant)
var/mob/living/carbon/human/prisoner = occupant
if(!isplasmaman(prisoner) && jumpsuit_type)
var/suit_or_skirt = prisoner.jumpsuit_style == PREF_SKIRT ? jumpskirt_type : jumpsuit_type //Check player prefs for jumpsuit or jumpskirt toggle, then give appropriate prison outfit.
diff --git a/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm b/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm
index 5ce9fe02bd4..8d1eea88213 100644
--- a/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm
+++ b/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm
@@ -154,7 +154,7 @@
blame_msg = " with no known fingerprints"
else
source_msg = "with no known source"
-
- if(!istype(holder, /obj/machinery/plumbing)) //excludes standard plumbing equipment from spamming admins with this shit
+ var/area/fluid_area = get_area(location)
+ if(!istype(holder, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) //excludes standard plumbing equipment as well as deathmatch from spamming admins with this shit
message_admins("\A [flood] flood started at [ADMIN_VERBOSEJMP(location)] [source_msg][blame_msg].")
log_game("\A [flood] flood started at [location || "nonexistant location"] [holder ? "from [holder] last touched by [holder || "N/A"]" : "with no known source"].")
diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm
index 2d524e19042..fe35819caaf 100644
--- a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm
+++ b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm
@@ -432,16 +432,17 @@
var/where = "[AREACOORD(location)]"
var/contained = length(contained_reagents) ? "[contained_reagents.Join(", ", " \[", "\]")] @ [chemholder.chem_temp]K" : null
+ var/area/fluid_area = get_area(location)
if(carry.my_atom?.fingerprintslast) //Some reagents don't have a my_atom in some cases
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
var/more = ""
if(M)
more = "[ADMIN_LOOKUPFLW(M)] "
- if(!istype(carry.my_atom, /obj/machinery/plumbing))
+ if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) // I like to be able to see my logs thank you
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].")
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].")
else
- if(!istype(carry.my_atom, /obj/machinery/plumbing))
+ if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) // Deathmatch has way too much smoke to log
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.")
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
diff --git a/code/game/objects/effects/posters/poster.dm b/code/game/objects/effects/posters/poster.dm
index 75a3a26ce26..c4703d700c4 100644
--- a/code/game/objects/effects/posters/poster.dm
+++ b/code/game/objects/effects/posters/poster.dm
@@ -180,7 +180,7 @@
qdel(src)
else
to_chat(user, span_notice("You carefully remove the poster from the wall."))
- roll_and_drop(Adjacent(user) ? get_turf(user) : loc)
+ roll_and_drop(Adjacent(user) ? get_turf(user) : loc, user)
/obj/structure/sign/poster/attack_hand(mob/user, list/modifiers)
. = ..()
@@ -207,11 +207,12 @@
return FALSE
return !user.gloves || !(user.gloves.body_parts_covered & HANDS) || HAS_TRAIT(user, TRAIT_FINGERPRINT_PASSTHROUGH) || HAS_TRAIT(user.gloves, TRAIT_FINGERPRINT_PASSTHROUGH)
-/obj/structure/sign/poster/proc/roll_and_drop(atom/location)
+/obj/structure/sign/poster/proc/roll_and_drop(atom/location, mob/user)
pixel_x = 0
pixel_y = 0
var/obj/item/poster/rolled_poster = new poster_item_type(location, src) // /obj/structure/sign/poster/wanted/roll_and_drop() has some snowflake handling due to icon memes, if you make a major change to this, don't forget to update it too. <3
- forceMove(rolled_poster)
+ if(!user?.put_in_hands(rolled_poster))
+ forceMove(rolled_poster)
return rolled_poster
//separated to reduce code duplication. Moved here for ease of reference and to unclutter r_wall/attackby()
@@ -246,7 +247,7 @@
var/turf/user_drop_location = get_turf(user) //cache this so it just falls to the ground if they move. also no tk memes allowed.
if(!do_after(user, PLACE_SPEED, placed_poster, extra_checks = CALLBACK(placed_poster, TYPE_PROC_REF(/obj/structure/sign/poster, snowflake_closed_turf_check), src)))
- placed_poster.roll_and_drop(user_drop_location)
+ placed_poster.roll_and_drop(user_drop_location, user)
return
placed_poster.on_placed_poster(user)
diff --git a/code/game/objects/effects/wanted_poster.dm b/code/game/objects/effects/wanted_poster.dm
index 6859a185d6e..6cb36838fbc 100644
--- a/code/game/objects/effects/wanted_poster.dm
+++ b/code/game/objects/effects/wanted_poster.dm
@@ -93,9 +93,10 @@
poster_icon.Blend(letter_icon, ICON_OVERLAY)
startX = startX + 4
-/obj/structure/sign/poster/wanted/roll_and_drop(atom/location)
+/obj/structure/sign/poster/wanted/roll_and_drop(atom/location, mob/user)
pixel_x = 0
pixel_y = 0
var/obj/item/poster/rolled_poster = new poster_item_type(location, original_icon, wanted_name, desc, posterHeaderText, posterHeaderColor)
- forceMove(rolled_poster)
+ if(!user?.put_in_hands(rolled_poster))
+ forceMove(rolled_poster)
return rolled_poster
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index 1c8e5906a61..cea9daf9a16 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -349,18 +349,18 @@ CIGARETTE PACKETS ARE IN FANCY.DM
if(!istype(smoker))
// If not, check if it's a gas mask
if(!istype(smoker, /obj/item/clothing/mask/gas))
- reagents.remove_any(to_smoke)
+ reagents.remove_all(to_smoke)
return
smoker = smoker.loc
// If it is, check if that mask is on a carbon mob
if(!istype(smoker) || smoker.get_item_by_slot(ITEM_SLOT_MASK) != loc)
- reagents.remove_any(to_smoke)
+ reagents.remove_all(to_smoke)
return
else
if(src != smoker.wear_mask)
- reagents.remove_any(to_smoke)
+ reagents.remove_all(to_smoke)
return
reagents.expose(smoker, INGEST, min(to_smoke / reagents.total_volume, 1))
@@ -369,7 +369,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
var/smoker_resistance = HAS_TRAIT(smoker, TRAIT_SMOKER) ? 0.5 : 1
smoker.adjustOrganLoss(ORGAN_SLOT_LUNGS, lung_harm * smoker_resistance)
if(!reagents.trans_to(smoker, to_smoke, methods = INGEST, ignore_stomach = TRUE))
- reagents.remove_any(to_smoke)
+ reagents.remove_all(to_smoke)
/obj/item/clothing/mask/cigarette/process(seconds_per_tick)
var/mob/living/user = isliving(loc) ? loc : null
@@ -1172,7 +1172,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
var/mob/living/carbon/vaper = loc
if(!iscarbon(vaper) || src != vaper.wear_mask)
- reagents.remove_any(REAGENTS_METABOLISM)
+ reagents.remove_all(REAGENTS_METABOLISM)
return
if(reagents.get_reagent_amount(/datum/reagent/fuel))
@@ -1187,7 +1187,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
qdel(src)
if(!reagents.trans_to(vaper, REAGENTS_METABOLISM, methods = INGEST, ignore_stomach = TRUE))
- reagents.remove_any(REAGENTS_METABOLISM)
+ reagents.remove_all(REAGENTS_METABOLISM)
/obj/item/clothing/mask/vape/process(seconds_per_tick)
var/mob/living/M = loc
diff --git a/code/game/objects/items/devices/scanners/sequence_scanner.dm b/code/game/objects/items/devices/scanners/sequence_scanner.dm
index 4a80179f60e..3d212cbb771 100644
--- a/code/game/objects/items/devices/scanners/sequence_scanner.dm
+++ b/code/game/objects/items/devices/scanners/sequence_scanner.dm
@@ -55,7 +55,7 @@
//no scanning if its a husk, DNA-less Species or DNA that isn't able to be copied by a changeling/disease
if (!HAS_TRAIT(interacting_with, TRAIT_GENELESS) && !HAS_TRAIT(interacting_with, TRAIT_BADDNA) && !HAS_TRAIT(interacting_with, TRAIT_NO_DNA_COPY))
user.visible_message(span_warning("[user] is scanning [interacting_with]'s genetic makeup."))
- if(!do_after(user, 3 SECONDS))
+ if(!do_after(user, 3 SECONDS, interacting_with))
balloon_alert(user, "scan failed!")
user.visible_message(span_warning("[user] fails to scan [interacting_with]'s genetic makeup."))
return ITEM_INTERACT_BLOCKING
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index 5777985083a..01ff7eb5f08 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -71,6 +71,66 @@
result = roll(sides)
update_appearance()
+/obj/item/dice/attack_self(mob/user)
+ diceroll(user, in_hand = TRUE)
+
+/obj/item/dice/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ var/mob/thrown_by = thrownby?.resolve()
+ if(thrown_by)
+ diceroll(thrown_by)
+ return ..()
+
+/obj/item/dice/proc/diceroll(mob/user, in_hand=FALSE)
+ result = roll(sides)
+ if(rigged != DICE_NOT_RIGGED && result != rigged_value)
+ if(rigged == DICE_BASICALLY_RIGGED && prob(clamp(1/(sides - 1) * 100, 25, 80)))
+ result = rigged_value
+ else if(rigged == DICE_TOTALLY_RIGGED)
+ result = rigged_value
+
+ . = result
+
+ var/fake_result = roll(sides)//Daredevil isn't as good as he used to be
+ var/comment = ""
+ if(sides > MIN_SIDES_ALERT && result == 1) // less comment spam
+ comment = "Ouch, bad luck."
+ if(sides == 20 && result == 20)
+ comment = "NAT 20!"
+ update_appearance()
+ result = manipulate_result(result)
+ if(special_faces.len == sides)
+ comment = "" // its not a number
+ result = special_faces[result]
+ if(!ISINTEGER(result))
+ comment = special_faces[result] // should be a str now
+
+ if(in_hand) //Dice was rolled in someone's hand
+ user.visible_message(
+ span_notice("[user] rolls [src]. It lands on [result]. [comment]"),
+ span_notice("You roll [src]. It lands on [result]. [comment]"),
+ span_hear("You hear [src] rolling, it sounds like a [fake_result]."),
+ )
+ else
+ visible_message(span_notice("[src] rolls to a stop, landing on [result]. [comment]"))
+
+ return .
+
+
+/obj/item/dice/update_overlays()
+ . = ..()
+ . += "[icon_state]-[result]"
+
+/obj/item/dice/microwave_act(obj/machinery/microwave/microwave_source, mob/microwaver, randomize_pixel_offset)
+ if(microwave_riggable)
+ rigged = DICE_BASICALLY_RIGGED
+ rigged_value = result
+
+ return ..() | COMPONENT_MICROWAVE_SUCCESS
+
+/// A proc to modify the displayed result. (Does not affect what the icon_state is passed.)
+/obj/item/dice/proc/manipulate_result(original)
+ return original
+
/obj/item/dice/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is gambling with death! It looks like [user.p_theyre()] trying to commit suicide!"))
return OXYLOSS
@@ -214,61 +274,6 @@
AddElement(/datum/element/update_icon_blocker)
return ..()
-/obj/item/dice/attack_self(mob/user)
- diceroll(user)
-
-/obj/item/dice/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- var/mob/thrown_by = thrownby?.resolve()
- if(thrown_by)
- diceroll(thrown_by)
- return ..()
-
-/obj/item/dice/proc/diceroll(mob/user)
- result = roll(sides)
- if(rigged != DICE_NOT_RIGGED && result != rigged_value)
- if(rigged == DICE_BASICALLY_RIGGED && prob(clamp(1/(sides - 1) * 100, 25, 80)))
- result = rigged_value
- else if(rigged == DICE_TOTALLY_RIGGED)
- result = rigged_value
-
- . = result
-
- var/fake_result = roll(sides)//Daredevil isn't as good as he used to be
- var/comment = ""
- if(sides > MIN_SIDES_ALERT && result == 1) // less comment spam
- comment = "Ouch, bad luck."
- if(sides == 20 && result == 20)
- comment = "NAT 20!" // maint wanted this hardcoded to nat20 don't blame me
- update_appearance()
- result = manipulate_result(result)
- if(special_faces.len == sides)
- comment = "" // its not a number
- result = special_faces[result]
- if(!ISINTEGER(result))
- comment = special_faces[result] // should be a str now
-
- if(user != null) //Dice was rolled in someone's hand
- user.visible_message(span_notice("[user] throws [src]. It lands on [result]. [comment]"), \
- span_notice("You throw [src]. It lands on [result]. [comment]"), \
- span_hear("You hear [src] rolling, it sounds like a [fake_result]."))
- else if(!src.throwing) //Dice was thrown and is coming to rest
- visible_message(span_notice("[src] rolls to a stop, landing on [result]. [comment]"))
-
-/obj/item/dice/update_overlays()
- . = ..()
- . += "[icon_state]-[result]"
-
-/obj/item/dice/microwave_act(obj/machinery/microwave/microwave_source, mob/microwaver, randomize_pixel_offset)
- if(microwave_riggable)
- rigged = DICE_BASICALLY_RIGGED
- rigged_value = result
-
- return ..() | COMPONENT_MICROWAVE_SUCCESS
-
-/// A proc to modify the displayed result. (Does not affect what the icon_state is passed.)
-/obj/item/dice/proc/manipulate_result(original)
- return original
-
// Die of fate stuff
/obj/item/dice/d20/fate
name = "\improper Die of Fate"
@@ -309,7 +314,7 @@
/obj/item/dice/d20/fate/stealth/cursed/one_use
reusable = FALSE
-/obj/item/dice/d20/fate/diceroll(mob/user)
+/obj/item/dice/d20/fate/diceroll(mob/user, in_hand=FALSE)
if(!COOLDOWN_FINISHED(src, roll_cd))
to_chat(user, span_warning("Hold on, [src] isn't caught up with your last roll!"))
return
diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm
index 7d5bc0b9528..2d32485f31f 100644
--- a/code/game/objects/items/dualsaber.dm
+++ b/code/game/objects/items/dualsaber.dm
@@ -23,7 +23,7 @@
light_on = FALSE
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
- block_chance = 45 //NOVA EDIT - Lowered ORIGINAL:75
+ block_chance = 75
block_sound = 'sound/weapons/block_blade.ogg'
max_integrity = 200
armor_type = /datum/armor/item_dualsaber
diff --git a/code/game/objects/items/food/frozen.dm b/code/game/objects/items/food/frozen.dm
index 0bd0cd3a606..27052507bfd 100644
--- a/code/game/objects/items/food/frozen.dm
+++ b/code/game/objects/items/food/frozen.dm
@@ -12,6 +12,7 @@
foodtypes = GRAIN | DAIRY | SUGAR
food_flags = FOOD_FINGER_FOOD
crafting_complexity = FOOD_COMPLEXITY_2
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/strawberryicecreamsandwich
name = "strawberry ice cream sandwich"
@@ -27,7 +28,7 @@
foodtypes = FRUIT | DAIRY | SUGAR
food_flags = FOOD_FINGER_FOOD
crafting_complexity = FOOD_COMPLEXITY_3
-
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/spacefreezy
name = "space freezy"
@@ -43,6 +44,7 @@
tastes = list("blue cherries" = 2, "ice cream" = 2)
foodtypes = FRUIT | DAIRY | SUGAR
crafting_complexity = FOOD_COMPLEXITY_3
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/spacefreezy/make_edible()
. = ..()
@@ -62,6 +64,7 @@
tastes = list("ice cream" = 1, "banana" = 1)
foodtypes = FRUIT | DAIRY | SUGAR
crafting_complexity = FOOD_COMPLEXITY_3
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/sundae/make_edible()
. = ..()
@@ -81,6 +84,7 @@
tastes = list("ice cream" = 1, "banana" = 1, "a bad joke" = 1)
foodtypes = FRUIT | DAIRY | SUGAR
crafting_complexity = FOOD_COMPLEXITY_4
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/honkdae/make_edible()
. = ..()
@@ -104,6 +108,7 @@
foodtypes = SUGAR //We use SUGAR as a base line to act in as junkfood, other wise we use fruit
food_flags = FOOD_FINGER_FOOD
crafting_complexity = FOOD_COMPLEXITY_2
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/snowcones/lime
name = "lime snowcone"
@@ -330,6 +335,7 @@
foodtypes = DAIRY | SUGAR
food_flags = FOOD_FINGER_FOOD
crafting_complexity = FOOD_COMPLEXITY_3
+ crafted_food_buff = /datum/status_effect/food/chilling
var/overlay_state = "creamsicle_o" //This is the edible part of the popsicle.
var/bite_states = 4 //This value value is used for correctly setting the bite_consumption to ensure every bite changes the sprite. Do not set to zero.
@@ -434,6 +440,7 @@
foodtypes = DAIRY | SUGAR
venue_value = FOOD_PRICE_NORMAL
crafting_complexity = FOOD_COMPLEXITY_3
+ crafted_food_buff = /datum/status_effect/food/chilling
/obj/item/food/popsicle/meatsicle
name = "Meatsicle"
diff --git a/code/game/objects/items/food/pastries.dm b/code/game/objects/items/food/pastries.dm
index 277e6efaff8..e1449007b71 100644
--- a/code/game/objects/items/food/pastries.dm
+++ b/code/game/objects/items/food/pastries.dm
@@ -77,6 +77,10 @@
w_class = WEIGHT_CLASS_SMALL
crafting_complexity = FOOD_COMPLEXITY_2
+/obj/item/food/waffles/make_edible()
+ . = ..()
+ AddComponent(/datum/component/ice_cream_holder, max_scoops = 1, x_offset = -2)
+
/obj/item/food/soylentgreen
name = "\improper Soylent Green"
desc = "Not made of people. Honest." //Totally people.
@@ -123,6 +127,10 @@
w_class = WEIGHT_CLASS_SMALL
crafting_complexity = FOOD_COMPLEXITY_3
+/obj/item/food/rofflewaffles/make_edible()
+ . = ..()
+ AddComponent(/datum/component/ice_cream_holder, max_scoops = 1, x_offset = -2)
+
////////////////////////////////////////////OTHER////////////////////////////////////////////
/obj/item/food/cookie
@@ -356,7 +364,7 @@
bite_consumption = 4
foodtypes = DAIRY | SUGAR
food_flags = FOOD_FINGER_FOOD
- crafting_complexity = FOOD_COMPLEXITY_3
+ crafting_complexity = FOOD_COMPLEXITY_2
max_volume = 10 //The max volumes scales up with the number of scoops of ice cream served.
/// These two variables are used by the ice cream vat. Latter is the one that shows on the UI.
var/list/ingredients = list(
@@ -371,14 +379,10 @@
*/
var/list/prefill_flavours
-/obj/item/food/icecream/New(loc, list/prefill_flavours)
+/obj/item/food/icecream/Initialize(mapload, list/prefill_flavours)
if(ingredients)
ingredients_text = "Requires: [reagent_paths_list_to_text(ingredients)]"
- return ..()
-
-/obj/item/food/icecream/Initialize(mapload, list/prefill_flavours)
- if(prefill_flavours)
- src.prefill_flavours = prefill_flavours
+ src.prefill_flavours = prefill_flavours
return ..()
/obj/item/food/icecream/make_edible()
@@ -398,7 +402,6 @@
/datum/reagent/consumable/sugar,
/datum/reagent/consumable/coco,
)
- crafting_complexity = FOOD_COMPLEXITY_3
/obj/item/food/icecream/korta
name = "korta cone"
diff --git a/code/game/objects/items/grenades/_grenade.dm b/code/game/objects/items/grenades/_grenade.dm
index bb1cade96b8..e7e592eabac 100644
--- a/code/game/objects/items/grenades/_grenade.dm
+++ b/code/game/objects/items/grenades/_grenade.dm
@@ -22,6 +22,8 @@
var/dud_flags = NONE
///Is this grenade currently armed?
var/active = FALSE
+ ///Is it a cluster grenade? We dont wanna spam admin logs with these.
+ var/type_cluster = FALSE
///How long it takes for a grenade to explode after being armed
var/det_time = 5 SECONDS
///Will this state what it's det_time is when examined?
@@ -135,7 +137,8 @@
arm_grenade(user)
/obj/item/grenade/proc/log_grenade(mob/user)
- log_bomber(user, "has primed a", src, "for detonation", message_admins = !dud_flags)
+ if(!type_cluster)
+ log_bomber(user, "has primed a", src, "for detonation", message_admins = dud_flags != NONE)
/**
* arm_grenade (formerly preprime) refers to when a grenade with a standard time fuze is activated, making it go beepbeepbeep and then detonate a few seconds later.
diff --git a/code/game/objects/items/grenades/clusterbuster.dm b/code/game/objects/items/grenades/clusterbuster.dm
index e4862919c1c..b27285e0786 100644
--- a/code/game/objects/items/grenades/clusterbuster.dm
+++ b/code/game/objects/items/grenades/clusterbuster.dm
@@ -95,6 +95,7 @@
var/obj/item/grenade/grenade = new type(loc)
if(istype(grenade))
grenade.active = TRUE
+ grenade.type_cluster = TRUE
addtimer(CALLBACK(grenade, TYPE_PROC_REF(/obj/item/grenade, detonate)), rand(RANDOM_DETONATE_MIN_TIME, RANDOM_DETONATE_MAX_TIME))
var/steps = rand(1, 4)
for(var/step in 1 to steps)
diff --git a/code/game/objects/items/implants/implant_explosive.dm b/code/game/objects/items/implants/implant_explosive.dm
index 25966a24c68..51564799f6e 100644
--- a/code/game/objects/items/implants/implant_explosive.dm
+++ b/code/game/objects/items/implants/implant_explosive.dm
@@ -19,6 +19,8 @@
var/active = FALSE
///The final countdown (delay before we explode)
var/delay = MICROBOMB_DELAY
+ ///If the delay is equal or lower to MICROBOMB_DELAY (0.7 sec), the explosion will be instantaneous.
+ var/instant_explosion = TRUE
///Radius of weak devastation explosive impact
var/explosion_light = MICROBOMB_EXPLOSION_LIGHT
///Radius of medium devastation explosive impact
@@ -33,7 +35,8 @@
var/no_paralyze = FALSE
///Do we override other explosive implants?
var/master_implant = FALSE
-
+ ///Will this implant notify ghosts when activated?
+ var/notify_ghosts = TRUE
/obj/item/implant/explosive/proc/on_death(datum/source, gibbed)
SIGNAL_HANDLER
@@ -73,7 +76,7 @@
var/turf/boomturf = get_turf(imp_in)
message_admins("[ADMIN_LOOKUPFLW(imp_in)] has activated their [name] at [ADMIN_VERBOSEJMP(boomturf)], with cause of [cause].")
//If the delay is shorter or equal to the default delay, just blow up already jeez
- if(delay <= MICROBOMB_DELAY)
+ if(delay <= MICROBOMB_DELAY && instant_explosion)
explode()
return
timed_explosion()
@@ -119,14 +122,15 @@
/obj/item/implant/explosive/proc/timed_explosion()
imp_in.visible_message(span_warning("[imp_in] starts beeping ominously!"))
- notify_ghosts(
- "[imp_in] is about to detonate their explosive implant!",
- source = src,
- header = "Tick Tick Tick...",
- notify_flags = NOTIFY_CATEGORY_NOFLASH,
- ghost_sound = 'sound/machines/warning-buzzer.ogg',
- notify_volume = 75,
- )
+ if(notify_ghosts)
+ notify_ghosts(
+ "[imp_in] is about to detonate their explosive implant!",
+ source = src,
+ header = "Tick Tick Tick...",
+ notify_flags = NOTIFY_CATEGORY_NOFLASH,
+ ghost_sound = 'sound/machines/warning-buzzer.ogg',
+ notify_volume = 75,
+ )
playsound(loc, 'sound/items/timer.ogg', 30, FALSE)
if(!panic_beep_sound)
@@ -203,6 +207,13 @@
if(source.health < source.crit_threshold)
INVOKE_ASYNC(src, PROC_REF(activate), "deniability")
+/obj/item/implant/explosive/deathmatch
+ name = "deathmatch microbomb implant"
+ delay = 0.5 SECONDS
+ actions_types = null
+ instant_explosion = FALSE
+ notify_ghosts = FALSE
+
/obj/item/implanter/explosive
name = "implanter (microbomb)"
imp_type = /obj/item/implant/explosive
diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm
index 9528894232d..907807397d7 100644
--- a/code/game/objects/items/mop.dm
+++ b/code/game/objects/items/mop.dm
@@ -67,7 +67,7 @@
var/val2remove = 1
if(cleaner?.mind)
val2remove = round(cleaner.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER), 0.1)
- reagents.remove_any(val2remove) //reaction() doesn't use up the reagents
+ reagents.remove_all(val2remove) //reaction() doesn't use up the reagents
/obj/item/mop/cyborg/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/items/piggy_bank.dm b/code/game/objects/items/piggy_bank.dm
index 012ff91b021..8d50e8a39e8 100644
--- a/code/game/objects/items/piggy_bank.dm
+++ b/code/game/objects/items/piggy_bank.dm
@@ -4,7 +4,7 @@
*/
/obj/item/piggy_bank
name = "piggy bank"
- desc = "A pig-shaped money container made of porkelain, oink. Do not throw." //pun very intended.
+ desc = "A pig-shaped money container made of porkelain, oink. Do not throw." //pun very intended.
icon = 'icons/obj/fluff/general.dmi'
icon_state = "piggy_bank"
max_integrity = 8
@@ -20,6 +20,8 @@
var/datum/callback/persistence_cb
///How much dosh can this piggy bank hold.
var/maximum_value = PAYCHECK_COMMAND * 20
+ ///A limit to much dosh can you put inside this piggy bank each round. If 0, there's no limit. Only applies to persistent piggies.
+ var/maximum_savings_per_shift = 0
///How much dosh this piggy bank spawns with.
var/initial_value = 0
@@ -49,6 +51,9 @@
if(initial_value && initial_value + calculate_dosh_amount() <= maximum_value)
new /obj/item/holochip(src, initial_value)
+ if(maximum_savings_per_shift)
+ maximum_value = calculate_dosh_amount() + maximum_savings_per_shift
+
/obj/item/piggy_bank/proc/save_cash()
SSpersistence.save_piggy_bank(src)
@@ -127,3 +132,17 @@
/obj/item/piggy_bank/museum/Initialize(mapload)
. = ..()
AddComponent(/datum/component/areabound) //do not steal.
+
+/obj/item/piggy_bank/vault
+ name = "vault piggy bank"
+ desc = "A pig-shaped money container made of porkelain, containing the station's emergency funds carried between shifts, oink. Do not throw."
+ persistence_id = "vault_piggy"
+ greyscale_colors = COLOR_LIGHT_ORANGE
+ maximum_value = PAYCHECK_COMMAND * 33
+ initial_value = PAYCHECK_CREW //it takes about 66 shifts for it to hit its max value on its own.
+ maximum_savings_per_shift = PAYCHECK_COMMAND * 16 //and 2 if you actively use it.
+
+/obj/item/piggy_bank/vault/Initialize(mapload)
+ . = ..()
+ //one piggy bank should exist, preferibly inside the vault's safe.
+ REGISTER_REQUIRED_MAP_ITEM(1, 1)
diff --git a/code/game/objects/items/robot/items/food.dm b/code/game/objects/items/robot/items/food.dm
index a747f813ace..0ecfefa589d 100644
--- a/code/game/objects/items/robot/items/food.dm
+++ b/code/game/objects/items/robot/items/food.dm
@@ -60,10 +60,7 @@
if(DISPENSE_LOLLIPOP_MODE)
food_item = new /obj/item/food/lollipop/cyborg(turf_to_dispense_to)
if(DISPENSE_ICECREAM_MODE)
- food_item = new /obj/item/food/icecream(
- loc = turf_to_dispense_to,
- prefill_flavours = list(ICE_CREAM_VANILLA),
- )
+ food_item = new /obj/item/food/icecream(turf_to_dispense_to, list(ICE_CREAM_VANILLA))
food_item.desc = "Eat the ice cream."
var/into_hands = FALSE
diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm
index cdc786fc8db..c07bc780c91 100644
--- a/code/game/objects/items/shrapnel.dm
+++ b/code/game/objects/items/shrapnel.dm
@@ -35,6 +35,9 @@
wound_bonus = 30
embedding = list(embed_chance=70, ignore_throwspeed_threshold=TRUE, fall_chance=1)
+/obj/projectile/bullet/shrapnel/short_range
+ range = 5
+
/obj/projectile/bullet/shrapnel/mega
name = "flying shrapnel hunk"
range = 45
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 282ced06519..c3ae6bf70b5 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -359,6 +359,7 @@
. = ..()
atom_storage.max_slots = 5
atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ atom_storage.max_total_storage = 11 // NOVA EDIT - ADDITION
atom_storage.set_holdable(list(
/obj/item/ammo_box,
/obj/item/ammo_casing/shotgun,
@@ -368,7 +369,7 @@
/obj/item/flashlight/seclite,
/obj/item/food/donut,
/obj/item/grenade,
- /obj/item/gun, //NOVA EDIT ADDITION
+ /obj/item/gun, //NOVA EDIT - ADDITION
/obj/item/holosign_creator/security,
/obj/item/knife/combat,
/obj/item/melee/baton,
diff --git a/code/game/objects/items/storage/boxes/clothes_boxes.dm b/code/game/objects/items/storage/boxes/clothes_boxes.dm
index 18a6ec31d87..ce4dfb6c5e5 100644
--- a/code/game/objects/items/storage/boxes/clothes_boxes.dm
+++ b/code/game/objects/items/storage/boxes/clothes_boxes.dm
@@ -211,3 +211,11 @@
new /obj/item/clothing/gloves/combat/floortile(src)
new /obj/item/clothing/shoes/jackboots/floortile(src)
new /obj/item/storage/backpack/floortile(src)
+
+/obj/item/storage/box/collar_bomb
+ name = "collar bomb box"
+ desc = "A small print on the back reads 'For research purposes only. Handle with care. In case of emergency, call the following number:'... the rest is scratched out with a marker..."
+
+/obj/item/storage/box/collar_bomb/PopulateContents()
+ var/obj/item/collar_bomb_button/button = new(src)
+ new /obj/item/clothing/neck/collar_bomb(src, button)
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index 0b2be473535..efb79bb64e1 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -20,6 +20,7 @@
#define KIT_BEES "bee"
#define KIT_MR_FREEZE "mr_freeze"
#define KIT_TRAITOR_2006 "ancient"
+#define KIT_DEAD_MONEY "dead_money"
#define KIT_SAM_FISHER "sam_fisher"
#define KIT_PROP_HUNT "prop_hunt"
@@ -190,6 +191,7 @@
KIT_MAD_SCIENTIST = 2,
KIT_BEES = 1,
KIT_MR_FREEZE = 2,
+ KIT_DEAD_MONEY = 2,
KIT_TRAITOR_2006 = 1,
KIT_SAM_FISHER = 1,
KIT_PROP_HUNT = 1
@@ -282,6 +284,17 @@
if(KIT_TRAITOR_2006) //A kit so old, it's probably older than you. //This bundle is filled with the entire uplink contents traitors had access to in 2006, from OpenSS13. Notably the esword was not a choice but existed in code.
new /obj/item/storage/toolbox/emergency/old/ancientbundle(src) //Items fit neatly into a classic toolbox just to remind you what the theme is.
+ if(KIT_DEAD_MONEY)
+ for(var/i in 1 to 4)
+ new /obj/item/clothing/neck/collar_bomb(src) // These let you remotely kill people with a signaler, though you have to get them first.
+ new /obj/item/storage/box/syndie_kit/signaler(src)
+ new /obj/item/mod/control/pre_equipped/responsory/inquisitory/syndie(src) // basically a snowflake yet better elite modsuit, so like, 8 + 5 tc.
+ new /obj/item/card/id/advanced/chameleon(src) // 2 tc
+ new /obj/item/clothing/mask/chameleon(src)
+ new /obj/item/melee/baton/telescopic/contractor_baton(src) // 7 tc
+ new /obj/item/jammer(src) // 5 tc
+ new /obj/item/pinpointer/crew(src) //priceless
+
if(KIT_SAM_FISHER)
new /obj/item/clothing/under/syndicate/combat(src)
new /obj/item/clothing/suit/armor/vest/marine/pmc(src) //The armor kit is comparable to the infiltrator, 6 TC
@@ -866,5 +879,6 @@
#undef KIT_BEES
#undef KIT_MR_FREEZE
#undef KIT_TRAITOR_2006
+#undef KIT_DEAD_MONEY
#undef KIT_SAM_FISHER
#undef KIT_PROP_HUNT
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index 29dcdbcf702..9950b8e9c93 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -304,7 +304,7 @@
balloon_alert(user, "still recharging!")
return
COOLDOWN_START(src, resin_cooldown, 10 SECONDS)
- R.remove_any(100)
+ R.remove_all(100)
var/obj/effect/resin_container/resin = new (get_turf(src))
user.log_message("used Resin Launcher", LOG_GAME)
playsound(src,'sound/items/syringeproj.ogg',40,TRUE)
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index f7219921765..c2f654f8553 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -791,26 +791,28 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
hitsound = 'sound/effects/snap.ogg'
w_class = WEIGHT_CLASS_SMALL
/// Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc.
- var/list/splattable
+ var/static/list/splattable
/// Things in this list which take a lot more damage from the fly swatter, but not be necessarily killed by it.
- var/list/strong_against
+ var/static/list/strong_against
/// How much extra damage the fly swatter does against mobs it is strong against
var/extra_strength_damage = 24
/obj/item/melee/flyswatter/Initialize(mapload)
. = ..()
- splattable = typecacheof(list(
- /mob/living/basic/ant,
- /mob/living/basic/butterfly,
- /mob/living/basic/cockroach,
- /mob/living/basic/spider/growing/spiderling,
- /mob/living/basic/bee,
- /obj/effect/decal/cleanable/ants,
- /obj/item/queen_bee,
- ))
- strong_against = typecacheof(list(
- /mob/living/basic/spider/giant,
- ))
+ if (isnull(splattable))
+ splattable = typecacheof(list(
+ /mob/living/basic/ant,
+ /mob/living/basic/butterfly,
+ /mob/living/basic/cockroach,
+ /mob/living/basic/spider/growing/spiderling,
+ /mob/living/basic/bee,
+ /obj/effect/decal/cleanable/ants,
+ /obj/item/queen_bee,
+ ))
+ if (isnull(strong_against))
+ strong_against = typecacheof(list(
+ /mob/living/basic/spider,
+ ))
/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag)
diff --git a/code/game/objects/structures/gym/punching_bag.dm b/code/game/objects/structures/gym/punching_bag.dm
index 9c67bd89cfc..f441887f2c1 100644
--- a/code/game/objects/structures/gym/punching_bag.dm
+++ b/code/game/objects/structures/gym/punching_bag.dm
@@ -44,17 +44,22 @@
flick("[icon_state]-punch", src)
playsound(loc, pick(hit_sounds), 25, TRUE, -1)
+ if(!iscarbon(user))
+ return
+
+ var/is_heavy_gravity = user.has_gravity() > STANDARD_GRAVITY
+
var/stamina_exhaustion = 3
if(ishuman(user))
var/mob/living/carbon/human/boxer = user
var/obj/item/clothing/gloves/boxing/boxing_gloves = boxer.get_item_by_slot(ITEM_SLOT_GLOVES)
if(istype(boxing_gloves))
stamina_exhaustion = 2
+ if (is_heavy_gravity)
+ stamina_exhaustion *= 1.5
- if(!iscarbon(user))
- return
user.adjustStaminaLoss(stamina_exhaustion)
- user.mind?.adjust_experience(/datum/skill/fitness, 0.1)
+ user.mind?.adjust_experience(/datum/skill/fitness, is_heavy_gravity ? 0.2 : 0.1)
user.apply_status_effect(/datum/status_effect/exercised)
/obj/structure/punching_bag/wrench_act_secondary(mob/living/user, obj/item/tool)
diff --git a/code/game/objects/structures/gym/weight_machine.dm b/code/game/objects/structures/gym/weight_machine.dm
index 055c9788c95..d3614ee6815 100644
--- a/code/game/objects/structures/gym/weight_machine.dm
+++ b/code/game/objects/structures/gym/weight_machine.dm
@@ -133,8 +133,9 @@
user.adjust_nutrition(-5) // feel the burn
if(iscarbon(user))
+ var/gravity_modifier = user.has_gravity() > STANDARD_GRAVITY ? 2 : 1
// remember the real xp gain is from sleeping after working out
- user.mind.adjust_experience(/datum/skill/fitness, WORKOUT_XP)
+ user.mind.adjust_experience(/datum/skill/fitness, WORKOUT_XP * gravity_modifier)
user.apply_status_effect(/datum/status_effect/exercised, EXERCISE_STATUS_DURATION)
end_workout()
@@ -161,8 +162,13 @@
if(!iscarbon(user) || isnull(user.mind))
return TRUE
+
+ var/affected_gravity = user.has_gravity()
+ if (!affected_gravity)
+ return TRUE // No weight? I could do this all day
+ var/gravity_modifier = affected_gravity > STANDARD_GRAVITY ? 0.75 : 1
// the amount of workouts you can do before you hit stamcrit
- var/workout_reps = total_workout_reps[user.mind.get_skill_level(/datum/skill/fitness)]
+ var/workout_reps = total_workout_reps[user.mind.get_skill_level(/datum/skill/fitness)] * gravity_modifier
// total stamina drain of 1 workout calculated based on the workout length
var/stamina_exhaustion = FLOOR(user.maxHealth / workout_reps / WORKOUT_LENGTH, 0.1)
user.adjustStaminaLoss(stamina_exhaustion * seconds_per_tick)
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 43d11eaaa71..2b3ea7802c5 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -446,14 +446,10 @@ GLOBAL_LIST_EMPTY(crematoriums)
/obj/structure/bodycontainer/crematorium/creamatorium/cremate(mob/user)
var/list/icecreams = list()
for(var/mob/living/i_scream as anything in get_all_contents_type(/mob/living))
- var/obj/item/food/icecream/IC = new /obj/item/food/icecream(
- loc = null,
- prefill_flavours = list(ICE_CREAM_MOB = list(null, i_scream.name))
- )
- icecreams += IC
+ icecreams += new /obj/item/food/icecream(null, list(ICE_CREAM_MOB = list(null, i_scream.name)))
. = ..()
- for(var/obj/IC in icecreams)
- IC.forceMove(src)
+ for(var/obj/ice_cream as anything in icecreams)
+ ice_cream.forceMove(src)
/*
* Generic Tray
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 9c1a8f1c4f3..bcc9d138695 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -41,6 +41,7 @@ FLOOR SAFES
/obj/structure/safe/Initialize(mapload)
. = ..()
+ update_appearance(UPDATE_ICON)
// Combination generation
for(var/iterating in 1 to number_of_tumblers)
tumblers.Add(rand(0, 99))
@@ -57,7 +58,8 @@ FLOOR SAFES
inserting_item.forceMove(src)
/obj/structure/safe/update_icon_state()
- icon_state = "[initial(icon_state)][open ? "-open" : null]"
+ //uses the same icon as the captain's spare safe (therefore lockable storage) so keep it in line with that
+ icon_state = "[initial(icon_state)][open ? null : "_locked"]"
return ..()
/obj/structure/safe/attackby(obj/item/attacking_item, mob/user, params)
@@ -247,5 +249,13 @@ FLOOR SAFES
. = ..()
AddElement(/datum/element/undertile)
+///Special safe for the station's vault. Not explicitly required, but the piggy bank inside it is.
+/obj/structure/safe/vault
+
+/obj/structure/safe/vault/Initialize(mapload)
+ . = ..()
+ var/obj/item/piggy_bank/vault/piggy = new(src)
+ space += piggy.w_class
+
#undef SOUND_CHANCE
#undef BROKEN_THRESHOLD
diff --git a/code/game/objects/structures/secure_safe.dm b/code/game/objects/structures/secure_safe.dm
index 43027090e5b..c4d5d29f8eb 100644
--- a/code/game/objects/structures/secure_safe.dm
+++ b/code/game/objects/structures/secure_safe.dm
@@ -1,3 +1,25 @@
+/obj/item/wallframe/secure_safe
+ name = "secure safe frame"
+ desc = "A locked safe. It being unpowered prevents any access until placed back onto a wall."
+ icon = 'icons/obj/storage/storage.dmi'
+ icon_state = "wall_safe"
+ base_icon_state = "wall_safe"
+ result_path = /obj/structure/secure_safe
+ pixel_shift = 32
+
+/obj/item/wallframe/secure_safe/Initialize(mapload)
+ . = ..()
+ create_storage(
+ max_specific_storage = WEIGHT_CLASS_GIGANTIC,
+ max_total_storage = 20,
+ )
+ atom_storage.locked = STORAGE_FULLY_LOCKED
+
+/obj/item/wallframe/secure_safe/after_attach(obj/attached_to)
+ . = ..()
+ for(var/obj/item in contents)
+ item.forceMove(attached_to)
+
/**
* Wall safes
* Holds items and uses the lockable storage component
@@ -18,8 +40,17 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
. = ..()
//this will create the storage for us.
AddComponent(/datum/component/lockable_storage)
- find_and_hang_on_wall()
- PopulateContents()
+ if(!density)
+ find_and_hang_on_wall()
+ if(mapload)
+ PopulateContents()
+
+/obj/structure/secure_safe/deconstruct(disassembled)
+ if(!density) //if we're a wall item, we'll drop a wall frame.
+ var/obj/item/wallframe/secure_safe/new_safe = new(get_turf(src))
+ for(var/obj/item in contents)
+ item.forceMove(new_safe)
+ return ..()
/obj/structure/secure_safe/proc/PopulateContents()
new /obj/item/paper(src)
@@ -42,12 +73,16 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
desc = "In case of emergency, do not break glass. All Captains and Acting Captains are provided with codes to access this safe. \
It is made out of the same material as the station's Black Box and is designed to resist all conventional weaponry. \
There appears to be a small amount of surface corrosion. It doesn't look like it could withstand much of an explosion.\
- It remains quite flush against the wall, and there only seems to be enough room to fit something as slim as an ID card."
+ Due to the expensive material, it was made incredibly small to cut corners, leaving only enough room to fit something as slim as an ID card."
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "safe"
+ base_icon_state = "safe"
armor_type = /datum/armor/safe_caps_spare
max_integrity = 300
- color = "#ffdd33"
-
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe/caps_spare, 32)
+ density = TRUE
+ anchored_tabletop_offset = 4
+ custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT)
+ material_flags = MATERIAL_EFFECTS | MATERIAL_COLOR
/datum/armor/safe_caps_spare
melee = 100
@@ -60,6 +95,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe/caps_spare, 32)
/obj/structure/secure_safe/caps_spare/Initialize(mapload)
. = ..()
+ var/matrix/small_safe_transformation = new
+ small_safe_transformation.Scale(0.6)
+ transform = small_safe_transformation
atom_storage.set_holdable(/obj/item/card/id)
AddComponent(/datum/component/lockable_storage, \
lock_code = SSid_access.spare_id_safe_code, \
diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm
index ab41615fd96..55bdf262c0b 100644
--- a/code/game/objects/structures/shower.dm
+++ b/code/game/objects/structures/shower.dm
@@ -123,7 +123,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
//NOVA EDIT ADDITION
/obj/machinery/shower/plunger_act(obj/item/plunger/P, mob/living/user, reinforced)
if(do_after(user, 3 SECONDS, src))
- reagents.remove_any(reagents.total_volume)
+ reagents.remove_all(reagents.total_volume)
balloon_alert(user, "reservoir emptied")
//NOVA EDIT END
@@ -320,7 +320,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
if(!ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash above
wash_atom(movable_content) // Reagent exposure is handled in wash_atom
- reagents.remove_any(SHOWER_SPRAY_VOLUME)
+ reagents.remove_all(SHOWER_SPRAY_VOLUME)
/obj/machinery/shower/on_deconstruction(disassembled = TRUE)
new /obj/item/stack/sheet/iron(drop_location(), 2)
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index edf7f2fc803..eac58d119a8 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -362,7 +362,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14))
return
busy = FALSE
- reagents.remove_any(5)
+ reagents.remove_all(5)
reagents.expose(user, TOUCH, 5 / max(reagents.total_volume, 5))
begin_reclamation()
if(washing_face)
diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm
index 4e79d835271..13bc0a43da6 100644
--- a/code/game/turfs/closed/walls.dm
+++ b/code/game/turfs/closed/walls.dm
@@ -140,7 +140,7 @@
for(var/obj/O in src.contents) //Eject contents!
if(istype(O, /obj/structure/sign/poster))
var/obj/structure/sign/poster/P = O
- P.roll_and_drop(src)
+ INVOKE_ASYNC(P, TYPE_PROC_REF(/obj/structure/sign/poster, roll_and_drop), src)
if(decon_type)
ChangeTurf(decon_type, flags = CHANGETURF_INHERIT_AIR)
else
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index 8f9e7b44aa6..8165b35a256 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -249,7 +249,7 @@
/turf/open/lava/proc/can_burn_stuff(atom/movable/burn_target)
if(QDELETED(burn_target))
return LAVA_BE_IGNORING
- if(burn_target.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) //you're flying over it.
+ if(burn_target.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || !burn_target.has_gravity()) //you're flying over it.
return LAVA_BE_IGNORING
if(isobj(burn_target))
@@ -268,7 +268,7 @@
var/mob/living/burn_living = burn_target
var/atom/movable/burn_buckled = burn_living.buckled
if(burn_buckled)
- if(burn_buckled.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
+ if(burn_buckled.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || !burn_buckled.has_gravity())
return LAVA_BE_PROCESSING
if(isobj(burn_buckled))
var/obj/burn_buckled_obj = burn_buckled
diff --git a/code/game/world.dm b/code/game/world.dm
index cd403b56591..6e047a38b13 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -119,7 +119,7 @@ GLOBAL_VAR(restart_counter)
// From a really fucking old commit (91d7150)
// I wanted to move it but I think this needs to be after /world/New is called but before any sleeps?
// - Dominion/Cyberboss
- GLOB.timezoneOffset = text2num(time2text(0,"hh")) * 36000
+ GLOB.timezoneOffset = world.timezone * 36000
// First possible sleep()
InitTgs()
diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm
index 2938e5f4fd2..3ed57df1dca 100644
--- a/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm
+++ b/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm
@@ -17,6 +17,12 @@
return
return ..()
+/obj/structure/sign/poster/abductor/attackby(obj/item/tool, mob/user, params)
+ if(tool.toolspeed >= 0.2)
+ balloon_alert(user, "tool too weak!")
+ return FALSE
+ return ..()
+
/obj/structure/sign/poster/abductor/random
name = "random abductor poster"
icon_state = "random_abductor"
diff --git a/code/modules/antagonists/heretic/magic/fire_blast.dm b/code/modules/antagonists/heretic/magic/fire_blast.dm
index 1d2d7daaceb..f76a1f18d17 100644
--- a/code/modules/antagonists/heretic/magic/fire_blast.dm
+++ b/code/modules/antagonists/heretic/magic/fire_blast.dm
@@ -31,7 +31,7 @@
/datum/action/cooldown/spell/charged/beam/fire_blast/send_beam(atom/origin, mob/living/carbon/to_beam, bounces = 4)
// Send a beam from the origin to the hit mob
- origin.Beam(to_beam, icon_state = "solar_beam", time = beam_duration, beam_type = /obj/effect/ebeam/fire)
+ origin.Beam(to_beam, icon_state = "solar_beam", time = beam_duration, beam_type = /obj/effect/ebeam/reacting/fire)
// If they block the magic, the chain wont necessarily stop,
// but likely will (due to them not catching on fire)
@@ -141,25 +141,11 @@
owner.adjustStaminaLoss(2 * tick_damage * seconds_between_ticks)
// The beam fireblast spits out, causes people to walk through it to be on fire
-/obj/effect/ebeam/fire
+/obj/effect/ebeam/reacting/fire
name = "fire beam"
-/obj/effect/ebeam/fire/Initialize(mapload)
+/obj/effect/ebeam/reacting/fire/beam_entered(atom/movable/entered)
. = ..()
- var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
- )
- AddElement(/datum/element/connect_loc, loc_connections)
-
- if(!isturf(loc) || mapload) // idk if this would ever be maploaded but you never know
- return
-
- for(var/mob/living/living_mob in loc)
- on_entered(entered = living_mob)
-
-/obj/effect/ebeam/fire/proc/on_entered(datum/source, atom/movable/entered)
- SIGNAL_HANDLER
-
if(!isliving(entered))
return
var/mob/living/living_entered = entered
diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
index 84f3a151dd2..2b323d51162 100644
--- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
+++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
@@ -271,6 +271,10 @@
. = TRUE
if("send")
start_sending()
+ //We ensure that the holding facility is loaded in time in case we're selling mobs.
+ //This isn't the prettiest place to put it, but 'start_sending()' is also used by civilian bounty computers
+ //And we don't need them to also load the holding facility.
+ SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_NINJA_HOLDING_FACILITY)
. = TRUE
if("stop")
stop_sending()
@@ -283,12 +287,9 @@
status_report = "Predicted value: "
var/value = 0
- var/datum/export_report/report = new
+
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
- for(var/atom/movable/AM in get_turf(pad))
- if(AM == pad)
- continue
- export_item_and_contents(AM, apply_elastic = FALSE, dry_run = TRUE, external_report = report, ignore_typecache = nosell_typecache)
+ var/datum/export_report/report = pirate_export_loop(pad)
for(var/datum/export/exported_datum in report.total_amount)
status_report += exported_datum.total_printout(report,notes = FALSE)
@@ -303,13 +304,8 @@
if(!sending)
return
- var/datum/export_report/report = new
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
-
- for(var/atom/movable/item_on_pad in get_turf(pad))
- if(item_on_pad == pad)
- continue
- export_item_and_contents(item_on_pad, apply_elastic = FALSE, delete_unsold = FALSE, external_report = report, ignore_typecache = nosell_typecache)
+ var/datum/export_report/report = pirate_export_loop(pad, dry_run = FALSE)
status_report = "Sold: "
var/value = 0
@@ -341,6 +337,33 @@
pad.icon_state = pad.idle_state
sending = FALSE
+///The loop that calculates the value of stuff on a pirate pad, or plain sell them if dry_run is FALSE.
+/obj/machinery/computer/piratepad_control/proc/pirate_export_loop(obj/machinery/piratepad/pad, dry_run = TRUE)
+ var/datum/export_report/report = new
+ for(var/atom/movable/item_on_pad as anything in get_turf(pad))
+ if(item_on_pad == pad)
+ continue
+ var/list/hidden_mobs = list()
+ var/skip_movable = FALSE
+ var/list/item_contents = item_on_pad.get_all_contents()
+ for(var/atom/movable/thing in reverse_range(item_contents))
+ ///Don't destroy/sell stuff like the captain's laser gun, or borgs.
+ if(thing.resistance_flags & INDESTRUCTIBLE || is_type_in_typecache(thing, nosell_typecache))
+ skip_movable = TRUE
+ break
+ if(isliving(thing))
+ hidden_mobs += thing
+ if(skip_movable)
+ continue
+ for(var/mob/living/hidden as anything in hidden_mobs)
+ ///Sell mobs, but leave their contents intact.
+ export_single_item(hidden, apply_elastic = FALSE, dry_run = dry_run, external_report = report)
+ ///there are still licing mobs inside that item. Stop, don't sell it ffs.
+ if(locate(/mob/living) in item_on_pad.get_all_contents())
+ continue
+ export_item_and_contents(item_on_pad, apply_elastic = FALSE, dry_run = dry_run, delete_unsold = FALSE, external_report = report, ignore_typecache = nosell_typecache)
+ return report
+
/// Prepares to sell the items on the pad
/obj/machinery/computer/piratepad_control/proc/start_sending()
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
@@ -391,7 +414,7 @@
/datum/export/pirate/ransom/get_cost(atom/movable/exported_item)
var/mob/living/carbon/human/ransomee = exported_item
- if(ransomee.stat != CONSCIOUS || !ransomee.mind) //mint condition only
+ if(ransomee.stat != CONSCIOUS || !ransomee.mind || HAS_TRAIT(ransomee.mind, TRAIT_HAS_BEEN_KIDNAPPED)) //mint condition only
return 0
else if(FACTION_PIRATE in ransomee.faction) //can't ransom your fellow pirates to CentCom!
return 0
@@ -400,6 +423,33 @@
else
return 1000
+/datum/export/pirate/ransom/sell_object(mob/living/carbon/human/sold_item, datum/export_report/report, dry_run = TRUE, apply_elastic = TRUE)
+ . = ..()
+ if(. == EXPORT_NOT_SOLD)
+ return
+ var/turf/picked_turf = pick(GLOB.holdingfacility)
+ sold_item.forceMove(picked_turf)
+ var/mob_cost = get_cost(sold_item)
+ sold_item.process_capture(mob_cost, mob_cost * 1.2)
+ do_sparks(8, FALSE, sold_item)
+ playsound(picked_turf, 'sound/weapons/emitter2.ogg', 25, TRUE)
+ sold_item.flash_act()
+ sold_item.adjust_confusion(10 SECONDS)
+ sold_item.adjust_dizzy(10 SECONDS)
+ addtimer(src, CALLBACK(src, PROC_REF(send_back_to_station), sold_item), COME_BACK_FROM_CAPTURE_TIME)
+ to_chat(sold_item, span_hypnophrase("A million voices echo in your head... \"Yaarrr, thanks for the booty, landlubber. \
+ You will be ransomed back to your station, so it's only a matter of time before we ship you back..."))
+
+ return EXPORT_SOLD_DONT_DELETE
+
+///Send them back to the station after a while.
+/datum/export/pirate/ransom/proc/send_back_to_station(mob/living/prisoner)
+ ///Deleted or already bailed out of the place.
+ if(QDELETED(prisoner) || !istype(get_area(prisoner), /area/centcom/central_command_areas/holding))
+ return
+ var/obj/structure/closet/supplypod/back_to_station/return_pod = new()
+ return_pod.return_from_capture(prisoner)
+
/datum/export/pirate/parrot
cost = 2000
unit_name = "alive parrot"
diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index e3c96fc815f..0a9422e07b8 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -133,13 +133,15 @@
do_sparks(3, FALSE, stealing)
// Don't mess with it while it's going away
+ var/had_attack_hand_interaction = stealing.interaction_flags_atom & INTERACT_ATOM_ATTACK_HAND
stealing.interaction_flags_atom &= ~INTERACT_ATOM_ATTACK_HAND
+ var/was_anchored = stealing.anchored
stealing.anchored = TRUE
// Add some pizzazz
- animate(stealing, time = 0.5 SECONDS, transform = matrix(stealing.transform).Scale(0.01), easing = CUBIC_EASING)
+ animate(stealing, time = 0.5 SECONDS, transform = stealing.transform.Scale(0.01), easing = CUBIC_EASING)
if(isitem(stealing) && ((stealing.resistance_flags & INDESTRUCTIBLE) || prob(black_market_prob)))
- addtimer(CALLBACK(src, PROC_REF(send_to_black_market), stealing), 0.5 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(send_to_black_market), stealing, had_attack_hand_interaction, was_anchored), 0.5 SECONDS)
else
addtimer(CALLBACK(src, PROC_REF(finish_cleanup), stealing), 0.5 SECONDS)
@@ -158,33 +160,31 @@
*
* * thing - The item to put up on the black market.
*/
-/datum/spy_bounty/proc/send_to_black_market(atom/movable/thing)
+/datum/spy_bounty/proc/send_to_black_market(atom/movable/thing, had_attack_hand_interaction, was_anchored)
if(QDELETED(thing)) // Just in case anything does anything weird
return FALSE
- thing.interaction_flags_atom = initial(thing.interaction_flags_atom)
- thing.anchored = initial(thing.anchored)
+ ///reset the appearance and all.
+ if(had_attack_hand_interaction)
+ thing.interaction_flags_atom |= INTERACT_ATOM_ATTACK_HAND
+ thing.anchored = was_anchored
+ thing.transform = thing.transform.Scale(10)
thing.moveToNullspace()
- var/datum/market_item/new_item = new()
- new_item.item = thing
- new_item.name = "Stolen [thing.name]"
- new_item.desc = "A [thing.name], stolen from somewhere on the station. Whoever owned it probably wouldn't be happy to see it here."
- new_item.category = "Fenced Goods"
- new_item.stock = 1
- new_item.availability_prob = 100
-
+ var/item_price
switch(difficulty)
if(SPY_DIFFICULTY_EASY)
- new_item.price = PAYCHECK_COMMAND * 2.5
+ item_price = PAYCHECK_COMMAND * 2.5
if(SPY_DIFFICULTY_MEDIUM)
- new_item.price = PAYCHECK_COMMAND * 5
+ item_price = PAYCHECK_COMMAND * 5
if(SPY_DIFFICULTY_HARD)
- new_item.price = PAYCHECK_COMMAND * 10
+ item_price = PAYCHECK_COMMAND * 10
- new_item.price += rand(0, PAYCHECK_COMMAND * 5)
+ item_price += rand(0, PAYCHECK_COMMAND * 5)
if(thing.resistance_flags & INDESTRUCTIBLE)
- new_item.price *= 2
+ item_price *= 2
+
+ var/datum/market_item/stolen_good/new_item = new(thing, item_price)
return SSblackmarket.markets[/datum/market/blackmarket].add_item(new_item)
diff --git a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
index b82ebd77bd7..876aea675de 100644
--- a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
@@ -21,6 +21,8 @@
var/wanted_message
///List of everything found on the victim at the time of contracting, used to return their stuff afterwards.
var/list/victim_belongings = list()
+ ///timerid for stuff that handles victim chat messages, effects and returnal
+ var/victim_timerid
/datum/syndicate_contract/New(contract_owner, blacklist, type=CONTRACT_PAYOUT_SMALL)
contract = new(src)
@@ -129,20 +131,8 @@
//we'll start the effects in a few seconds since it takes a moment for the pod to leave.
addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), person_sent), 3 SECONDS)
- // This is slightly delayed because of the sleep calls above to handle the narrative.
- // We don't want to tell the station instantly.
- var/points_to_check
- var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
- if(cargo_account)
- points_to_check = cargo_account.account_balance
- if(points_to_check >= ransom)
- cargo_account.adjust_money(-ransom)
- else
- cargo_account.adjust_money(-points_to_check)
- priority_announce(
- text = "One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
- As is policy we've taken a portion of the station's funds to offset the overall cost.",
- sender_override = "Nanotrasen Asset Protection")
+ var/datum/market_item/hostage/market_item = person_sent.process_capture(ransom * (rand(11, 13)/10))
+ RegisterSignal(market_item, COMSIG_MARKET_ITEM_SPAWNED, PROC_REF(on_victim_shipped))
addtimer(CALLBACK(src, PROC_REF(finish_enter)), 3 SECONDS)
@@ -178,10 +168,10 @@
* level - The current stage of harassement they are facing. This increases by itself, looping until finished.
*/
/datum/syndicate_contract/proc/handle_victim_experience(mob/living/victim, level = VICTIM_EXPERIENCE_START)
- // Ship 'em back - dead or alive, 4 minutes wait.
+ // Ship 'em back - dead or alive
// Even if they weren't the target, we're still treating them the same.
if(!level)
- addtimer(CALLBACK(src, PROC_REF(return_victim), victim), (60 * 10) * 4)
+ victim_timerid = addtimer(CALLBACK(src, PROC_REF(return_victim), victim), COME_BACK_FROM_CAPTURE_TIME, TIMER_STOPPABLE)
if(victim.stat == DEAD)
return
@@ -221,7 +211,7 @@
level++ //move onto the next level.
if(time_until_next)
- addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), victim, level), time_until_next)
+ victim_timerid = addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), victim, level), time_until_next, TIMER_STOPPABLE)
#undef VICTIM_EXPERIENCE_START
#undef VICTIM_EXPERIENCE_FIRST_HIT
@@ -229,35 +219,30 @@
#undef VICTIM_EXPERIENCE_THIRD_HIT
#undef VICTIM_EXPERIENCE_LAST_HIT
-// We're returning the victim
+/// We're returning the victim to the station
/datum/syndicate_contract/proc/return_victim(mob/living/victim)
var/list/possible_drop_loc = list()
-
- for(var/turf/possible_drop in contract.dropoff.contents)
+ for(var/turf/possible_drop in shuffle(contract.dropoff.contents))
if(!isspaceturf(possible_drop) && !isclosedturf(possible_drop))
if(!possible_drop.is_blocked_turf())
possible_drop_loc.Add(possible_drop)
- if(!possible_drop_loc.len)
- to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Seems where you got sent here from won't \
- be able to handle our pod... if we wanted the occupant to survive. Brace yourself, corporate dog.\""))
- for(var/turf/possible_drop in contract.dropoff.contents)
- possible_drop_loc.Add(possible_drop)
- if(iscarbon(victim))
- var/mob/living/carbon/carbon_victim = victim
- if(carbon_victim.can_heartattack())
- carbon_victim.set_heartattack(TRUE)
- carbon_victim.investigate_log("was returned without a valid drop location by the contractor [contract.owner?.current].", INVESTIGATE_DEATHS) // NOVA EDIT ADDITION
-
- var/pod_rand_loc = rand(1, possible_drop_loc.len)
- var/obj/structure/closet/supplypod/return_pod = new()
- return_pod.bluespace = TRUE
- return_pod.explosionSize = list(0,0,0,0)
- return_pod.style = STYLE_SYNDICATE
-
- do_sparks(8, FALSE, victim)
- victim.visible_message(span_notice("[victim] vanishes..."))
+ var/turf/destination
+ if(length(possible_drop_loc))
+ destination = pick(possible_drop_loc)
+
+ var/obj/structure/closet/supplypod/back_to_station/return_pod = new()
+ return_pod.return_from_capture(victim, destination)
+ returnal_side_effects(return_pod, victim)
+
+///Called if the victim is being returned to the station early, when from the black market.
+/datum/syndicate_contract/proc/on_victim_shipped(datum/market_item/source, obj/item/market_uplink/uplink, shipping_method, turf/shipping_loc)
+ SIGNAL_HANDLER
+ deltimer(victim_timerid)
+ returnal_side_effects(shipping_loc, source.item)
+///The annoying negative effects applied to the victim when returned to the station.
+/datum/syndicate_contract/proc/returnal_side_effects(atom/dropoff_location, mob/living/victim)
for(var/datum/weakref/belonging_ref in victim_belongings)
var/obj/item/belonging = belonging_ref.resolve()
if(!belonging)
@@ -269,20 +254,15 @@
continue
if(belonging == human_victim.shoes)
continue
- belonging.forceMove(return_pod)
-
- for(var/obj/item/W in victim_belongings)
- W.forceMove(return_pod)
+ belonging.forceMove(dropoff_location)
- victim.forceMove(return_pod)
+ for(var/obj/item/item in victim_belongings)
+ item.forceMove(dropoff_location)
victim.flash_act()
victim.adjust_eye_blur(3 SECONDS)
victim.adjust_dizzy(3.5 SECONDS)
victim.adjust_confusion(2 SECONDS)
-
- new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod)
-
// NOVA EDIT ADDITION START
#undef RANSOM_LOWER
#undef RANSOM_UPPER
diff --git a/code/modules/antagonists/traitor/objectives/hack_comm_console.dm b/code/modules/antagonists/traitor/objectives/hack_comm_console.dm
index 93323e4e15f..1874b79d8a1 100644
--- a/code/modules/antagonists/traitor/objectives/hack_comm_console.dm
+++ b/code/modules/antagonists/traitor/objectives/hack_comm_console.dm
@@ -15,6 +15,8 @@
var/progression_objectives_minimum = 20 MINUTES
/datum/traitor_objective/hack_comm_console/can_generate_objective(datum/mind/generating_for, list/possible_duplicates)
+ if(length(possible_duplicates) > 0)
+ return FALSE
if(SStraitor.get_taken_count(/datum/traitor_objective/hack_comm_console) > 0)
return FALSE
if(handler.get_completion_progression(/datum/traitor_objective) < progression_objectives_minimum)
diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm
index 73a3fc3b3f0..d6aec912fdb 100644
--- a/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -15,6 +15,8 @@
var/alive_bonus = 0
/// All stripped targets belongings (weakrefs)
var/list/target_belongings = list()
+ /// The ID of the stoppable timer for returning the captured crew
+ var/list/victim_timerid
duplicate_type = /datum/traitor_objective/target_player
@@ -216,6 +218,7 @@
new /obj/effect/pod_landingzone(get_turf(user), new_pod)
/datum/traitor_objective/target_player/kidnapping/proc/enter_check(obj/structure/closet/supplypod/extractionpod/source, entered_atom)
+ SIGNAL_HANDLER
if(!istype(source))
CRASH("Kidnapping objective's enter_check called with source being not an extraction pod: [source ? source.type : "N/A"]")
@@ -224,9 +227,6 @@
var/mob/living/carbon/human/sent_mob = entered_atom
- if(sent_mob.mind)
- ADD_TRAIT(sent_mob.mind, TRAIT_HAS_BEEN_KIDNAPPED, TRAIT_GENERIC)
-
for(var/obj/item/belonging in sent_mob.gather_belongings())
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
@@ -236,12 +236,8 @@
continue
target_belongings.Add(WEAKREF(belonging))
- var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
-
- if(cargo_account) //Just in case
- cargo_account.adjust_money(-min(rand(1000, 3000), cargo_account.account_balance)) //Not so much, especially for competent cargo. Plus this can't be mass-triggered like it has been done with contractors
-
- priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. As is policy we've taken a portion of the station's funds to offset the overall cost.", "Nanotrasen Asset Protection", has_important_message = TRUE)
+ var/datum/market_item/hostage/market_item = sent_mob.process_capture(rand(1000, 3000))
+ RegisterSignal(market_item, COMSIG_MARKET_ITEM_SPAWNED, PROC_REF(on_victim_shipped))
addtimer(CALLBACK(src, PROC_REF(handle_target), sent_mob), 1.5 SECONDS)
@@ -257,7 +253,7 @@
source.startExitSequence(source)
/datum/traitor_objective/target_player/kidnapping/proc/handle_target(mob/living/carbon/human/sent_mob)
- addtimer(CALLBACK(src, PROC_REF(return_target), sent_mob), 3 MINUTES)
+ victim_timerid = addtimer(CALLBACK(src, PROC_REF(return_target), sent_mob), COME_BACK_FROM_CAPTURE_TIME, TIMER_STOPPABLE)
if(sent_mob.stat == DEAD)
return
@@ -266,29 +262,24 @@
sent_mob.adjust_dizzy(10 SECONDS)
sent_mob.set_eye_blur_if_lower(100 SECONDS)
sent_mob.dna.species.give_important_for_life(sent_mob) // so plasmamen do not get left for dead
- to_chat(sent_mob, span_hypnophrase(span_reallybig("A million voices echo in your head... \"Your mind held many valuable secrets - \
+ to_chat(sent_mob, span_hypnophrase("A million voices echo in your head... \"Your mind held many valuable secrets - \
we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
- so it's only a matter of time before we ship you back...\"")))
+ so it's only a matter of time before we ship you back...\""))
/datum/traitor_objective/target_player/kidnapping/proc/return_target(mob/living/carbon/human/sent_mob)
if(!sent_mob || QDELETED(sent_mob)) //suicided and qdeleted themselves
return
- var/turf/return_turf = get_safe_random_station_turf()
- if(!return_turf) //SOMEHOW
- to_chat(sent_mob, span_hypnophrase(span_reallybig("A million voices echo in your head... \"Seems where you got sent here from won't \
- be able to handle our pod... You will die here instead.\"")))
- if (sent_mob.can_heartattack())
- sent_mob.set_heartattack(TRUE)
- return
+ var/obj/structure/closet/supplypod/back_to_station/return_pod = new()
+ return_pod.return_from_capture(sent_mob)
+ returnal_side_effects(return_pod, sent_mob)
- var/obj/structure/closet/supplypod/return_pod = new()
- return_pod.bluespace = TRUE
- return_pod.explosionSize = list(0,0,0,0)
- return_pod.style = STYLE_SYNDICATE
+/datum/traitor_objective/target_player/kidnapping/proc/on_victim_shipped(datum/market_item/source, obj/item/market_uplink/uplink, shipping_method, turf/shipping_loc)
+ SIGNAL_HANDLER
+ deltimer(victim_timerid)
+ returnal_side_effects(shipping_loc, source.item)
- do_sparks(8, FALSE, sent_mob)
- sent_mob.visible_message(span_notice("[sent_mob] vanishes!"))
+/datum/traitor_objective/target_player/kidnapping/proc/returnal_side_effects(atom/dropoff_location, mob/living/carbon/human/sent_mob)
for(var/obj/item/belonging in sent_mob.gather_belongings())
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
@@ -298,13 +289,10 @@
var/obj/item/belonging = belonging_ref.resolve()
if(!belonging)
continue
- belonging.forceMove(return_pod)
+ belonging.forceMove(dropoff_location)
- sent_mob.forceMove(return_pod)
sent_mob.flash_act()
sent_mob.adjust_confusion(10 SECONDS)
sent_mob.adjust_dizzy(10 SECONDS)
sent_mob.set_eye_blur_if_lower(100 SECONDS)
sent_mob.dna.species.give_important_for_life(sent_mob) // so plasmamen do not get left for dead
-
- new /obj/effect/pod_landingzone(return_turf, return_pod)
diff --git a/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm b/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm
index 52813fcdf57..b3e211d2867 100644
--- a/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm
+++ b/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm
@@ -27,6 +27,8 @@
var/area/weakpoint_area
/datum/traitor_objective/locate_weakpoint/can_generate_objective(datum/mind/generating_for, list/possible_duplicates)
+ if(length(possible_duplicates) > 0)
+ return FALSE
if(handler.get_completion_progression(/datum/traitor_objective) < progression_objectives_minimum)
return FALSE
if(SStraitor.get_taken_count(/datum/traitor_objective/locate_weakpoint) > 0)
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 478d5000721..b5c847a78ab 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -2,189 +2,298 @@
name = "infrared emitter"
desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted."
icon_state = "infrared"
- custom_materials = list(/datum/material/iron=HALF_SHEET_MATERIAL_AMOUNT, /datum/material/glass=SMALL_MATERIAL_AMOUNT*5)
+ base_icon_state = "infrared"
+ custom_materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5,
+ )
is_position_sensitive = TRUE
drop_sound = 'sound/items/handling/component_drop.ogg'
pickup_sound = 'sound/items/handling/component_pickup.ogg'
+ set_dir_on_move = FALSE
+ /// Whether the beam is beaming
var/on = FALSE
+ /// Whether the beam is visible
var/visible = FALSE
- var/maxlength = 8
- var/list/obj/effect/beam/i_beam/beams
- var/olddir = 0
- var/turf/listeningTo
+ /// The length the beam can go
+ var/max_beam_length = 8
+ /// The radius of which people can hear triggers
var/hearing_range = 3
+ /// Pass flags the beam uses to determine what it can pass through
+ var/beam_pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE
+ /// The current active beam datum
+ VAR_FINAL/datum/beam/active_beam
+ /// A reference to the turf at the END of our active beam
+ VAR_FINAL/turf/buffer_turf
/obj/item/assembly/infra/Initialize(mapload)
. = ..()
- beams = list()
- START_PROCESSING(SSobj, src)
- AddComponent(/datum/component/simple_rotation, post_rotation = CALLBACK(src, PROC_REF(post_rotation)))
+ AddComponent(/datum/component/simple_rotation)
-/obj/item/assembly/infra/proc/post_rotation(mob/user, degrees)
- refreshBeam()
+/obj/item/assembly/infra/Destroy()
+ QDEL_NULL(active_beam)
+ buffer_turf = null
+ return ..()
/obj/item/assembly/infra/AltClick(mob/user)
return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation
-/obj/item/assembly/infra/Destroy()
- STOP_PROCESSING(SSobj, src)
- listeningTo = null
- QDEL_LIST(beams)
- . = ..()
-
/obj/item/assembly/infra/examine(mob/user)
. = ..()
- . += span_notice("The infrared trigger is [on?"on":"off"].")
+ . += span_notice("The infrared trigger is [on ? "on" : "off"].")
+
+/// Checks if the passed movable can block the beam.
+/obj/item/assembly/infra/proc/atom_blocks_beam(atom/movable/beam_atom)
+ if(isnull(beam_atom))
+ return FALSE
+ if(beam_atom == src || beam_atom == holder)
+ return FALSE
+ // Blocks beams from triggering themselves, important to avoid infinite loops
+ if(istype(beam_atom, /obj/effect/ebeam))
+ return FALSE
+ // Anti-revenant / anti-ghost guard
+ if(beam_atom.invisibility)
+ return FALSE
+ // In general non-dense items should not block beams, but make special cases for things being thrown
+ if(!beam_atom.density && !beam_atom.throwing)
+ return FALSE
+ // The actually important check. Ensures stuff like mobs trip it but stuff like laser projectiles don't
+ if(beam_atom.pass_flags_self & beam_pass_flags)
+ return FALSE
+ if(isitem(beam_atom))
+ var/obj/item/beam_item = beam_atom
+ if(beam_item.item_flags & ABSTRACT)
+ return FALSE
-/obj/item/assembly/infra/activate()
- if(!..())
- return FALSE //Cooldown check
- on = !on
- refreshBeam()
- update_appearance()
return TRUE
-/obj/item/assembly/infra/toggle_secure()
- secured = !secured
- if(secured)
- START_PROCESSING(SSobj, src)
- refreshBeam()
- else
- QDEL_LIST(beams)
- STOP_PROCESSING(SSobj, src)
- update_appearance()
- return secured
+/// Checks if the passed turf (or something on it) can block the beam.
+/obj/item/assembly/infra/proc/turf_blocks_beam(turf/beam_turf)
+ if(beam_turf.density)
+ return TRUE
+ for(var/atom/movable/blocker as anything in beam_turf)
+ if(atom_blocks_beam(blocker))
+ return TRUE
+ return FALSE
-/obj/item/assembly/infra/update_appearance(updates=ALL)
- . = ..()
- holder?.update_appearance(updates)
+/// Used to refresh the beam in whatever context.
+/obj/item/assembly/infra/proc/make_beam()
+ SHOULD_NOT_SLEEP(TRUE)
-/obj/item/assembly/infra/update_overlays()
- . = ..()
- attached_overlays = list()
- if(!on)
+ if(!isnull(buffer_turf))
+ UnregisterSignal(buffer_turf, list(COMSIG_ATOM_EXITED, COMSIG_TURF_CHANGE))
+ buffer_turf = null
+
+ QDEL_NULL(active_beam)
+ if(!on || !secured)
return
- . += "infrared_on"
- attached_overlays += "infrared_on"
- if(visible && secured)
- . += "infrared_visible"
- attached_overlays += "infrared_visible"
-/obj/item/assembly/infra/dropped()
- . = ..()
- if(holder)
- holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
- else
- INVOKE_ASYNC(src, PROC_REF(refreshBeam))
+ var/atom/start_loc = holder || src
+ var/turf/start_turf = start_loc.loc
+ if(!istype(start_turf))
+ return
+ // One extra turf is added to max length to get an extra buffer
+ var/list/turf/potential_turfs = get_line(start_turf, get_ranged_target_turf(start_turf, dir, max_beam_length + 1))
+ if(!length(potential_turfs))
+ return
-/obj/item/assembly/infra/process()
- if(!on || !secured)
- refreshBeam()
+ var/list/turf/final_turfs = list()
+ for(var/turf/target_turf as anything in potential_turfs)
+ if(target_turf != start_turf && turf_blocks_beam(target_turf))
+ break
+ final_turfs += target_turf
+
+ if(!length(final_turfs))
return
-/obj/item/assembly/infra/proc/refreshBeam()
- QDEL_LIST(beams)
- if(throwing || !on || !secured)
+ var/turf/last_turf = final_turfs[length(final_turfs)]
+ buffer_turf = get_step(last_turf, dir)
+
+ var/beam_target_x = pixel_x
+ var/beam_target_y = pixel_y
+ // The beam by default will go to middle of turf (because items are in the middle of turfs)
+ // So we need to offset it
+ if(dir & NORTH)
+ beam_target_y += 16
+ else if(dir & SOUTH)
+ beam_target_y -= 16
+ if(dir & WEST)
+ beam_target_x -= 16
+ else if(dir & EAST)
+ beam_target_x += 16
+
+ active_beam = start_loc.Beam(
+ BeamTarget = last_turf,
+ beam_type = /obj/effect/ebeam/reacting/infrared,
+ icon = 'icons/effects/beam.dmi',
+ icon_state = "1-full",
+ beam_color = COLOR_RED,
+ emissive = TRUE,
+ override_target_pixel_x = beam_target_x,
+ override_target_pixel_y = beam_target_y,
+ )
+ RegisterSignal(active_beam, COMSIG_BEAM_ENTERED, PROC_REF(beam_entered))
+ RegisterSignal(active_beam, COMSIG_BEAM_TURFS_CHANGED, PROC_REF(beam_turfs_changed))
+ update_visible()
+ // Buffer can be null (if we're at map edge for an example) but this fine
+ if(!isnull(buffer_turf))
+ // We need to check the state of the turf at the end of the beam, to determine when we need to re-grow (if blocked)
+ RegisterSignal(buffer_turf, COMSIG_ATOM_EXITED, PROC_REF(buffer_exited))
+ RegisterSignal(buffer_turf, COMSIG_TURF_CHANGE, PROC_REF(buffer_changed))
+
+/obj/item/assembly/infra/proc/beam_entered(datum/beam/source, obj/effect/ebeam/hit, atom/movable/entered)
+ SIGNAL_HANDLER
+
+ // First doesn't count
+ if(hit == active_beam.elements[1])
return
- if(holder)
- if(holder.master) //incase the sensor is part of an assembly that's contained in another item, such as a single tank bomb
- if(!holder.master.IsSpecialAssembly() || !isturf(holder.master.loc))
- return
- else if(!isturf(holder.loc)) //else just check where the holder is
- return
- else if(!isturf(loc)) //or just where the fuck we are in general
+ if(!atom_blocks_beam(entered))
return
- var/turf/T = get_turf(src)
- var/_dir = dir
- var/turf/_T = get_step(T, _dir)
- if(_T)
- for(var/i in 1 to maxlength)
- var/obj/effect/beam/i_beam/I = new(T)
- if(istype(holder, /obj/item/assembly_holder))
- I.icon_state = "[initial(I.icon_state)]_l" //Sync the offset of the beam with the position of the sensor.
- else if(istype(holder, /obj/item/transfer_valve))
- I.icon_state = "[initial(I.icon_state)]_ttv"
- I.set_density(TRUE)
- if(!I.Move(_T))
- qdel(I)
- switchListener(_T)
- break
- I.set_density(FALSE)
- beams += I
- I.master = src
- I.setDir(_dir)
- if(!visible)
- I.SetInvisibility(INVISIBILITY_ABSTRACT)
- T = _T
- _T = get_step(_T, _dir)
- CHECK_TICK
-/obj/item/assembly/infra/on_detach()
+ beam_trigger(hit, entered)
+
+/obj/item/assembly/infra/proc/beam_turfs_changed(datum/beam/source, list/datum/callback/post_change_callbacks)
+ SIGNAL_HANDLER
+ // If the turfs changed it's possible something is now blocking it, remake when done
+ post_change_callbacks += CALLBACK(src, PROC_REF(make_beam))
+
+/obj/item/assembly/infra/proc/buffer_exited(turf/source, atom/movable/exited, ...)
+ SIGNAL_HANDLER
+
+ if(!atom_blocks_beam(exited))
+ return
+
+ make_beam()
+
+/obj/item/assembly/infra/proc/buffer_changed(turf/source, path, list/new_baseturfs, flags, list/datum/callback/post_change_callbacks)
+ SIGNAL_HANDLER
+
+ post_change_callbacks += CALLBACK(src, PROC_REF(make_beam))
+
+/obj/item/assembly/infra/proc/beam_trigger(obj/effect/ebeam/hit, atom/movable/entered)
+ make_beam()
+ if(!COOLDOWN_FINISHED(src, next_activate))
+ return
+
+ pulse()
+ audible_message(
+ message = span_infoplain("[icon2html(src, hearers(holder || src))] *beep* *beep* *beep*"),
+ hearing_distance = hearing_range,
+ )
+ playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE, extrarange = hearing_range - SOUND_RANGE + 1, falloff_distance = hearing_range)
+ COOLDOWN_START(src, next_activate, 3 SECONDS)
+
+/obj/item/assembly/infra/activate()
. = ..()
if(!.)
return
- refreshBeam()
-/obj/item/assembly/infra/attack_hand(mob/user, list/modifiers)
+ toggle_on()
+
+/obj/item/assembly/infra/toggle_secure()
. = ..()
- refreshBeam()
+ make_beam()
+
+/// Toggles the beam on or off.
+/obj/item/assembly/infra/proc/toggle_on()
+ on = !on
+ make_beam()
+ update_appearance()
+
+/// Toggles the visibility of the beam.
+/obj/item/assembly/infra/proc/toggle_visible()
+ visible = !visible
+ update_visible()
+ update_appearance()
-/obj/item/assembly/infra/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
- var/t = dir
+/// Updates the visibility of the beam (if active).
+/obj/item/assembly/infra/proc/update_visible()
+ if(visible)
+ for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
+ beam.RemoveInvisibility(REF(src))
+ else
+ for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
+ beam.SetInvisibility(INVISIBILITY_ABSTRACT, REF(src))
+
+/obj/item/assembly/infra/vv_edit_var(var_name, var_value)
. = ..()
- setDir(t)
+ if(!.)
+ return
+ switch(var_name)
+ if(NAMEOF(src, visible))
+ update_visible()
+ update_appearance()
+
+ if(NAMEOF(src, on), NAMEOF(src, max_beam_length), NAMEOF(src, beam_pass_flags))
+ make_beam()
+ update_appearance()
-/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
+/obj/item/assembly/infra/update_appearance(updates)
. = ..()
- olddir = dir
+ holder?.update_appearance(updates)
-/obj/item/assembly/infra/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+/obj/item/assembly/infra/update_overlays()
. = ..()
- if(!olddir)
- return
- setDir(olddir)
- olddir = null
+ attached_overlays = list()
+ if(on)
+ attached_overlays += "[base_icon_state]_on"
-/obj/item/assembly/infra/proc/trigger_beam(atom/movable/AM, turf/location)
- refreshBeam()
- switchListener(location)
- if(!secured || !on || next_activate > world.time)
- return FALSE
- pulse()
- audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
- for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src))
- hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
- next_activate = world.time + 30
+ . += attached_overlays
-/obj/item/assembly/infra/proc/switchListener(turf/newloc)
- if(listeningTo == newloc)
- return
- if(listeningTo)
- UnregisterSignal(listeningTo, COMSIG_ATOM_EXITED)
- RegisterSignal(newloc, COMSIG_ATOM_EXITED, PROC_REF(check_exit))
- listeningTo = newloc
+/obj/item/assembly/infra/dropped()
+ . = ..()
+ if(holder)
+ holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
+ else
+ make_beam()
-/obj/item/assembly/infra/proc/check_exit(datum/source, atom/movable/gone, direction)
- SIGNAL_HANDLER
+/obj/item/assembly/infra/on_attach()
+ . = ..()
+ make_beam()
+ holder.set_dir_on_move = set_dir_on_move
- if(QDELETED(src))
- return
- if(src == gone || istype(gone, /obj/effect/beam/i_beam))
+/obj/item/assembly/infra/on_detach()
+ holder.set_dir_on_move = initial(holder.set_dir_on_move)
+ . = ..()
+ if(!.)
return
- if(isitem(gone))
- var/obj/item/I = gone
- if (I.item_flags & ABSTRACT)
- return
- INVOKE_ASYNC(src, PROC_REF(refreshBeam))
+ make_beam()
-/obj/item/assembly/infra/setDir()
+/obj/item/assembly/infra/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
+ . = ..()
+ if(loc == old_loc)
+ return
+ make_beam()
+ if(!visible || forced || !movement_dir || !Adjacent(old_loc))
+ return
+ // Because the new beam is made in the new loc, it "jumps" from one turf to another
+ // We can do an animate to pretend we're gliding between turfs rather than making a whole new beam
+ var/x_move = 0
+ var/y_move = 0
+ if(movement_dir & NORTH)
+ y_move = -32
+ else if(movement_dir & SOUTH)
+ y_move = 32
+ if(movement_dir & WEST)
+ x_move = 32
+ else if(movement_dir & EAST)
+ x_move = -32
+
+ var/fake_glide_time = round(world.icon_size / glide_size * world.tick_lag, world.tick_lag)
+ for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
+ var/matrix/base_transform = matrix(beam.transform)
+ beam.transform = beam.transform.Translate(x_move, y_move)
+ animate(beam, transform = base_transform, time = fake_glide_time)
+
+/obj/item/assembly/infra/setDir(newdir)
+ var/prev_dir = dir
. = ..()
- refreshBeam()
+ if(dir == prev_dir)
+ return
+ make_beam()
/obj/item/assembly/infra/ui_status(mob/user, datum/ui_state/state)
- if(is_secured(user))
- return ..()
- return UI_CLOSE
+ return is_secured(user) ? ..() : UI_CLOSE
/obj/item/assembly/infra/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -201,44 +310,17 @@
/obj/item/assembly/infra/ui_act(action, params)
. = ..()
if(.)
- return
+ return .
switch(action)
if("power")
- on = !on
- . = TRUE
+ toggle_on()
+ return TRUE
if("visibility")
- visible = !visible
- . = TRUE
-
- update_appearance()
- refreshBeam()
+ toggle_visible()
+ return TRUE
-/***************************IBeam*********************************/
-
-/obj/effect/beam/i_beam
+// Beam subtype for the infrared emitter
+/obj/effect/ebeam/reacting/infrared
name = "infrared beam"
- icon = 'icons/obj/weapons/guns/projectiles.dmi'
- icon_state = "ibeam"
- anchored = TRUE
- density = FALSE
- pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE
- pass_flags_self = LETPASSTHROW
- var/obj/item/assembly/infra/master
-
-/obj/effect/beam/i_beam/Initialize(mapload)
- . = ..()
- var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
- )
- AddElement(/datum/element/connect_loc, loc_connections)
-
-/obj/effect/beam/i_beam/proc/on_entered(datum/source, atom/movable/AM as mob|obj)
- SIGNAL_HANDLER
- if(istype(AM, /obj/effect/beam))
- return
- if (isitem(AM))
- var/obj/item/I = AM
- if (I.item_flags & ABSTRACT)
- return
- INVOKE_ASYNC(master, TYPE_PROC_REF(/obj/item/assembly/infra, trigger_beam), AM, get_turf(src))
+ alpha = 175
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 82760e75a62..d42618e9ee9 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -643,8 +643,10 @@
if(!has_cap_visuals)
return
- var/turf/our_turf = get_turf(src)
- our_turf.vis_contents -= cap_overlay
+ cap_overlay?.moveToNullspace()
+
+ if(!HAS_TRAIT(src, TRAIT_UNDERFLOOR))
+ return
var/connections = NONE
for(var/obj/machinery/atmospherics/node in nodes)
@@ -660,8 +662,8 @@
if(connections == NONE)
return
- var/bitfield = CARDINAL_TO_PIPECAPS(connections)
- bitfield |= ((~connections) & ALL_CARDINALS)
+ var/bitfield = CARDINAL_TO_PIPECAPS(connections) | (~connections) & ALL_CARDINALS
+ var/turf/our_turf = get_turf(src)
if(isnull(cap_overlay))
cap_overlay = new
@@ -672,12 +674,13 @@
cap_overlay.layer = layer
cap_overlay.icon_state = "[bitfield]_[piping_layer]"
- our_turf.vis_contents += cap_overlay
+ cap_overlay.forceMove(our_turf)
/obj/effect/overlay/cap_visual
- appearance_flags = KEEP_APART
- vis_flags = VIS_INHERIT_ID
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
icon = 'icons/obj/pipes_n_cables/!pipes_bitmask.dmi'
+ vis_flags = NONE
+ anchored = TRUE
/**
* Called by the RPD.dm pre_attack()
diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm
index fd6e91a8580..7996cc0f8fa 100644
--- a/code/modules/cargo/exports.dm
+++ b/code/modules/cargo/exports.dm
@@ -30,6 +30,14 @@ Then the player gets the profit from selling his own wasted time.
///set to false if any objects in a dry run were unscannable
var/all_contents_scannable = TRUE
+/// Makes sure the exports list is populated and that the report isn't null.
+/proc/init_export(datum/export_report/external_report)
+ if(!length(GLOB.exports_list))
+ setupExports()
+ if(isnull(external_report))
+ external_report = new
+ return external_report
+
/*
* Handles exporting a movable atom and its contents
* Arguments:
@@ -40,32 +48,15 @@ Then the player gets the profit from selling his own wasted time.
** ignore_typecache: typecache containing types that should be completely ignored
*/
/proc/export_item_and_contents(atom/movable/exported_atom, apply_elastic = TRUE, delete_unsold = TRUE, dry_run = FALSE, datum/export_report/external_report, list/ignore_typecache)
- if(!GLOB.exports_list.len)
- setupExports()
+ external_report = init_export(external_report)
var/list/contents = exported_atom.get_all_contents_ignoring(ignore_typecache)
- var/datum/export_report/report = external_report
-
- if(!report) //If we don't have any longer transaction going on
- report = new
-
// We go backwards, so it'll be innermost objects sold first. We also make sure nothing is accidentally delete before everything is sold.
var/list/to_delete = list()
for(var/atom/movable/thing as anything in reverse_range(contents))
- var/sold = FALSE
- for(var/datum/export/export as anything in GLOB.exports_list)
- if(export.applies_to(thing, apply_elastic))
- if(!dry_run && (SEND_SIGNAL(thing, COMSIG_ITEM_PRE_EXPORT) & COMPONENT_STOP_EXPORT))
- break
- //Don't add value of unscannable items for a dry run report
- if(dry_run && !export.scannable)
- report.all_contents_scannable = FALSE
- break
- sold = export.sell_object(thing, report, dry_run, apply_elastic)
- report.exported_atoms += " [thing.name]"
- break
- if(!dry_run && (sold || delete_unsold))
+ var/sold = _export_loop(thing, apply_elastic, dry_run, external_report)
+ if(!dry_run && (sold || delete_unsold) && sold != EXPORT_SOLD_DONT_DELETE)
if(ismob(thing))
thing.investigate_log("deleted through cargo export", INVESTIGATE_CARGO)
to_delete += thing
@@ -74,7 +65,35 @@ Then the player gets the profit from selling his own wasted time.
if(!QDELETED(thing))
qdel(thing)
- return report
+ return external_report
+
+/// It works like export_item_and_contents(), however it ignores the contents. Meaning only `exported_atom` will be valued.
+/proc/export_single_item(atom/movable/exported_atom, apply_elastic = TRUE, delete_unsold = TRUE, dry_run = FALSE, datum/export_report/external_report)
+ external_report = init_export(external_report)
+
+ var/sold = _export_loop(exported_atom, apply_elastic, dry_run, external_report)
+ if(!dry_run && (sold || delete_unsold) && sold != EXPORT_SOLD_DONT_DELETE)
+ if(ismob(exported_atom))
+ exported_atom.investigate_log("deleted through cargo export", INVESTIGATE_CARGO)
+ qdel(exported_atom)
+
+ return external_report
+
+/// The main bit responsible for selling the item. Shared by export_single_item() and export_item_and_contents()
+/proc/_export_loop(atom/movable/exported_atom, apply_elastic = TRUE, dry_run = FALSE, datum/export_report/external_report)
+ var/sold = EXPORT_NOT_SOLD
+ for(var/datum/export/export as anything in GLOB.exports_list)
+ if(export.applies_to(exported_atom, apply_elastic))
+ if(!dry_run && (SEND_SIGNAL(exported_atom, COMSIG_ITEM_PRE_EXPORT) & COMPONENT_STOP_EXPORT))
+ break
+ //Don't add value of unscannable items for a dry run report
+ if(dry_run && !export.scannable)
+ external_report.all_contents_scannable = FALSE
+ break
+ sold = export.sell_object(exported_atom, external_report, dry_run, apply_elastic)
+ external_report.exported_atoms += " [exported_atom.name]"
+ break
+ return sold
/datum/export
/// Unit name. Only used in "Received [total_amount] [name]s [message]."
@@ -164,7 +183,7 @@ Then the player gets the profit from selling his own wasted time.
var/export_amount = get_amount(sold_item)
if(export_amount <= 0 || (export_value <= 0 && !allow_negative_cost))
- return FALSE
+ return EXPORT_NOT_SOLD
// If we're not doing a dry run, send COMSIG_ITEM_EXPORTED to the sold item
var/export_result
@@ -180,7 +199,7 @@ Then the player gets the profit from selling his own wasted time.
if(apply_elastic)
cost *= NUM_E**(-1 * k_elasticity * export_amount) //marginal cost modifier
SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[sold_item.type]", "[export_value]"))
- return TRUE
+ return EXPORT_SOLD
/*
* Total printout for the cargo console.
diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm
index fd30973d241..c39fa59c200 100644
--- a/code/modules/cargo/goodies.dm
+++ b/code/modules/cargo/goodies.dm
@@ -308,3 +308,10 @@
desc = "A less cheap imported climbing hook. Absolutely no use outside of planetary stations."
cost = PAYCHECK_CREW * 5
contains = list(/obj/item/climbing_hook)
+
+/datum/supply_pack/goody/double_barrel
+ name = "Double Barrel Shotgun"
+ desc = "Lost your beloved bunny to a demonic invasion? Clown broke in and stole your beloved gun? No worries! Get a new gun so long as you can pay the absurd fees."
+ cost = PAYCHECK_COMMAND * 18
+ access_view = ACCESS_WEAPONS
+ contains = list(/obj/item/gun/ballistic/shotgun/doublebarrel)
diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm
index a4af2bc981d..78585ed842f 100644
--- a/code/modules/cargo/markets/_market.dm
+++ b/code/modules/cargo/markets/_market.dm
@@ -20,39 +20,53 @@
categories += item.category
available_items[item.category] = list()
- available_items[item.category] += item
+ available_items[item.category][item.identifier] = item
+ RegisterSignal(item, COMSIG_QDELETING, PROC_REF(on_item_del))
return TRUE
+/datum/market/proc/on_item_del(datum/market_item/item)
+ SIGNAL_HANDLER
+ available_items[item.category] -= item.identifier
+ if(!length(available_items[item.category]))
+ available_items -= item.category
+
/// Handles buying the item, this is mainly for future use and moving the code away from the uplink.
-/datum/market/proc/purchase(item, category, method, obj/item/market_uplink/uplink, user)
- if(!istype(uplink) || !(method in shipping))
+/datum/market/proc/purchase(identifier, category, method, obj/item/market_uplink/uplink, user)
+ var/datum/market_item/item = available_items[category][identifier]
+ if(isnull(item))
+ return FALSE
+
+ if(!istype(uplink) || !((method in shipping) || (method in item.shipping_override)))
+ return FALSE
+
+ var/shipment_fee = item.shipping_override?[method]
+ if(isnull(shipment_fee))
+ shipment_fee = shipping[method]
+ var/price = item.price + shipment_fee
+
+ if(!uplink.current_user)///There is no ID card on the user, or the ID card has no account
+ to_chat(user, span_warning("The uplink sparks, as it can't identify an ID card with a bank account on you."))
return FALSE
+ var/balance = uplink?.current_user.account_balance
- for(var/datum/market_item/I in available_items[category])
- if(I.type != item)
- continue
- var/price = I.price + shipping[method]
-
- if(!uplink.current_user)///There is no ID card on the user, or the ID card has no account
- to_chat(user, span_warning("The uplink sparks, as it can't identify an ID card with a bank account on you."))
- return FALSE
- var/balance = uplink?.current_user.account_balance
-
- // I can't get the price of the item and shipping in a clean way to the UI, so I have to do this.
- if(balance < price)
- to_chat(user, span_warning("You don't have enough credits in [uplink] for [I] with [method] shipping."))
- return FALSE
-
- if(I.buy(uplink, user, method))
- uplink.current_user.adjust_money(-price, "Other: Third Party Transaction")
- if(ismob(user))
- var/mob/m_user = user
- m_user.playsound_local(get_turf(m_user), 'sound/machines/twobeep_high.ogg', 50, TRUE)
- return TRUE
+ // I can't get the price of the item and shipping in a clean way to the UI, so I have to do this.
+ if(balance < price)
+ to_chat(user, span_warning("You don't have enough credits in [uplink] for [item] with [method] shipping."))
return FALSE
+ if(item.buy(uplink, user, method))
+ uplink.current_user.adjust_money(-price, "Other: Third Party Transaction")
+ if(ismob(user))
+ var/mob/m_user = user
+ m_user.playsound_local(get_turf(m_user), 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ return TRUE
+
+ return FALSE
+
/datum/market/blackmarket
name = "Black Market"
- shipping = list(SHIPPING_METHOD_LTSRBT =50,
- SHIPPING_METHOD_LAUNCH =10,
- SHIPPING_METHOD_TELEPORT=75)
+ shipping = list(
+ SHIPPING_METHOD_LTSRBT = 40,
+ SHIPPING_METHOD_LAUNCH = 10,
+ SHIPPING_METHOD_TELEPORT= 75,
+ )
diff --git a/code/modules/cargo/markets/market_item.dm b/code/modules/cargo/markets/market_item.dm
index d5689c17a45..01eb37c077b 100644
--- a/code/modules/cargo/markets/market_item.dm
+++ b/code/modules/cargo/markets/market_item.dm
@@ -14,7 +14,7 @@
var/stock
/// Path to or the item itself what this entry is for, this should be set even if you override spawn_item to spawn your item.
- var/obj/item/item
+ var/atom/movable/item
/// Minimum price for the item if generated randomly.
var/price_min = 0
@@ -27,27 +27,60 @@
/// Probability for this item to be available. Used by SSblackmarket on init.
var/availability_prob = 0
+ ///The identifier for the market item, generated on runtime and used to access them in the market categories.
+ var/identifier
+
+ ///If set, these will override the shipment methods set by the market
+ var/list/shipping_override
+
/datum/market_item/New()
if(isnull(price))
price = rand(price_min, price_max)
if(isnull(stock))
stock = rand(stock_min, stock_max)
+ identifier = "[type]"
+
+///For 'dynamic' market items generated on runtime, this proc is to be used to properly sets the item, especially if it's a hardref.
+/datum/market_item/proc/set_item(path_or_ref)
+ //we're replacing the item to sell, and the old item is an instance!
+ if(ismovable(item))
+ UnregisterSignal(item, COMSIG_QDELETING)
+ item = path_or_ref
+ identifier = "[path_or_ref]"
+ if(ismovable(path_or_ref))
+ RegisterSignal(item, COMSIG_QDELETING, PROC_REF(on_item_del))
+ identifier = "[REF(src)]"
/datum/market_item/Destroy()
item = null
return ..()
+/datum/market_item/proc/on_item_del(datum/source)
+ SIGNAL_HANDLER
+ qdel(src)
+
/// Used for spawning the wanted item, override if you need to do something special with the item.
-/datum/market_item/proc/spawn_item(loc)
+/datum/market_item/proc/spawn_item(loc, datum/market_purchase/purchase)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(src, COMSIG_MARKET_ITEM_SPAWNED, purchase.uplink, purchase.method, loc)
if(ismovable(item))
- item.forceMove(loc)
- return item
+ var/atom/movable/return_item = item
+ UnregisterSignal(item, COMSIG_QDELETING)
+ if(isnull(loc))
+ item.moveToNullspace()
+ else
+ do_sparks(8, FALSE, item)
+ item.visible_message(span_notice("[item] vanishes..."))
+ item.forceMove(loc)
+ item = null
+ return return_item
if(ispath(item))
return new item(loc)
CRASH("Invalid item type for market item [item || "null"]")
/// Buys the item and makes SSblackmarket handle it.
/datum/market_item/proc/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method)
+ SHOULD_CALL_PARENT(TRUE)
// Sanity
if(!istype(uplink) || !istype(buyer))
return FALSE
@@ -70,16 +103,34 @@
/datum/market_purchase
/// The entry being purchased.
var/datum/market_item/entry
- /// Instance of the item being sent.
- var/item
+ /// Instance of the item being sent, used by the market telepad
+ var/atom/movable/item
/// The uplink where this purchase was done from.
var/obj/item/market_uplink/uplink
/// Shipping method used to buy this item.
var/method
-/datum/market_purchase/New(_entry, _uplink, _method)
- entry = _entry
- if(!ispath(entry.item))
+/datum/market_purchase/New(datum/market_item/entry, obj/item/market_uplink/uplink, method)
+ if(!uplink || !entry || !method)
+ CRASH("[type] created with a false value arg: (entry: [entry] - uplink: [uplink] - method: [method])")
+ src.entry = entry
+ src.uplink = uplink
+ src.method = method
+ RegisterSignal(entry, COMSIG_QDELETING, PROC_REF(on_instance_del))
+ RegisterSignal(uplink, COMSIG_QDELETING, PROC_REF(on_instance_del))
+ if(ismovable(entry.item))
item = entry.item
- uplink = _uplink
- method = _method
+ RegisterSignal(entry.item, COMSIG_QDELETING, PROC_REF(on_instance_del))
+
+/datum/market_purchase/Destroy()
+ entry = null
+ uplink = null
+ SSblackmarket.queued_purchases -= src
+ return ..()
+
+/datum/market_purchase/proc/on_instance_del(datum/source)
+ SIGNAL_HANDLER
+ if(QDELETED(src))
+ return
+ // Uh oh, uplink or item is gone. We will just keep the money and you will not get your order.
+ qdel(src)
diff --git a/code/modules/cargo/markets/market_items/clothing.dm b/code/modules/cargo/markets/market_items/clothing.dm
index 8af34e22916..b38cf4caf2e 100644
--- a/code/modules/cargo/markets/market_items/clothing.dm
+++ b/code/modules/cargo/markets/market_items/clothing.dm
@@ -92,6 +92,13 @@
price_max = CARGO_CRATE_VALUE
stock_max = 3
availability_prob = 40
-
-
-
+/** Nova Edit Removal
+/datum/market_item/clothing/collar_bomb
+ name = "Collar Bomb Kit"
+ desc = "An unpatented and questionably ethical kit consisting of a low-yield explosive collar and a remote to trigger it."
+ item = /obj/item/storage/box/collar_bomb
+ price_min = CARGO_CRATE_VALUE * 3.5
+ price_max = CARGO_CRATE_VALUE * 4.5
+ stock_max = 3
+ availability_prob = 60
+**/
diff --git a/code/modules/cargo/markets/market_items/consumables.dm b/code/modules/cargo/markets/market_items/consumables.dm
index 153241e5d3d..5550d31c5f8 100644
--- a/code/modules/cargo/markets/market_items/consumables.dm
+++ b/code/modules/cargo/markets/market_items/consumables.dm
@@ -39,7 +39,8 @@
/obj/item/storage/pill_bottle/lsd,
/obj/item/storage/pill_bottle/aranesp,
/obj/item/storage/pill_bottle/stimulant))
- return new pillbottle(loc)
+ item = pillbottle
+ return ..()
/datum/market_item/consumable/floor_pill
name = "Strange Pill"
diff --git a/code/modules/cargo/markets/market_items/hostages.dm b/code/modules/cargo/markets/market_items/hostages.dm
new file mode 100644
index 00000000000..6551ee6156b
--- /dev/null
+++ b/code/modules/cargo/markets/market_items/hostages.dm
@@ -0,0 +1,84 @@
+///A special category for mobs captured by pirates, tots and contractors, should someone ever want to get them back in advance.
+/datum/market_item/hostage
+ category = "Hostages"
+ stock = 1
+ availability_prob = 100
+ shipping_override = list(SHIPPING_METHOD_LTSRBT = 0, SHIPPING_METHOD_SUPPLYPOD = 350)
+ /// temporary reference to the 4 in 7 chances of signaler and electropack.
+ var/obj/item/assembly/signaler/signaler
+
+/datum/market_item/hostage/New(mob/living/mob, new_price)
+ ..()
+ set_item(mob)
+ name = "[mob.real_name]"
+ var/specimen = initial(mob.name)
+ var/humie_mob = ishuman(mob)
+ if(humie_mob)
+ var/mob/living/carbon/human/humie = mob
+ specimen = humie.dna.species.name
+ desc = pick(list(
+ "If you're looking for a recently stolen [specimen], you've come to the right place.",
+ "we've recently aquired a fine [specimen] from a station around here, eheh...",
+ "For a limited time, we're offering this [specimen] for you to buy (back).",
+ ))
+ desc += " DISCLAIMER: The offer will expire once the creature is returned to the station."
+ if(humie_mob)
+ desc += "[mob.p_they(TRUE)] may be delivered handcuffed, for safety of course."
+
+ price = new_price
+ RegisterSignal(mob, COMSIG_LIVING_RETURN_FROM_CAPTURE, PROC_REF(on_return_from_capture))
+
+/datum/market_item/hostage/proc/on_return_from_capture(mob/living/source, turf/destination)
+ SIGNAL_HANDLER
+ qdel(src) //as if we never existed, our mentions we'll be removed from the market.
+
+/datum/market_item/hostage/Destroy()
+ signaler = null
+ return ..()
+
+/datum/market_item/hostage/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method)
+ . = ..()
+ var/mob/living/humie = item
+ if(!. || !istype(humie) || !prob(57)) // 3 in 7 chance of the electropack set not spawning...
+ return
+ signaler = new(uplink.drop_location())
+ RegisterSignal(signaler, COMSIG_QDELETING, PROC_REF(clear_signaler_ref))
+ signaler.set_frequency(sanitize_frequency(rand(MIN_FREE_FREQ, MAX_FREE_FREQ)))
+ signaler.code = rand(1, 100)
+ buyer.put_in_hands(signaler)
+ to_chat(buyer, span_notice("A [signaler] appears [buyer.is_holding(signaler) ? "in your hands" : "at your feet"]!"))
+
+/datum/market_item/hostage/proc/clear_signaler_ref(datum/source)
+ SIGNAL_HANDLER
+ signaler = null
+
+/datum/market_item/hostage/spawn_item(loc, datum/market_purchase/purchase)
+ var/mob/living/mob = item
+ UnregisterSignal(mob, COMSIG_LIVING_RETURN_FROM_CAPTURE)
+ if(!mob.IsUnconscious())
+ to_chat(mob, span_boldnicegreen("You have been bought back to the station. Be grateful to whoever got you out of the holding facility early."))
+ if(!ishuman(item))
+ return ..()
+ var/mob/living/carbon/human/humie = item
+ if(signaler) //57% chance that the mob is equipped with electropack and cuffs
+ humie.equip_to_slot_or_del(new /obj/item/restraints/handcuffs, ITEM_SLOT_HANDCUFFED, indirect_action = TRUE)
+ if(humie.back) //try to remove the neck item if possible before we attempt to install the collar bomb
+ humie.transferItemToLoc(humie.back, loc)
+ var/obj/item/electropack/pack = new(loc)
+ pack.set_frequency(signaler.frequency)
+ pack.code = signaler.code
+ humie.equip_to_slot_if_possible(pack, ITEM_SLOT_BACK, disable_warning = TRUE)
+ UnregisterSignal(signaler, COMSIG_QDELETING)
+ signaler = null
+ else if(prob(66)) // 29% chance of just cuffs
+ humie.equip_to_slot_or_del(new /obj/item/restraints/handcuffs, ITEM_SLOT_HANDCUFFED, indirect_action = TRUE)
+ else // 14% chance of just a tee souvenir and pin, no cuffs and shit.
+ var/obj/item/clothing/under/misc/syndicate_souvenir/souvenir = new(loc)
+ humie.equip_to_slot_if_possible(souvenir, ITEM_SLOT_ICLOTHING, indirect_action = TRUE)
+ var/obj/item/clothing/accessory/anti_sec_pin/pin = new(loc)
+ pin.attach(souvenir)
+
+ if(isnull(humie.w_uniform))
+ //FUCKING SLAVES, GET YOUR CLOTHES BACK ON!
+ humie.equip_to_slot_or_del(new /obj/item/clothing/under/costume/jabroni(humie), ITEM_SLOT_ICLOTHING, indirect_action = TRUE)
+ return ..()
diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm
index 0953a73e0ac..de0fcaa9256 100644
--- a/code/modules/cargo/markets/market_items/misc.dm
+++ b/code/modules/cargo/markets/market_items/misc.dm
@@ -68,9 +68,11 @@
stock_max = 3
availability_prob = 40
-/datum/market_item/misc/holywater/spawn_item(loc)
+/datum/market_item/misc/holywater/spawn_item(loc, datum/market_purchase/purchase)
if (prob(6.66))
- return new /obj/item/reagent_containers/cup/beaker/unholywater(loc)
+ item = /obj/item/reagent_containers/cup/beaker/unholywater
+ else
+ item = initial(item)
return ..()
/datum/market_item/misc/strange_seed
diff --git a/code/modules/cargo/markets/market_items/stolen_goods.dm b/code/modules/cargo/markets/market_items/stolen_goods.dm
new file mode 100644
index 00000000000..c9c17f1d2b6
--- /dev/null
+++ b/code/modules/cargo/markets/market_items/stolen_goods.dm
@@ -0,0 +1,12 @@
+///A special category for goods stolen by spies for their bounties.
+/datum/market_item/stolen_good
+ category = "Fenced Goods"
+ stock = 1
+ availability_prob = 100
+
+/datum/market_item/stolen_good/New(atom/movable/thing, thing_price)
+ ..()
+ set_item(thing)
+ name = "Stolen [thing.name]"
+ desc = "A [thing.name], stolen from somewhere on the station. Whoever owned it probably wouldn't be happy to see it here."
+ price = thing_price
diff --git a/code/modules/cargo/markets/market_items/tools.dm b/code/modules/cargo/markets/market_items/tools.dm
index c20834e640b..5d036fae0ef 100644
--- a/code/modules/cargo/markets/market_items/tools.dm
+++ b/code/modules/cargo/markets/market_items/tools.dm
@@ -1,6 +1,18 @@
/datum/market_item/tool
category = "Tools"
+/datum/market_item/tool/blackmarket_telepad
+ name = "Black Market LTSRBT"
+ desc = "Need a faster and better way of transporting your illegal goods from and to the \
+ station? Fear not, the Long-To-Short-Range-Bluespace-Transceiver is here to help. \
+ Contains a LTSRBT circuit. Bluespace crystals and ansible not included."
+ item = /obj/item/circuitboard/machine/ltsrbt
+ stock_min = 2
+ stock_max = 4
+ price_min = CARGO_CRATE_VALUE * 2.5
+ price_max = CARGO_CRATE_VALUE * 3.75
+ availability_prob = 100
+
/datum/market_item/tool/caravan_wrench
name = "Experimental Wrench"
desc = "The extra fast and handy wrench you always wanted!"
diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm
index e99e4b88d22..7a3365d7a0f 100644
--- a/code/modules/cargo/markets/market_telepad.dm
+++ b/code/modules/cargo/markets/market_telepad.dm
@@ -26,13 +26,13 @@
/// The time it takes for the machine to recharge before being able to send or receive items.
var/recharge_time = 0
/// Current recharge progress.
- var/recharge_cooldown = 0
+ COOLDOWN_DECLARE(recharge_cooldown)
/// Base recharge time in seconds which is used to get recharge_time.
var/base_recharge_time = 100
/// Current /datum/market_purchase being received.
- var/receiving
+ var/datum/market_purchase/receiving
/// Current /datum/market_purchase being sent to the target uplink.
- var/transmitting
+ var/datum/market_purchase/transmitting
/// Queue for purchases that the machine should receive and send.
var/list/datum/market_purchase/queue = list()
@@ -67,46 +67,48 @@
/obj/machinery/ltsrbt/proc/add_to_queue(datum/market_purchase/purchase)
if(!recharge_cooldown && !receiving && !transmitting)
receiving = purchase
- return
- queue += purchase
+ else
+ queue += purchase
+ RegisterSignal(receiving, COMSIG_QDELETING, PROC_REF(on_receiving_del))
+
+/obj/machinery/ltsrbt/proc/on_receiving_del(datum/market_purchase/purchase)
+ SIGNAL_HANDLER
+ queue -= purchase
+ if(receiving == purchase)
+ receiving = null
+ if(transmitting == purchase)
+ transmitting = null
/obj/machinery/ltsrbt/process(seconds_per_tick)
if(machine_stat & NOPOWER)
return
- if(recharge_cooldown > 0)
- recharge_cooldown -= seconds_per_tick
+ if(!COOLDOWN_FINISHED(src, recharge_cooldown) && isnull(receiving) && isnull(transmitting))
return
- var/turf/T = get_turf(src)
+ COOLDOWN_START(src, recharge_cooldown, recharge_time)
+
+ var/turf/turf = get_turf(src)
if(receiving)
- var/datum/market_purchase/P = receiving
- P.item = P.entry.spawn_item(T)
+ receiving.item = receiving.entry.spawn_item(turf, receiving)
use_power(power_usage_per_teleport / power_efficiency)
var/datum/effect_system/spark_spread/sparks = new
sparks.set_up(5, 1, get_turf(src))
- sparks.attach(P.item)
+ sparks.attach(receiving.item)
sparks.start()
+ transmitting = receiving
receiving = null
- transmitting = P
recharge_cooldown = recharge_time
return
- else if(transmitting)
- var/datum/market_purchase/P = transmitting
- if(!P.item)
- QDEL_NULL(transmitting)
- if(!(P.item in T.contents))
- QDEL_NULL(transmitting)
- return
- do_teleport(P.item, get_turf(P.uplink))
- use_power(power_usage_per_teleport / power_efficiency)
+ if(transmitting)
+ if(transmitting.item.loc == turf)
+ do_teleport(transmitting.item, get_turf(transmitting.uplink))
+ use_power(power_usage_per_teleport / power_efficiency)
QDEL_NULL(transmitting)
-
- recharge_cooldown = recharge_time
return
if(queue.len)
diff --git a/code/modules/cargo/markets/market_uplink.dm b/code/modules/cargo/markets/market_uplink.dm
index a82218082e9..da86161e46a 100644
--- a/code/modules/cargo/markets/market_uplink.dm
+++ b/code/modules/cargo/markets/market_uplink.dm
@@ -9,7 +9,7 @@
var/viewing_category
/// What market is currently being bought from by the uplink?
var/viewing_market
- /// What item is the current uplink attempting to buy?
+ /// the identifier of the item that the current uplink is attempting to buy
var/selected_item
/// Is the uplink in the process of buying the selected item?
var/buying
@@ -56,25 +56,31 @@
current_user = null
data["categories"] = market ? market.categories : null
data["delivery_methods"] = list()
- if(market)
- for(var/delivery in market.shipping)
- data["delivery_methods"] += list(list("name" = delivery, "price" = market.shipping[delivery]))
data["money"] = "N/A cr"
if(current_user)
data["money"] = current_user.account_balance
data["buying"] = buying
+ if(buying && market)
+ var/datum/market_item/target_item = market.available_items[viewing_category][selected_item]
+ var/list/shipping_list = market.shipping
+ if(length(target_item?.shipping_override))
+ shipping_list = target_item.shipping_override
+ for(var/delivery in shipping_list)
+ UNTYPED_LIST_ADD(data["delivery_methods"], list("name" = delivery, "price" = shipping_list[delivery]))
data["items"] = list()
- data["viewing_category"] = viewing_category
+ data["viewing_category"] = market.categories[viewing_category] ? viewing_category : null
data["viewing_market"] = viewing_market
if(viewing_category && market)
if(market.available_items[viewing_category])
- for(var/datum/market_item/I in market.available_items[viewing_category])
+ var/list/market_category = market.available_items[viewing_category]
+ for(var/id in market_category)
+ var/datum/market_item/item = market_category[id]
data["items"] += list(list(
- "id" = I.type,
- "name" = I.name,
- "cost" = I.price,
- "amount" = I.stock,
- "desc" = I.desc || I.name
+ "id" = id,
+ "name" = item.name,
+ "cost" = item.price,
+ "amount" = item.stock,
+ "desc" = item.desc || item.name
))
return data
@@ -123,8 +129,7 @@
if("select")
if(isnull(params["item"]))
return
- var/item = text2path(params["item"])
- selected_item = item
+ selected_item = params["item"]
buying = TRUE
. = TRUE
if("cancel")
diff --git a/code/modules/cargo/packs/imports.dm b/code/modules/cargo/packs/imports.dm
index 781205eb03b..94849d61ef5 100644
--- a/code/modules/cargo/packs/imports.dm
+++ b/code/modules/cargo/packs/imports.dm
@@ -118,19 +118,6 @@
if(prob(10)) //A little extra sugar every now and then to shake things up.
new /obj/item/switchblade(our_crate)
-/datum/supply_pack/imports/blackmarket_telepad
- name = "Black Market LTSRBT"
- desc = "Need a faster and better way of transporting your illegal goods from and to the \
- station? Fear not, the Long-To-Short-Range-Bluespace-Transceiver (LTSRBT for short) \
- is here to help. Contains a LTSRBT circuit, two bluespace crystals, and one ansible."
- cost = CARGO_CRATE_VALUE * 20
- contraband = TRUE
- contains = list(
- /obj/item/circuitboard/machine/ltsrbt,
- /obj/item/stack/ore/bluespace_crystal/artificial = 2,
- /obj/item/stock_parts/subspace/ansible,
- )
-
/datum/supply_pack/imports/contraband
name = "'Contraband' Crate"
desc = "Psst.. bud... want some contraband? I can get you a poster, some nice cigs, dank, even some \
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 58c4298ac0e..84ce7b03609 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -70,6 +70,15 @@
bluespace = TRUE
explosionSize = list(0,0,0,0)
+/obj/structure/closet/supplypod/podspawn/deathmatch
+ desc = "A blood-red styled drop pod."
+ specialised = TRUE
+
+/obj/structure/closet/supplypod/podspawn/deathmatch/preOpen()
+ for(var/mob/living/critter in contents)
+ critter.faction = list(FACTION_HOSTILE) //No infighting, but also KILL!!
+ return ..()
+
/obj/structure/closet/supplypod/extractionpod
name = "Syndicate Extraction Pod"
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas. Targets must be manually stuffed inside the pod for proper delivery."
@@ -90,6 +99,29 @@
delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+/obj/structure/closet/supplypod/back_to_station
+ name = "blood-red supply pod"
+ desc = "An intimidating supply pod, covered in the blood-red markings"
+ bluespace = TRUE
+ explosionSize = list(0,0,0,0)
+ style = STYLE_SYNDICATE
+ specialised = TRUE
+
+/obj/structure/closet/supplypod/deadmatch_missile
+ name = "cruise missile"
+ desc = "A big ass missile, likely launched from some far-off deep space missile silo."
+ icon_state = "smissile"
+ decal = null
+ door = null
+ fin_mask = null
+ explosionSize = list(0,1,2,2)
+ effectShrapnel = TRUE
+ rubble_type = RUBBLE_THIN
+ specialised = TRUE
+ delays = list(POD_TRANSIT = 2.6 SECONDS, POD_FALLING = 0.4 SECONDS)
+ effectMissile = TRUE
+ shrapnel_type = /obj/projectile/bullet/shrapnel/short_range
+
/datum/armor/closet_supplypod
melee = 30
bullet = 50
@@ -213,6 +245,24 @@
/obj/structure/closet/supplypod/open(mob/living/user, force = FALSE, special_effects = TRUE)
return
+///Called by the drop pods that return captured crewmembers from the ninja den.
+/obj/structure/closet/supplypod/proc/return_from_capture(mob/living/victim, turf/destination = get_safe_random_station_turf())
+ if(isnull(destination)) //Uuuuh, something went wrong. This is gonna hurt.
+ to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Seems where you got sent won't \
+ be able to handle our pod... as if we wanted the occupant to survive. Brace yourself, corporate dog.\""))
+ flags_1 &= ~PREVENT_CONTENTS_EXPLOSION_1
+ explosionSize = list(0,1,1,1)
+ destination = get_random_station_turf()
+
+ do_sparks(8, FALSE, victim)
+ victim.visible_message(span_notice("[victim] vanishes..."))
+
+ victim.forceMove(src)
+
+ new /obj/effect/pod_landingzone(destination, src)
+
+ SEND_SIGNAL(victim, COMSIG_LIVING_RETURN_FROM_CAPTURE, destination)
+
/obj/structure/closet/supplypod/proc/handleReturnAfterDeparting(atom/movable/holder = src)
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open_pod() )
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
diff --git a/code/modules/client/preferences/hemiplegic.dm b/code/modules/client/preferences/hemiplegic.dm
new file mode 100644
index 00000000000..923a6f4acdf
--- /dev/null
+++ b/code/modules/client/preferences/hemiplegic.dm
@@ -0,0 +1,17 @@
+/datum/preference/choiced/hemiplegic
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "hemiplegic"
+ savefile_identifier = PREFERENCE_CHARACTER
+
+/datum/preference/choiced/hemiplegic/init_possible_values()
+ return list("Random") + GLOB.side_choice_hemiplegic
+
+/datum/preference/choiced/hemiplegic/is_accessible(datum/preferences/preferences)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ return "Hemiplegic" in preferences.all_quirks
+
+/datum/preference/choiced/hemiplegic/apply_to_human(mob/living/carbon/human/target, value)
+ return
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 63424cf7a9e..5f68e8f0022 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -54,7 +54,7 @@
/obj/item/clothing/Initialize(mapload)
if(clothing_flags & VOICEBOX_TOGGLABLE)
- actions_types += /datum/action/item_action/toggle_voice_box
+ actions_types += list(/datum/action/item_action/toggle_voice_box)
. = ..()
AddElement(/datum/element/venue_price, FOOD_PRICE_CHEAP)
if(can_be_bloody && ((body_parts_covered & FEET) || (flags_inv & HIDESHOES)))
diff --git a/code/modules/clothing/masks/bandana.dm b/code/modules/clothing/masks/bandana.dm
index a6719556b3f..cafe5d397f2 100644
--- a/code/modules/clothing/masks/bandana.dm
+++ b/code/modules/clothing/masks/bandana.dm
@@ -211,7 +211,7 @@
/obj/item/clothing/mask/facescarf
name = "facescarf"
desc = "Cover your face like in the cowboy movies. It also has breathtube so you can wear it everywhere!"
- actions_types = /datum/action/item_action/adjust
+ actions_types = list(/datum/action/item_action/adjust)
icon_state = "facescarf"
inhand_icon_state = "greyscale_facescarf"
alternate_worn_layer = BACK_LAYER
diff --git a/code/modules/clothing/neck/collar_bomb.dm b/code/modules/clothing/neck/collar_bomb.dm
new file mode 100644
index 00000000000..72f5b20320d
--- /dev/null
+++ b/code/modules/clothing/neck/collar_bomb.dm
@@ -0,0 +1,118 @@
+///Special neckwear that kills its wearer if triggered, by either its specific remote or assemblies.
+/obj/item/clothing/neck/collar_bomb
+ name = "collar bomb"
+ desc = "A cumbersome collar of some sort, filled with just enough explosive to rip one's head off... at least that's what it reads on the front tag."
+ icon_state = "collar_bomb"
+ icon = 'icons/obj/clothing/neck.dmi'
+ inhand_icon_state = "reverse_bear_trap"
+ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+ clothing_flags = INEDIBLE_CLOTHING
+ armor_type = /datum/armor/collar_bomb
+ equip_delay_self = 6 SECONDS
+ equip_delay_other = 8 SECONDS
+ ///The button it's associated with
+ var/obj/item/collar_bomb_button/button
+ ///Whether the collar countdown has been triggered.
+ var/active = FALSE
+
+/datum/armor/collar_bomb
+ fire = 97
+ bomb = 97
+ acid = 97
+
+/obj/item/clothing/neck/collar_bomb/Initialize(mapload, obj/item/collar_bomb_button/button)
+ . = ..()
+ src.button = button
+ button?.collar = src
+ set_wires(new /datum/wires/collar_bomb(src))
+
+/obj/item/clothing/neck/collar_bomb/Destroy()
+ button?.collar = null
+ button = null
+ return ..()
+
+/obj/item/clothing/neck/collar_bomb/examine(mob/user)
+ . = ..()
+ if(user.get_item_by_slot(ITEM_SLOT_NECK) == src)
+ return
+ . += span_tinynotice("It has a [EXAMINE_HINT("wire")] panel that could be interacted with...")
+
+/obj/item/clothing/neck/collar_bomb/attackby(obj/item/item, mob/user, params)
+ if(is_wire_tool(item))
+ wires.interact(user)
+ else
+ return ..()
+
+/obj/item/clothing/neck/collar_bomb/equipped(mob/user, slot, initial = FALSE)
+ . = ..()
+ if(slot == ITEM_SLOT_NECK)
+ ADD_TRAIT(src, TRAIT_NODROP, INNATE_TRAIT)
+
+/obj/item/clothing/neck/collar_bomb/dropped(mob/user, silent = FALSE)
+ . = ..()
+ REMOVE_TRAIT(src, TRAIT_NODROP, INNATE_TRAIT)
+
+/obj/item/clothing/neck/collar_bomb/proc/explosive_countdown(ticks_left)
+ active = TRUE
+ if(ticks_left > 0)
+ playsound(src, 'sound/items/timer.ogg', 30, FALSE)
+ balloon_alert_to_viewers("[ticks_left]")
+ ticks_left--
+ addtimer(CALLBACK(src, PROC_REF(explosive_countdown), ticks_left), 1 SECONDS)
+ return
+
+ playsound(src, 'sound/effects/snap.ogg', 75, TRUE)
+ if(!ishuman(loc))
+ balloon_alert_to_viewers("dud...")
+ active = FALSE
+ return
+ var/mob/living/carbon/human/brian = loc
+ if(brian.get_item_by_slot(ITEM_SLOT_NECK) != src)
+ balloon_alert_to_viewers("dud...")
+ active = FALSE
+ return
+ visible_message(span_warning("[src] goes off, outright decapitating [brian]!"), span_hear("You hear a fleshy boom!"))
+ playsound(src, SFX_EXPLOSION, 30, TRUE)
+ brian.apply_damage(200, BRUTE, BODY_ZONE_HEAD)
+ var/obj/item/bodypart/head/myhead = brian.get_bodypart(BODY_ZONE_HEAD)
+ myhead?.dismember()
+ brian.investigate_log("has been decapitated by [src].", INVESTIGATE_DEATHS)
+ flash_color(brian, flash_color = "#FF0000", flash_time = 1 SECONDS)
+ qdel(src)
+
+///The button that detonates the collar.
+/obj/item/collar_bomb_button
+ name = "big yellow button"
+ desc = "It looks like a big red button, except it's yellow. It comes with a heavy trigger, to avoid accidents."
+ icon = 'icons/obj/devices/assemblies.dmi'
+ icon_state = "bigyellow"
+ inhand_icon_state = "electronic"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ w_class = WEIGHT_CLASS_TINY
+ ///The collar bomb it's associated with.
+ var/obj/item/clothing/neck/collar_bomb/collar
+
+/obj/item/collar_bomb_button/attack_self(mob/user)
+ . = ..()
+ if(DOING_INTERACTION_WITH_TARGET(user, src))
+ return
+ balloon_alert_to_viewers("pushing the button...")
+ if(!do_after(user, 1.2 SECONDS, target = src))
+ return
+ playsound(user, 'sound/machines/click.ogg', 25, TRUE)
+ if(!collar|| collar.active)
+ return
+ collar.explosive_countdown(ticks_left = 5)
+ if(!ishuman(collar.loc))
+ return
+ var/mob/living/carbon/human/brian = collar.loc
+ if(brian.get_item_by_slot(ITEM_SLOT_NECK) == collar)
+ brian.investigate_log("has has their [collar] triggered by [user] via yellow button.", INVESTIGATE_DEATHS)
+
+
+/obj/item/collar_bomb_button/Destroy()
+ collar?.button = null
+ collar = null
+ return ..()
diff --git a/code/modules/clothing/suits/wintercoats.dm b/code/modules/clothing/suits/wintercoats.dm
index 7249a40368a..de3badc2a24 100644
--- a/code/modules/clothing/suits/wintercoats.dm
+++ b/code/modules/clothing/suits/wintercoats.dm
@@ -521,6 +521,9 @@
/obj/item/pipe_dispenser,
/obj/item/storage/bag/construction,
/obj/item/t_scanner,
+ /obj/item/construction/rld,
+ /obj/item/construction/rtd,
+ /obj/item/gun/ballistic/rifle/rebarxbow
)
armor_type = /datum/armor/wintercoat_engineering
hoodtype = /obj/item/clothing/head/hooded/winterhood/engineering
diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm
index 94bd910ae0a..0acc319df79 100644
--- a/code/modules/clothing/under/accessories/badges.dm
+++ b/code/modules/clothing/under/accessories/badges.dm
@@ -218,8 +218,8 @@ GLOBAL_LIST_INIT(pride_pin_reskins, list(
/obj/item/clothing/accessory/anti_sec_pin/attach(obj/item/clothing/under/attach_to, mob/living/attacher)
. = ..()
- if (!.)
- return FALSE
+ if (!. || isnull(attacher))
+ return
var/target = ishuman(attach_to.loc) ? attach_to.loc : attach_to
log_combat(attacher, target, "pinned an 'arrest me immediately' pin onto", src)
diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm
index 4e648a040d6..703df15edf3 100644
--- a/code/modules/clothing/under/costume.dm
+++ b/code/modules/clothing/under/costume.dm
@@ -383,10 +383,25 @@
name = "Martial Artist Gi"
desc = "Assistant, nukie, whatever. You can beat anyone; it's called hard work!"
icon_state = "martial_arts_gi"
+ greyscale_config = /datum/greyscale_config/gi
+ greyscale_config_worn = /datum/greyscale_config/gi/worn
+ greyscale_colors = "#f1eeee#000000"
+ flags_1 = IS_PLAYER_COLORABLE_1
inhand_icon_state = null
female_sprite_flags = NO_FEMALE_UNIFORM
can_adjust = FALSE
+/obj/item/clothing/under/costume/gi/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/gags_recolorable)
+ update_icon(UPDATE_OVERLAYS)
+
+/obj/item/clothing/under/costume/gi/goku
+ name = "Sacred Gi"
+ desc = "Created by a man who touched the hearts and lives of many."
+ icon_state = "martial_arts_gi_goku"
+ greyscale_colors = "#f89925#3e6dd7"
+
/obj/item/clothing/under/costume/traditional
name = "Traditional Suit"
desc = "A full, vibrantly coloured suit. Likely with traditional purposes. Maybe the colours represent a familly, clan, or rank, who knows."
diff --git a/code/modules/clothing/under/skirt_dress.dm b/code/modules/clothing/under/skirt_dress.dm
index 12a61ceaf9e..9d9677f8ebd 100644
--- a/code/modules/clothing/under/skirt_dress.dm
+++ b/code/modules/clothing/under/skirt_dress.dm
@@ -6,18 +6,31 @@
icon = 'icons/obj/clothing/under/dress.dmi'
worn_icon = 'icons/mob/clothing/under/dress.dmi'
+/obj/item/clothing/under/dress/striped/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/gags_recolorable)
+ update_icon(UPDATE_OVERLAYS)
+
/obj/item/clothing/under/dress/striped
name = "striped dress"
desc = "Fashion in space."
- icon_state = "striped_dress"
+ icon_state = "stripeddress"
inhand_icon_state = null
female_sprite_flags = FEMALE_UNIFORM_FULL
+ greyscale_config = /datum/greyscale_config/striped_dress
+ greyscale_config_worn = /datum/greyscale_config/striped_dress/worn
+ flags_1 = IS_PLAYER_COLORABLE_1
+ greyscale_colors = "#003284#000000#ffffff"
/obj/item/clothing/under/dress/sailor
name = "sailor dress"
desc = "Formal wear for a leading lady."
icon_state = "sailor_dress"
- inhand_icon_state = null
+ greyscale_config = /datum/greyscale_config/sailor_dress
+ greyscale_config_worn = /datum/greyscale_config/sailor_dress/worn
+ greyscale_colors = "#0000ff#cc0000#eaeaea"
+ inhand_icon_state = "blackskirt"
+ flags_1 = IS_PLAYER_COLORABLE_1
/obj/item/clothing/under/dress/wedding_dress
name = "wedding dress"
@@ -27,17 +40,25 @@
body_parts_covered = CHEST|GROIN|LEGS
flags_inv = HIDESHOES
-/obj/item/clothing/under/dress/redeveninggown
- name = "red evening gown"
+/obj/item/clothing/under/dress/eveninggown
+ name = "evening gown"
desc = "Fancy dress for space bar singers."
- icon_state = "red_evening_gown"
+ icon_state = "evening_gown"
inhand_icon_state = null
+ greyscale_config = /datum/greyscale_config/evening_dress
+ greyscale_config_worn = /datum/greyscale_config/evening_dress/worn
+ flags_1 = IS_PLAYER_COLORABLE_1
+ greyscale_colors = "#e11f1f"
/obj/item/clothing/under/dress/skirt
- name = "black skirt"
- desc = "A black skirt, very fancy!"
- icon_state = "blackskirt"
+ name = "cardigan skirt"
+ desc = "A nice skirt with a cute cardigan, very fancy!"
+ icon_state = "cardiganskirt"
+ greyscale_config = /datum/greyscale_config/cardiganskirt
+ greyscale_config_worn = /datum/greyscale_config/cardiganskirt/worn
+ greyscale_colors = "#bf504d#545454"
inhand_icon_state = "blackskirt"
+ flags_1 = IS_PLAYER_COLORABLE_1
/obj/item/clothing/under/dress/skirt/plaid
name = "plaid skirt"
diff --git a/code/modules/deathmatch/deathmatch_controller.dm b/code/modules/deathmatch/deathmatch_controller.dm
index 86e86f491fb..45b5f087c5b 100644
--- a/code/modules/deathmatch/deathmatch_controller.dm
+++ b/code/modules/deathmatch/deathmatch_controller.dm
@@ -5,6 +5,8 @@
var/list/datum/lazy_template/deathmatch/maps = list()
/// All loadouts
var/list/datum/outfit/loadouts
+ /// All modifiers
+ var/list/datum/deathmatch_modifier/modifiers
/datum/deathmatch_controller/New()
. = ..()
@@ -17,6 +19,7 @@
var/map_name = initial(template.name)
maps[map_name] = new template
loadouts = subtypesof(/datum/outfit/deathmatch_loadout)
+ modifiers = sortTim(init_subtypes_w_path_keys(/datum/deathmatch_modifier), GLOBAL_PROC_REF(cmp_deathmatch_mods), associative = TRUE)
/datum/deathmatch_controller/proc/create_new_lobby(mob/host)
lobbies[host.ckey] = new /datum/deathmatch_lobby(host)
diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm
index f994987427e..341b85fb926 100644
--- a/code/modules/deathmatch/deathmatch_loadouts.dm
+++ b/code/modules/deathmatch/deathmatch_loadouts.dm
@@ -17,7 +17,7 @@
if(!isnull(species_override))
user.set_species(species_override)
- else if (istype(user.dna.species.outfit_important_for_life)) //plasmamen get lit on fire and die
+ else if (!isnull(user.dna.species.outfit_important_for_life)) //plasmamen get lit on fire and die
user.set_species(/datum/species/human)
for(var/datum/action/act as anything in granted_spells)
var/datum/action/new_ability = new act(user)
diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm
index b58e85d5288..51ecab7a296 100644
--- a/code/modules/deathmatch/deathmatch_lobby.dm
+++ b/code/modules/deathmatch/deathmatch_lobby.dm
@@ -12,13 +12,17 @@
/// Whether players hear deadchat and people through walls
var/global_chat = FALSE
/// Whether the lobby is currently playing
- var/playing = FALSE
+ var/playing = DEATHMATCH_NOT_PLAYING
/// Number of total ready players
var/ready_count
/// List of loadouts, either gotten from the deathmatch controller or the map
var/list/loadouts
/// Current map player spawn locations, cleared after spawning
var/list/player_spawns = list()
+ /// A list of paths of modifiers enabled for the match.
+ var/list/modifiers = list()
+ /// Is the modifiers modal menu open (for the host)
+ var/mod_menu_open = FALSE
/datum/deathmatch_lobby/New(mob/player)
. = ..()
@@ -48,11 +52,12 @@
map = null
location = null
loadouts = null
+ modifiers = null
/datum/deathmatch_lobby/proc/start_game()
if (playing)
return
- playing = TRUE
+ playing = DEATHMATCH_PRE_PLAYING
RegisterSignal(map, COMSIG_LAZY_TEMPLATE_LOADED, PROC_REF(find_spawns_and_start_delay))
location = map.lazy_load()
@@ -78,6 +83,9 @@
playing = FALSE
return FALSE
+ for(var/modpath in modifiers)
+ GLOB.deathmatch_game.modifiers[modpath].on_start_game(src)
+
for (var/key in players)
var/mob/dead/observer/observer = players[key]["mob"]
if (isnull(observer) || !observer.client)
@@ -97,19 +105,26 @@
var/mob/observer = observers[observer_key]["mob"]
observer.forceMove(pick(location.reserved_turfs))
+ playing = DEATHMATCH_PLAYING
addtimer(CALLBACK(src, PROC_REF(game_took_too_long)), initial(map.automatic_gameend_time))
log_game("Deathmatch game [host] started.")
announce(span_reallybig("GO!"))
+ if(length(modifiers))
+ var/list/modifier_names = list()
+ for(var/datum/deathmatch_modifier/modifier as anything in modifiers)
+ modifier_names += uppertext(initial(modifier.name))
+ announce(span_boldnicegreen("THIS MATCH MODIFIERS: [english_list(modifier_names, and_text = " ,")]."))
return TRUE
/datum/deathmatch_lobby/proc/spawn_observer_as_player(ckey, loc)
- var/mob/dead/observer/observer = players[ckey]["mob"]
+ var/list/players_info = players[ckey]
+ var/mob/dead/observer/observer = players_info["mob"]
if (isnull(observer) || !observer.client)
remove_ckey_from_play(ckey)
return
// equip player
- var/datum/outfit/deathmatch_loadout/loadout = players[ckey]["loadout"]
+ var/datum/outfit/deathmatch_loadout/loadout = players_info["loadout"]
if (!(loadout in loadouts))
loadout = loadouts[1]
@@ -126,7 +141,10 @@
)
new_player.equipOutfit(loadout) // Loadout
new_player.key = ckey
- players[ckey]["mob"] = new_player
+ players_info["mob"] = new_player
+
+ for(var/datum/deathmatch_modifier/modifier as anything in modifiers)
+ GLOB.deathmatch_game.modifiers[modifier].apply(new_player, src)
// register death handling.
RegisterSignals(new_player, list(COMSIG_LIVING_DEATH, COMSIG_MOB_GHOSTIZED, COMSIG_QDELETING), PROC_REF(player_died))
@@ -163,6 +181,9 @@
loser.ghostize()
qdel(loser)
+ for(var/datum/deathmatch_modifier/modifier in modifiers)
+ GLOB.deathmatch_game.modifiers[modifier].on_end_game(src)
+
clear_reservation()
GLOB.deathmatch_game.remove_lobby(host)
log_game("Deathmatch game [host] ended.")
@@ -192,7 +213,8 @@
announce(span_reallybig("[player.real_name] HAS DIED.
[players.len] REMAIN."))
if(!gibbed && !QDELING(player)) // for some reason dusting or deleting in chasm storage messes up tgui bad
- player.dust(TRUE, TRUE, TRUE)
+ if(!HAS_TRAIT(src, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
+ player.dust(TRUE, TRUE, TRUE)
if (players.len <= 1)
end_game()
@@ -282,6 +304,9 @@
continue
players[player_key]["loadout"] = loadouts[1]
+ for(var/deathmatch_mod in modifiers)
+ GLOB.deathmatch_game.modifiers[deathmatch_mod].on_map_changed(src)
+
/datum/deathmatch_lobby/proc/clear_reservation()
if(isnull(location) || isnull(map))
return
@@ -317,11 +342,15 @@
for (var/map_key in GLOB.deathmatch_game.maps)
.["maps"] += map_key
+
/datum/deathmatch_lobby/ui_data(mob/user)
. = list()
+ var/is_player = !isnull(players[user.ckey])
+ var/is_host = (user.ckey == host)
+ var/is_admin = check_rights_for(user.client, R_ADMIN)
.["self"] = user.ckey
- .["host"] = (user.ckey == host)
- .["admin"] = check_rights_for(user.client, R_ADMIN)
+ .["host"] = is_host
+ .["admin"] = is_admin
.["global_chat"] = global_chat
.["playing"] = playing
.["loadouts"] = list("Randomize")
@@ -333,7 +362,27 @@
.["map"]["time"] = map.automatic_gameend_time
.["map"]["min_players"] = map.min_players
.["map"]["max_players"] = map.max_players
- if(!isnull(players[user.ckey]) && !isnull(players[user.ckey]["loadout"]))
+
+ .["mod_menu_open"] = FALSE
+ if((is_host || is_admin) && mod_menu_open)
+ .["mod_menu_open"] = TRUE
+ for(var/modpath in GLOB.deathmatch_game.modifiers)
+ var/datum/deathmatch_modifier/mod = GLOB.deathmatch_game.modifiers[modpath]
+ .["modifiers"] += list(list(
+ "name" = mod.name,
+ "desc" = mod.description,
+ "modpath" = "[modpath]",
+ "selected" = (modpath in modifiers),
+ "selectable" = is_host && mod.selectable(src),
+ ))
+ .["active_mods"] = "No modifiers selected"
+ if(length(modifiers))
+ var/list/mod_names = list()
+ for(var/datum/deathmatch_modifier/modpath as anything in modifiers)
+ mod_names += initial(modpath.name)
+ .["active_mods"] = "Selected modifiers: [english_list(mod_names)]"
+
+ if(is_player && !isnull(players[user.ckey]["loadout"]))
var/datum/outfit/deathmatch_loadout/loadout = players[user.ckey]["loadout"]
.["loadoutdesc"] = initial(loadout.desc)
else
@@ -442,6 +491,31 @@
if ("global_chat")
global_chat = !global_chat
return TRUE
+ if("open_mod_menu")
+ mod_menu_open = TRUE
+ return TRUE
+ if("exit_mod_menu")
+ mod_menu_open = FALSE
+ return TRUE
+ if("toggle_modifier")
+ var/datum/deathmatch_modifier/modpath = text2path(params["modpath"])
+ if(!ispath(modpath))
+ return TRUE
+ var/global_mod = params["global_mod"]
+ if(global_mod)
+ if(usr.ckey != host && !check_rights(R_ADMIN))
+ return TRUE
+ else if(!(usr.ckey in players))
+ return TRUE
+ var/datum/deathmatch_modifier/chosen_modifier = GLOB.deathmatch_game.modifiers[modpath]
+ if(modpath in modifiers)
+ chosen_modifier.unselect(src)
+ modifiers -= modpath
+ return TRUE
+ else if(chosen_modifier.selectable(src))
+ chosen_modifier.on_select(src)
+ modifiers += modpath
+ return TRUE
if ("admin") // Admin functions
if (!check_rights(R_ADMIN))
message_admins("[usr.key] has attempted to use admin functions in a deathmatch lobby!")
@@ -452,4 +526,7 @@
log_admin("[key_name(usr)] force started deathmatch lobby [host].")
start_game()
-
+/datum/deathmatch_lobby/ui_close(mob/user)
+ . = ..()
+ if(user.ckey == host)
+ mod_menu_open = FALSE
diff --git a/code/modules/deathmatch/deathmatch_mapping.dm b/code/modules/deathmatch/deathmatch_mapping.dm
index 9f6a5b4f594..c3e10279363 100644
--- a/code/modules/deathmatch/deathmatch_mapping.dm
+++ b/code/modules/deathmatch/deathmatch_mapping.dm
@@ -2,7 +2,7 @@
name = "Deathmatch Arena"
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
- area_flags = UNIQUE_AREA | NOTELEPORT | ABDUCTOR_PROOF | EVENT_PROTECTED
+ area_flags = UNIQUE_AREA | NOTELEPORT | ABDUCTOR_PROOF | EVENT_PROTECTED | QUIET_LOGS
/area/deathmatch/fullbright
static_lighting = FALSE
diff --git a/code/modules/deathmatch/deathmatch_maps.dm b/code/modules/deathmatch/deathmatch_maps.dm
index 71d7e8a8651..e594ab24fcb 100644
--- a/code/modules/deathmatch/deathmatch_maps.dm
+++ b/code/modules/deathmatch/deathmatch_maps.dm
@@ -1,11 +1,13 @@
/datum/lazy_template/deathmatch //deathmatch maps that have any possibility of the walls being destroyed should use indestructible walls, because baseturf moment
- map_dir = "_maps/map_files/Deathmatch"
+ map_dir = "_maps/deathmatch"
place_on_top = TRUE
/// Map UI Name
var/name
/// Map Description
var/desc = ""
+ /// Minimum players for this map
var/min_players = 2
+ /// Maximum players for this map
var/max_players = 2 // TODO: make this automatic.
/// The map will end in this time
var/automatic_gameend_time = 8 MINUTES
@@ -26,24 +28,24 @@
desc = "Dark maintenance tunnels, floor pills, improvised weaponry and a bloody beatdown. Welcome to assistant utopia."
max_players = 8
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/assistant)
- map_name = "Maint_Mania"
- key = "Maint_Mania"
+ map_name = "maint_mania"
+ key = "maint_mania"
/datum/lazy_template/deathmatch/osha_violator
name = "OSHA Violator"
desc = "What would Engineering be without an overly complicated engine, with conveyor belts, emitters and shield generators sprinkled about? That's right, not Engineering."
max_players = 10
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/assistant)
- map_name = "OSHA_Violator"
- key = "OSHA_Violator"
+ map_name = "OSHA_violator"
+ key = "OSHA_violator"
/datum/lazy_template/deathmatch/the_brig
name = "The Brig"
desc = "A recreation of MetaStation Brig."
max_players = 12
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/assistant)
- map_name = "The_Brig"
- key = "The_Brig"
+ map_name = "meta_brig"
+ key = "meta_brig"
/datum/lazy_template/deathmatch/shooting_range
name = "Shooting Range"
@@ -61,8 +63,8 @@
desc = "Presenting the Security Ring, ever wanted to shoot people with disablers? Well now you can."
max_players = 4
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/securing_sec)
- map_name = "SecuRing"
- key = "SecuRing"
+ map_name = "secu_ring"
+ key = "secu_ring"
/datum/lazy_template/deathmatch/instagib
name = "Instagib"
@@ -80,29 +82,29 @@
map_name = "mech_madness"
key = "mech_madness"
-/datum/lazy_template/deathmatch/sniperelite
+/datum/lazy_template/deathmatch/sniper_elite
name = "Sniper Elite"
desc = "Sound of gunfire and screaming people make my day"
max_players = 8
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/operative/sniper)
- map_name = "Sniper_elite"
- key = "Sniper_elite"
+ map_name = "sniper_elite"
+ key = "sniper_elite"
/datum/lazy_template/deathmatch/meatower
name = "Meat Tower"
desc = "There can only be one chef in this kitchen"
max_players = 8
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/chef)
- map_name = "meatower"
- key = "meatower"
+ map_name = "meat_tower"
+ key = "meat_tower"
/datum/lazy_template/deathmatch/sunrise
name = "Sunrise"
desc = "DEHUMANIZE YOURSELF AND FACE TO BLOODSHED DEHUMANIZE YOURSELF AND FACE TO BLOODSHED DEHUMANIZE YOURSELF AND FACE TO BLOODSHED DEHUMANIZE YOURSELF AND FACE TO BLOODSHED"
max_players = 8
allowed_loadouts = list(/datum/outfit/deathmatch_loadout/samurai)
- map_name = "chinatown"
- key = "chinatown"
+ map_name = "sunrise"
+ key = "sunrise"
/datum/lazy_template/deathmatch/starwars
name = "Arena Station"
@@ -124,10 +126,10 @@
/datum/outfit/deathmatch_loadout/battler/tgcoder,
/datum/outfit/deathmatch_loadout/naked,
)
- map_name = "starwars"
- key = "starwars"
+ map_name = "arena_station"
+ key = "arena_station"
-/datum/lazy_template/deathmatch/arenaplatform
+/datum/lazy_template/deathmatch/underground_thunderdome
name = "Underground Thunderdome"
desc = "An illegal underground thunderdome, for larger amounts of murder."
max_players = 15
@@ -135,10 +137,10 @@
/datum/outfit/deathmatch_loadout/operative,
/datum/outfit/deathmatch_loadout/naked,
)
- map_name = "arena"
- key = "arena"
+ map_name = "underground_arena"
+ key = "underground_arena"
-/datum/lazy_template/deathmatch/raidthebase
+/datum/lazy_template/deathmatch/backalley
name = "Backalley"
desc = "You are not built for these streets."
max_players = 8
@@ -146,5 +148,5 @@
/datum/outfit/deathmatch_loadout/assistant,
/datum/outfit/deathmatch_loadout/naked,
)
- map_name = "raidthebase"
- key = "raidthebase"
+ map_name = "backalley"
+ key = "backalley"
diff --git a/code/modules/deathmatch/deathmatch_modifier.dm b/code/modules/deathmatch/deathmatch_modifier.dm
new file mode 100644
index 00000000000..e3b74eb45f4
--- /dev/null
+++ b/code/modules/deathmatch/deathmatch_modifier.dm
@@ -0,0 +1,472 @@
+///Deathmatch modifiers are little options the host can choose to spice the match a bit.
+/datum/deathmatch_modifier
+ ///The name of the modifier
+ var/name = "Unnamed Modifier"
+ ///A small description/tooltip shown in the UI
+ var/description = "What the heck does this do?"
+ ///The color of the button shown in the UI
+ var/color = "blue"
+ ///A list of modifiers this is incompatible with.
+ var/list/blacklisted_modifiers
+ ///Is this trait exempted from the "Random Modifiers" modifier.
+ var/random_exempted = FALSE
+
+///Whether or not this modifier can be selected, for both host and player-selected modifiers.
+/datum/deathmatch_modifier/proc/selectable(datum/deathmatch_lobby/lobby)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!random_exempted && (/datum/deathmatch_modifier/random in lobby.modifiers))
+ return FALSE
+ if(length(lobby.modifiers & blacklisted_modifiers))
+ return FALSE
+ for(var/modpath in lobby.modifiers)
+ if(src in GLOB.deathmatch_game.modifiers[modpath].blacklisted_modifiers)
+ return FALSE
+ return TRUE
+
+///Called when selecting the deathmatch modifier.
+/datum/deathmatch_modifier/proc/on_select(datum/deathmatch_lobby/lobby)
+ return
+
+///When the host changes his mind and unselects it.
+/datum/deathmatch_modifier/proc/unselect(datum/deathmatch_lobby/lobby)
+ return
+
+///Called when the host chooses to change map.
+/datum/deathmatch_modifier/proc/on_map_changed(datum/deathmatch_lobby/lobby)
+ return
+
+///Called as the game is about to start.
+/datum/deathmatch_modifier/proc/on_start_game(datum/deathmatch_lobby/lobby)
+ return
+
+///Called as the game has ended, right before the reservation is deleted.
+/datum/deathmatch_modifier/proc/on_end_game(datum/deathmatch_lobby/lobby)
+ return
+
+///Apply the modifier to the newly spawned player as the game is about to start
+/datum/deathmatch_modifier/proc/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ return
+
+/datum/deathmatch_modifier/health
+ name = "Double-Health"
+ description = "Doubles your starting health"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/health/triple)
+ var/multiplier = 2
+
+/datum/deathmatch_modifier/health/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.maxHealth *= multiplier
+ player.health *= multiplier
+
+/datum/deathmatch_modifier/health/triple
+ name = "Triple-Health"
+ description = "When \"Double-Health\" isn't enough..."
+ multiplier = 3
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/health)
+
+/datum/deathmatch_modifier/tenacity
+ name = "Tenacity"
+ description = "Unaffected by critical condition and pain"
+
+/datum/deathmatch_modifier/tenacity/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.add_traits(list(TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_ANALGESIA), DEATHMATCH_TRAIT)
+
+/datum/deathmatch_modifier/no_wounds
+ name = "No Wounds"
+ description = "Ah, the good ol' days when people did't have literal dents in their skulls..."
+
+/datum/deathmatch_modifier/no_wounds/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ ADD_TRAIT(player, TRAIT_NEVER_WOUNDED, DEATHMATCH_TRAIT)
+
+/datum/deathmatch_modifier/no_knockdown
+ name = "No Knockdowns"
+ description = "I'M FUCKING INVINCIBLE!"
+
+/datum/deathmatch_modifier/no_knockdown/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.add_traits(list(TRAIT_STUNIMMUNE, TRAIT_SLEEPIMMUNE), DEATHMATCH_TRAIT)
+
+/datum/deathmatch_modifier/xray
+ name = "X-Ray Vision"
+ description = "See through the cordons of the deathmatch arena!"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/thermal, /datum/deathmatch_modifier/echolocation)
+
+/datum/deathmatch_modifier/xray/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ ADD_TRAIT(player, TRAIT_XRAY_VISION, DEATHMATCH_TRAIT)
+ player.update_sight()
+
+/datum/deathmatch_modifier/thermal
+ name = "Thermal Vision"
+ description = "See mobs through walls"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/xray, /datum/deathmatch_modifier/echolocation)
+
+/datum/deathmatch_modifier/thermal/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ ADD_TRAIT(player, TRAIT_THERMAL_VISION, DEATHMATCH_TRAIT)
+ player.update_sight()
+
+/datum/deathmatch_modifier/regen
+ name = "Health Regen"
+ description = "The closest thing to free health insurance you can get"
+
+/datum/deathmatch_modifier/regen/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.AddComponent(/datum/component/regenerator, regeneration_delay = 4 SECONDS, brute_per_second = 2.5, burn_per_second = 2.5, tox_per_second = 2.5)
+
+/datum/deathmatch_modifier/nearsightness
+ name = "Nearsightness"
+ description = "Oops, I forgot my glasses at home"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/echolocation)
+
+/datum/deathmatch_modifier/nearsightness/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.become_nearsighted(DEATHMATCH_TRAIT)
+
+/datum/deathmatch_modifier/echolocation
+ name = "Echolocation"
+ description = "On one hand, you're blind, but on the other..."
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/nearsightness, /datum/deathmatch_modifier/xray, /datum/deathmatch_modifier/thermal)
+
+/datum/deathmatch_modifier/echolocation/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.AddComponent(/datum/component/echolocation)
+
+/datum/deathmatch_modifier/ocelot
+ name = "Ocelot"
+ description = "Shoot faster, with extra ricochet and less spread. You're pretty good!"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/stormtrooper)
+
+/datum/deathmatch_modifier/ocelot/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.add_traits(list(TRAIT_NICE_SHOT, TRAIT_DOUBLE_TAP), DEATHMATCH_TRAIT)
+ RegisterSignal(player, COMSIG_MOB_FIRED_GUN, PROC_REF(reduce_spread))
+ RegisterSignal(player, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, PROC_REF(apply_ricochet))
+
+/datum/deathmatch_modifier/ocelot/proc/reduce_spread(mob/user, obj/item/gun/gun_fired, target, params, zone_override, list/bonus_spread_values)
+ SIGNAL_HANDLER
+ bonus_spread_values[MIN_BONUS_SPREAD_INDEX] -= 50
+ bonus_spread_values[MAX_BONUS_SPREAD_INDEX] -= 50
+
+/datum/deathmatch_modifier/ocelot/proc/apply_ricochet(mob/user, obj/projectile/projectile, datum/fired_from, atom/clicked_atom)
+ SIGNAL_HANDLER
+ projectile.ricochets_max += 2
+ projectile.min_ricochets += 2
+ projectile.ricochet_incidence_leeway = 0
+ ADD_TRAIT(projectile, TRAIT_ALWAYS_HIT_ZONE, DEATHMATCH_TRAIT)
+
+/datum/deathmatch_modifier/stormtrooper
+ name = "Stormtrooper Aim"
+ description = "Fresh out of the 'I Can't Aim For Shit' School"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/ocelot)
+
+/datum/deathmatch_modifier/stormtrooper/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ RegisterSignal(player, COMSIG_MOB_FIRED_GUN, PROC_REF(increase_spread))
+
+/datum/deathmatch_modifier/stormtrooper/proc/increase_spread(mob/user, obj/item/gun/gun_fired, target, params, zone_override, list/bonus_spread_values)
+ SIGNAL_HANDLER
+ bonus_spread_values[MIN_BONUS_SPREAD_INDEX] += 10
+ bonus_spread_values[MAX_BONUS_SPREAD_INDEX] += 35
+
+/datum/deathmatch_modifier/four_hands
+ name = "Four Hands"
+ description = "When one pair isn't enough..."
+
+/datum/deathmatch_modifier/four_hands/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.change_number_of_hands(4)
+
+/datum/deathmatch_modifier/paraplegic
+ name = "Paraplegic"
+ description = "Wheelchairs. For. Everyone."
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/mounts)
+
+/datum/deathmatch_modifier/paraplegic/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic, TRAUMA_RESILIENCE_ABSOLUTE)
+ var/obj/vehicle/ridden/wheelchair/motorized/improved/wheels = new (player.loc)
+ wheels.setDir(player.dir)
+ wheels.buckle_mob(player)
+
+/datum/deathmatch_modifier/mounts
+ name = "Mounts"
+ description = "A horse! A horse! My kingdom for a horse!"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/paraplegic)
+
+/datum/deathmatch_modifier/mounts/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ ///We do a bit of fun over balance here, some mounts may be better than others.
+ var/mount_path = pick(list(
+ /mob/living/basic/carp,
+ /mob/living/basic/pony,
+ /mob/living/basic/pony/syndicate,
+ /mob/living/basic/pig,
+ /mob/living/basic/cow,
+ /mob/living/basic/cow/moonicorn,
+ /mob/living/basic/mining/wolf,
+ /mob/living/basic/mining/goldgrub,
+ /mob/living/basic/mining/goliath/deathmatch,
+ ))
+ var/mob/living/basic/mount = new mount_path (player.loc)
+ mount.tamed(player, null)
+ mount.befriend(player)
+ mount.buckle_mob(player)
+ if(HAS_TRAIT(lobby, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
+ var/obj/item/implant/explosive/deathmatch/implant = new()
+ implant.implant(mount, silent = TRUE, force = TRUE)
+
+/datum/deathmatch_modifier/no_gravity
+ name = "No Gravity"
+ description = "Hone your robusting skills in zero g"
+ blacklisted_modifiers = list(/datum/deathmatch_modifier/mounts, /datum/deathmatch_modifier/paraplegic, /datum/deathmatch_modifier/minefield)
+
+/datum/deathmatch_modifier/no_gravity/on_start_game(datum/deathmatch_lobby/lobby)
+ ASYNC
+ for(var/turf/turf as anything in lobby.location.reserved_turfs)
+ turf.AddElement(/datum/element/forced_gravity, 0)
+ CHECK_TICK
+
+/datum/deathmatch_modifier/no_gravity/on_end_game(datum/deathmatch_lobby/lobby)
+ for(var/turf/turf as anything in lobby.location.reserved_turfs)
+ turf.RemoveElement(/datum/element/forced_gravity, 0)
+
+/datum/deathmatch_modifier/drop_pod
+ name = "Drop Pod: Syndies"
+ description = "Steel Rain: Syndicate Edition"
+ ///A lazylist of lobbies that have this modifier enabled
+ var/list/signed_lobbies
+ ///The type of drop pod that'll periodically fall from the sky
+ var/drop_pod_type = /obj/structure/closet/supplypod/podspawn/deathmatch
+ ///A (weighted) list of possible contents of the drop pod. Only one is picked at a time
+ var/list/contents
+ ///An interval representing the min and max cooldown between each time it's fired.
+ var/interval = list(7 SECONDS, 12 SECONDS)
+ ///How many (a number or a two keyed list) drop pods can be dropped at a time.
+ var/amount = list(1, 2)
+ ///The cooldown for dropping pods into every affected deathmatch arena.
+ COOLDOWN_DECLARE(drop_pod_cd)
+
+/datum/deathmatch_modifier/drop_pod/New()
+ . = ..()
+ populate_contents()
+
+/datum/deathmatch_modifier/drop_pod/on_select(datum/deathmatch_lobby/lobby)
+ if(isnull(signed_lobbies))
+ START_PROCESSING(SSprocessing, src)
+ LAZYADD(signed_lobbies, lobby)
+ RegisterSignal(lobby, COMSIG_QDELETING, PROC_REF(remove_lobby))
+
+/datum/deathmatch_modifier/drop_pod/unselect(datum/deathmatch_lobby/lobby)
+ remove_lobby(lobby)
+
+/datum/deathmatch_modifier/drop_pod/proc/remove_lobby(datum/deathmatch_lobby/lobby)
+ SIGNAL_HANDLER
+ LAZYREMOVE(signed_lobbies, lobby)
+ UnregisterSignal(lobby, COMSIG_QDELETING)
+ if(isnull(signed_lobbies))
+ STOP_PROCESSING(SSprocessing, src)
+
+/datum/deathmatch_modifier/drop_pod/process(seconds_per_tick)
+ if(!COOLDOWN_FINISHED(src, drop_pod_cd))
+ return
+ var/pod_spawned = FALSE
+ for(var/datum/deathmatch_lobby/lobby as anything in signed_lobbies)
+ if(lobby.playing != DEATHMATCH_PLAYING || isnull(lobby.location))
+ continue
+ var/yet_to_spawn = islist(amount) ? rand(amount[1], amount[2]) : amount
+ for(var/attempt in 1 to 10)
+ var/turf/to_strike = pick(lobby.location.reserved_turfs)
+ if(!isopenturf(to_strike) || isgroundlessturf(to_strike))
+ continue
+ var/atom/movable/to_spawn
+ if(length(contents))
+ var/spawn_path = pick_weight(contents)
+ to_spawn = new spawn_path (to_strike)
+ if(isliving(to_spawn) && HAS_TRAIT(lobby, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
+ var/obj/item/implant/explosive/deathmatch/implant = new()
+ implant.implant(to_spawn, silent = TRUE, force = TRUE)
+ podspawn(list(
+ "path" = drop_pod_type,
+ "target" = to_strike,
+ "spawn" = to_spawn,
+ ))
+ pod_spawned = TRUE
+ yet_to_spawn--
+ if(yet_to_spawn == 0)
+ break
+
+ if(pod_spawned)
+ COOLDOWN_START(src, drop_pod_cd, rand(interval[1], interval[2]))
+
+/datum/deathmatch_modifier/drop_pod/proc/populate_contents()
+ contents = typesof(/mob/living/basic/trooper/syndicate)
+
+/datum/deathmatch_modifier/drop_pod/monsters
+ name = "Drop Pod: Monsters"
+ description = "Monsters are raining from the sky!"
+
+/datum/deathmatch_modifier/drop_pod/monsters/populate_contents()
+ contents = list(
+ /mob/living/basic/ant = 2,
+ /mob/living/basic/construct/proteon = 2,
+ /mob/living/basic/flesh_spider = 2,
+ /mob/living/basic/garden_gnome = 2,
+ /mob/living/basic/killer_tomato = 2,
+ /mob/living/basic/leaper = 1,
+ /mob/living/basic/mega_arachnid = 1,
+ /mob/living/basic/mining/goliath = 1,
+ /mob/living/basic/mining/ice_demon = 1,
+ /mob/living/basic/mining/ice_whelp = 1,
+ /mob/living/basic/mining/lobstrosity = 1,
+ /mob/living/basic/mining/mook = 2,
+ /mob/living/basic/mouse/rat = 2,
+ /mob/living/basic/spider/giant/nurse/scrawny = 2,
+ /mob/living/basic/spider/giant/tarantula/scrawny = 2,
+ /mob/living/basic/spider/giant/hunter/scrawny = 2,
+ /mob/living/simple_animal/hostile/dark_wizard = 2,
+ /mob/living/simple_animal/hostile/retaliate/goose = 2,
+ /mob/living/simple_animal/hostile/ooze = 1,
+ /mob/living/simple_animal/hostile/vatbeast = 1,
+ )
+
+/datum/deathmatch_modifier/drop_pod/missiles
+ name = "Drop Pod: Cruise Missiles"
+ description = "You're going to get shelled hard"
+ drop_pod_type = /obj/structure/closet/supplypod/deadmatch_missile
+ interval = list(3 SECONDS, 5 SECONDS)
+ amount = list(1, 3)
+
+/datum/deathmatch_modifier/drop_pod/missiles/populate_contents()
+ return
+
+/datum/deathmatch_modifier/explode_on_death
+ name = "Explosive Death"
+ description = "Everyone gets a microbomb that cannot be manually activated."
+
+/datum/deathmatch_modifier/explode_on_death/on_start_game(datum/deathmatch_lobby/lobby)
+ ADD_TRAIT(lobby, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS, DEATHMATCH_TRAIT)
+
+/datum/deathmatch_modifier/explode_on_death/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ var/obj/item/implant/explosive/deathmatch/implant = new()
+ implant.implant(player, silent = TRUE, force = TRUE)
+
+/datum/deathmatch_modifier/helgrasp
+ name = "Helgrasped"
+ description = "Cursed hands are being thrown at you!"
+
+/datum/deathmatch_modifier/helgrasp/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ var/metabolism_rate = /datum/reagent/inverse/helgrasp/heretic::metabolization_rate
+ player.reagents.add_reagent(/datum/reagent/inverse/helgrasp/heretic, initial(lobby.map.automatic_gameend_time) / metabolism_rate)
+
+/datum/deathmatch_modifier/wasted
+ name = "Wasted"
+ description = "You've had one drink too many"
+
+/datum/deathmatch_modifier/wasted/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.adjust_drunk_effect(rand(30, 35))
+ var/metabolism_rate = /datum/reagent/consumable/ethanol/jack_rose::metabolization_rate
+ player.reagents.add_reagent(/datum/reagent/consumable/ethanol/jack_rose, initial(lobby.map.automatic_gameend_time) * 0.35 / metabolism_rate)
+
+/datum/deathmatch_modifier/monkeys
+ name = "Monkeyfication"
+ description = "Go back, I want to be monkey!"
+
+/datum/deathmatch_modifier/monkeys/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ //we don't call monkeyize(), because it'd set the player name to a generic "monkey(number)".
+ player.set_species(/datum/species/monkey)
+
+/datum/deathmatch_modifier/inverted_movement
+ name = "Inverted Movement"
+ description = "Up is down, left is right"
+
+/datum/deathmatch_modifier/inverted_movement/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.AddElement(/datum/element/inverted_movement)
+
+/datum/deathmatch_modifier/minefield
+ name = "Minefield"
+ description = "Oh, it seems you've trotted on a mine!"
+
+/datum/deathmatch_modifier/minefield/on_start_game(datum/deathmatch_lobby/lobby)
+ var/list/mines = subtypesof(/obj/effect/mine)
+ mines -= list(
+ /obj/effect/mine/explosive, //too lethal.
+ /obj/effect/mine/kickmine, //will kick the client, lol
+ /obj/effect/mine/gas, //Just spawns oxygen.
+ /obj/effect/mine/gas/n2o, //no sleeping please.
+ )
+
+ ///1 every 11 turfs, but it will actually spawn fewer mines since groundless and closed turfs are skipped.
+ var/mines_to_spawn = length(lobby.location.reserved_turfs) * 0.09
+ for(var/iteration in 1 to mines_to_spawn)
+ var/turf/target_turf = pick(lobby.location.reserved_turfs)
+ if(!isopenturf(target_turf) || isgroundlessturf(target_turf))
+ continue
+ ///don't spawn mine next to player spawns.
+ if(locate(/obj/effect/landmark/deathmatch_player_spawn) in range(1, target_turf))
+ continue
+ ///skip belt loops or they'll explode right away.
+ if(locate(/obj/machinery/conveyor) in target_turf.contents)
+ continue
+ var/mine_path = pick(mines)
+ new mine_path (target_turf)
+
+/datum/deathmatch_modifier/flipping
+ name = "Perma-Flipping"
+ description = "You're constantly flipping, however it's purely cosmetic"
+
+/datum/deathmatch_modifier/flipping/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
+ player.SpinAnimation(speed = 0.9 SECONDS, loops = -1)
+
+/datum/deathmatch_modifier/random
+ name = "Random Modifiers"
+ description = "Picks 3 to 5 random modifiers as the game is about to start"
+
+/datum/deathmatch_modifier/random/on_select(datum/deathmatch_lobby/lobby)
+ ///remove any other global modifier if chosen. It'll pick random ones when the time comes.
+ for(var/modpath in lobby.modifiers)
+ var/datum/deathmatch_modifier/modifier = GLOB.deathmatch_game.modifiers[modpath]
+ if(modifier.random_exempted)
+ continue
+ modifier.unselect(lobby)
+ lobby -= modpath
+
+/datum/deathmatch_modifier/random/on_start_game(datum/deathmatch_lobby/lobby)
+ lobby.modifiers -= type //remove it before attempting to select other modifiers, or they'll fail.
+
+ var/static/list/static_pool
+ if(!static_pool)
+ static_pool = subtypesof(/datum/deathmatch_modifier)
+ for(var/datum/deathmatch_modifier/modpath as anything in static_pool)
+ if(initial(modpath.random_exempted))
+ static_pool -= modpath
+ var/list/modifiers_pool = static_pool.Copy()
+
+ ///Pick global modifiers at random.
+ for(var/iteration in rand(3, 5))
+ var/mod_len = length(modifiers_pool)
+ if(!mod_len)
+ break
+ var/datum/deathmatch_modifier/modifier
+ if(mod_len > 1)
+ modifier = GLOB.deathmatch_game.modifiers[pick_n_take(modifiers_pool)]
+ else //pick() throws errors if the list has only one element iirc.
+ modifier = GLOB.deathmatch_game.modifiers[modifiers_pool[1]]
+ modifiers_pool = null
+ if(!modifier.selectable(lobby))
+ continue
+ modifier.on_select(lobby)
+ modifier.on_start_game(lobby)
+ lobby += modifier
+ modifiers_pool -= modifier.blacklisted_modifiers
+
+/datum/deathmatch_modifier/any_loadout
+ name = "Any Loadout Allowed"
+ description = "Watch players pick Instagib everytime"
+ random_exempted = TRUE
+
+/datum/deathmatch_modifier/any_loadout/selectable(datum/deathmatch_lobby/lobby)
+ . = ..()
+ if(!.)
+ return
+ return lobby.map.allowed_loadouts
+
+/datum/deathmatch_modifier/any_loadout/on_select(datum/deathmatch_lobby/lobby)
+ lobby.loadouts = GLOB.deathmatch_game.loadouts
+
+/datum/deathmatch_modifier/any_loadout/unselect(datum/deathmatch_lobby/lobby)
+ lobby.loadouts = lobby.map.allowed_loadouts
+
+/datum/deathmatch_modifier/any_loadout/on_map_changed(datum/deathmatch_lobby/lobby)
+ if(lobby.loadouts == GLOB.deathmatch_game.loadouts) //This arena already allows any loadout for some reason.
+ lobby.modifiers -= type
+ else
+ lobby.loadouts = GLOB.deathmatch_game.loadouts
diff --git a/code/modules/food_and_drinks/machinery/deep_fryer.dm b/code/modules/food_and_drinks/machinery/deep_fryer.dm
index c9b9fca044b..ac6ae105456 100644
--- a/code/modules/food_and_drinks/machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/machinery/deep_fryer.dm
@@ -224,7 +224,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
cold_multiplier += round(target_temp * 1.5 / T0C, 0.01)
dunking_target.apply_damage(min(30 * bio_multiplier * cold_multiplier, reagents.total_volume), BURN, BODY_ZONE_HEAD)
if(reagents.reagent_list) //This can runtime if reagents has nothing in it.
- reagents.remove_any((reagents.total_volume/2))
+ reagents.remove_all((reagents.total_volume/2))
dunking_target.Paralyze(60)
user.changeNext_move(CLICK_CD_MELEE)
return ..()
diff --git a/code/modules/food_and_drinks/machinery/icecream_vat.dm b/code/modules/food_and_drinks/machinery/icecream_vat.dm
index e5742418c14..d4de5991995 100644
--- a/code/modules/food_and_drinks/machinery/icecream_vat.dm
+++ b/code/modules/food_and_drinks/machinery/icecream_vat.dm
@@ -249,6 +249,12 @@
reagents.remove_reagent(reagents_used, CONE_REAGENET_NEEDED)
balloon_alert_to_viewers("scoops [selected_flavour]", "scoops [selected_flavour]")
+ if(istype(cone))
+ if(isnull(cone.crafted_food_buff))
+ cone.crafted_food_buff = /datum/status_effect/food/chilling
+ if(user.mind)
+ ADD_TRAIT(cone, TRAIT_FOOD_CHEF_MADE, REF(user.mind))
+
///Swaps the mode to the next one meant to be selected, then tells the user who changed it.
/obj/machinery/icecream_vat/proc/swap_modes(mob/user)
if(!user.can_perform_action(src))
diff --git a/code/modules/jobs/departments/departments.dm b/code/modules/jobs/departments/departments.dm
index 332e489eda4..9f2776bad0e 100644
--- a/code/modules/jobs/departments/departments.dm
+++ b/code/modules/jobs/departments/departments.dm
@@ -32,6 +32,8 @@
department_jobs -= job_datum
job_datum.departments_bitflags -= department_bitflags
job_datum.job_flags &= ~JOB_NEW_PLAYER_JOINABLE
+ job_datum.spawn_positions = 0
+ job_datum.total_positions = 0
/// Returns a nation name for this department.
/datum/job_department/proc/generate_nation_name()
diff --git a/code/modules/mining/boulder_processing/boulder.dm b/code/modules/mining/boulder_processing/boulder.dm
index 4aa7f57d8a7..796e382b21a 100644
--- a/code/modules/mining/boulder_processing/boulder.dm
+++ b/code/modules/mining/boulder_processing/boulder.dm
@@ -162,7 +162,7 @@
stack_trace("boulder found containing material type [picked.type] with no set ore_type")
continue
cracked_ore = new cracked_ore_type (drop_location(), quantity)
- SSblackbox.record_feedback("tally", "ore_mined", quantity, cracked_ore)
+ SSblackbox.record_feedback("tally", "ore_mined", quantity, cracked_ore.type)
///Moves boulder contents to the drop location, and then deletes the boulder.
/obj/item/boulder/proc/break_apart()
diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
index 18d69105a41..7cfe7132e15 100644
--- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
+++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
@@ -330,7 +330,7 @@
INVOKE_ASYNC(our_mop, TYPE_PROC_REF(/obj/item, melee_attack_chain), src, target)
return COMPONENT_CANCEL_ATTACK_CHAIN
- if(!iscarbon(target) && !is_type_in_typecache(target, huntable_trash))
+ if(!(iscarbon(target) && (bot_access_flags & BOT_COVER_EMAGGED)) && !is_type_in_typecache(target, huntable_trash))
return NONE
visible_message(span_danger("[src] sprays hydrofluoric acid at [target]!"))
diff --git a/code/modules/mob/living/basic/clown/clown.dm b/code/modules/mob/living/basic/clown/clown.dm
index 88c2b9496a9..a1a7014b263 100644
--- a/code/modules/mob/living/basic/clown/clown.dm
+++ b/code/modules/mob/living/basic/clown/clown.dm
@@ -403,7 +403,7 @@
GRANT_ACTION(/datum/action/cooldown/regurgitate)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_GLUTTON, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheesiehonkers, /obj/item/food/cornchips), tame_chance = 30, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed)))
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheesiehonkers, /obj/item/food/cornchips), tame_chance = 30, bonus_tame_chance = 0)
AddElement(/datum/element/damage_threshold, 10) //lots of fat to cushion blows.
/mob/living/basic/clown/mutant/glutton/attacked_by(obj/item/item, mob/living/user)
@@ -461,7 +461,7 @@
playsound(loc,'sound/items/eatfood.ogg', rand(30,50), TRUE)
flick("glutton_mouth", src)
-/mob/living/basic/clown/mutant/glutton/proc/tamed(mob/living/tamer)
+/mob/living/basic/clown/mutant/glutton/tamed(mob/living/tamer, atom/food)
buckle_lying = 0
AddElement(/datum/element/ridable, /datum/component/riding/creature/glutton)
diff --git a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
index e5d49aa867b..1472a64039a 100644
--- a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
+++ b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
@@ -61,10 +61,10 @@
var/static/list/food_types
if(!food_types)
food_types = src.food_types.Copy()
- AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
+ AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
AddElement(/datum/element/basic_eating, food_types = food_types)
-/mob/living/basic/cow/proc/tamed(mob/living/tamer)
+/mob/living/basic/cow/tamed(mob/living/tamer, atom/food)
buckle_lying = 0
visible_message("[src] [tame_message] as it seems to bond with [tamer].", "You [self_tame_message], recognizing [tamer] as your new pal.")
AddElement(/datum/element/ridable, /datum/component/riding/creature/cow)
diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm
index b3af3c59a12..f4c1849883f 100644
--- a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm
+++ b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm
@@ -35,9 +35,9 @@
if(!food_types)
food_types = src.food_types.Copy()
AddElement(/datum/element/basic_eating, food_types = food_types)
- AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
+ AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
-/mob/living/basic/cow/moonicorn/tamed(mob/living/tamer)
+/mob/living/basic/cow/moonicorn/tamed(mob/living/tamer, atom/food)
. = ..()
///stop killing my FRIENDS
faction |= tamer.faction
diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm
index 94183d044c5..6f732f16844 100644
--- a/code/modules/mob/living/basic/farm_animals/pig.dm
+++ b/code/modules/mob/living/basic/farm_animals/pig.dm
@@ -37,9 +37,9 @@
///wrapper for the tameable component addition so you can have non tamable cow subtypes
/mob/living/basic/pig/proc/make_tameable()
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 25, bonus_tame_chance = 15)
-/mob/living/basic/pig/proc/tamed(mob/living/tamer)
+/mob/living/basic/pig/tamed(mob/living/tamer, atom/food)
can_buckle = TRUE
buckle_lying = 0
AddElement(/datum/element/ridable, /datum/component/riding/creature/pig)
diff --git a/code/modules/mob/living/basic/farm_animals/pony.dm b/code/modules/mob/living/basic/farm_animals/pony.dm
index 7795ec630e6..4c2c76e84c2 100644
--- a/code/modules/mob/living/basic/farm_animals/pony.dm
+++ b/code/modules/mob/living/basic/farm_animals/pony.dm
@@ -41,9 +41,9 @@
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/ai_flee_while_injured)
AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling)
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = unique_tamer)
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 25, bonus_tame_chance = 15, unique = unique_tamer)
-/mob/living/basic/pony/proc/tamed(mob/living/tamer)
+/mob/living/basic/pony/tamed(mob/living/tamer, atom/food)
can_buckle = TRUE
buckle_lying = 0
playsound(src, 'sound/creatures/pony/snort.ogg', 50)
@@ -151,4 +151,4 @@
ponycolors = list("#5d566f", pick_weight(mane_colors))
name = pick("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
// Only one person can tame these fellas, and they only need one apple
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 100, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = unique_tamer)
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 100, bonus_tame_chance = 15, unique = unique_tamer)
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/protector.dm b/code/modules/mob/living/basic/guardian/guardian_types/protector.dm
index 27ec9cace81..a7a9e718e54 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/protector.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/protector.dm
@@ -16,7 +16,6 @@
/mob/living/basic/guardian/protector/Initialize(mapload, datum/guardian_fluff/theme)
. = ..()
shield = new(src)
- shield.owner_has_control = FALSE // Hide it from the user, it's integrated with guardian UI
shield.Grant(src)
/mob/living/basic/guardian/protector/Destroy()
@@ -49,6 +48,7 @@
background_icon_state = "base"
cooldown_time = 1 SECONDS
click_to_activate = FALSE
+ owner_has_control = FALSE // Hide it from the user, it's integrated with guardian UI
/datum/action/cooldown/mob_cooldown/protector_shield/Activate(mob/living/target)
if (!isliving(target))
diff --git a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
index c657fa42843..e452c156474 100644
--- a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
+++ b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
@@ -65,15 +65,9 @@
make_tameable()
/mob/living/basic/mining/wolf/proc/make_tameable()
- AddComponent(\
- /datum/component/tameable,\
- food_types = list(/obj/item/food/meat/slab),\
- tame_chance = 15,\
- bonus_tame_chance = 5,\
- after_tame = CALLBACK(src, PROC_REF(tame_wolf)),\
- )
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat/slab), tame_chance = 15, bonus_tame_chance = 5)
-/mob/living/basic/mining/wolf/proc/tame_wolf()
+/mob/living/basic/mining/wolf/tamed(mob/living/tamer, atom/food)
new /obj/effect/temp_visual/heart(src.loc)
// ride wolf, life good
AddElement(/datum/element/ridable, /datum/component/riding/creature/wolf)
diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
index f35f78ba80f..ce9f2439358 100644
--- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
+++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
@@ -106,15 +106,9 @@
return ..()
/mob/living/basic/mining/goldgrub/proc/make_tameable()
- AddComponent(\
- /datum/component/tameable,\
- food_types = list(/obj/item/stack/ore),\
- tame_chance = 25,\
- bonus_tame_chance = 5,\
- after_tame = CALLBACK(src, PROC_REF(tame_grub)),\
- )
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/stack/ore), tame_chance = 25, bonus_tame_chance = 5)
-/mob/living/basic/mining/goldgrub/proc/tame_grub()
+/mob/living/basic/mining/goldgrub/tamed(mob/living/tamer, atom/food)
new /obj/effect/temp_visual/heart(src.loc)
AddElement(/datum/element/ridable, /datum/component/riding/creature/goldgrub)
AddComponent(/datum/component/obeys_commands, pet_commands)
diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
index 6d197d8853a..4a28fba6630 100644
--- a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
+++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
@@ -68,13 +68,7 @@
AddComponent(/datum/component/basic_mob_attack_telegraph)
AddComponentFrom(INNATE_TRAIT, /datum/component/shovel_hands)
if (tameable)
- AddComponent(\
- /datum/component/tameable,\
- food_types = list(/obj/item/food/grown/ash_flora),\
- tame_chance = 10,\
- bonus_tame_chance = 5,\
- after_tame = CALLBACK(src, PROC_REF(tamed)),\
- )
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/ash_flora), tame_chance = 10, bonus_tame_chance = 5)
tentacles = new (src)
tentacles.Grant(src)
@@ -120,6 +114,9 @@
return
balloon_alert(user, "ready to ride")
qdel(attacking_item)
+ make_rideable()
+
+/mob/living/basic/mining/goliath/proc/make_rideable()
saddled = TRUE
buckle_lying = 0
add_overlay("goliath_saddled")
@@ -149,7 +146,7 @@
icon_state = tentacle_warning_state
/// Get ready for mounting
-/mob/living/basic/mining/goliath/proc/tamed()
+/mob/living/basic/mining/goliath/tamed(mob/living/tamer, atom/food)
tamed = TRUE
// Copy entire faction rather than just placing user into faction, to avoid tentacle peril on station
@@ -163,6 +160,19 @@
/mob/living/basic/mining/goliath/ranged_secondary_attack(atom/atom_target, modifiers)
tentacle_line?.Trigger(target = atom_target)
+/// Version of the goliath that already starts saddled and doesn't require a lasso to be ridden.
+/mob/living/basic/mining/goliath/deathmatch
+ saddled = TRUE
+ buckle_lying = 0
+
+/mob/living/basic/mining/goliath/deathmatch/Initialize(mapload)
+ . = ..()
+ make_rideable()
+
+/mob/living/basic/mining/goliath/deathmatch/make_rideable()
+ add_overlay("goliath_saddled")
+ AddElement(/datum/element/ridable, /datum/component/riding/creature/goliath/deathmatch)
+
/// Legacy Goliath mob with different sprites, largely the same behaviour
/mob/living/basic/mining/goliath/ancient
name = "ancient goliath"
diff --git a/code/modules/mob/living/basic/minebots/minebot.dm b/code/modules/mob/living/basic/minebots/minebot.dm
index 11b4530dd7a..30f486e9416 100644
--- a/code/modules/mob/living/basic/minebots/minebot.dm
+++ b/code/modules/mob/living/basic/minebots/minebot.dm
@@ -53,13 +53,7 @@
AddElement(/datum/element/death_drops, death_drops)
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), INNATE_TRAIT)
AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE)
- AddComponent(\
- /datum/component/tameable,\
- food_types = list(/obj/item/stack/ore),\
- tame_chance = 100,\
- bonus_tame_chance = 5,\
- after_tame = CALLBACK(src, PROC_REF(activate_bot)),\
- )
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/stack/ore), tame_chance = 100, bonus_tame_chance = 5)
var/static/list/innate_actions = list(
/datum/action/cooldown/mob_cooldown/minedrone/toggle_light = BB_MINEBOT_LIGHT_ABILITY,
@@ -154,7 +148,7 @@
return ACCESS_ALLOWED
return ACCESS_DISALLOWED
-/mob/living/basic/mining_drone/proc/activate_bot()
+/mob/living/basic/mining_drone/tamed(mob/living/tamer, atom/food)
AddComponent(/datum/component/obeys_commands, pet_commands)
/mob/living/basic/mining_drone/death(gibbed)
diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm
index f835d7c5e17..f513795e7f6 100644
--- a/code/modules/mob/living/basic/pets/dog/_dog.dm
+++ b/code/modules/mob/living/basic/pets/dog/_dog.dm
@@ -50,7 +50,7 @@
AddElement(/datum/element/pet_bonus, "woofs happily!")
AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW)
AddElement(/datum/element/unfriend_attacker, untamed_reaction = "%SOURCE% fixes %TARGET% with a look of betrayal.")
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat/slab/human/mutant/skeleton, /obj/item/stack/sheet/bone), tame_chance = 30, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = FALSE)
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat/slab/human/mutant/skeleton, /obj/item/stack/sheet/bone), tame_chance = 30, bonus_tame_chance = 15, unique = FALSE)
AddComponent(/datum/component/obeys_commands, pet_commands)
var/dog_area = get_area(src)
for(var/obj/structure/bed/dogbed/dog_bed in dog_area)
@@ -64,7 +64,7 @@
speech.emote_see = string_list(list("shakes [p_their()] head.", "chases [p_their()] tail.","shivers."))
///Proc to run on a successful taming attempt
-/mob/living/basic/pet/dog/proc/tamed(mob/living/tamer)
+/mob/living/basic/pet/dog/tamed(mob/living/tamer, atom/food)
visible_message(span_notice("[src] licks at [tamer] in a friendly manner!"))
/// A dog bone fully heals a dog, and befriends it if it's not your friend.
diff --git a/code/modules/mob/living/basic/pets/parrot/_parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
index 6dd5a9a4051..c23481d85a7 100644
--- a/code/modules/mob/living/basic/pets/parrot/_parrot.dm
+++ b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
@@ -91,13 +91,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
AddElement(/datum/element/strippable, GLOB.strippable_parrot_items)
AddElement(/datum/element/simple_flying)
AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING)
- AddComponent(\
- /datum/component/tameable,\
- food_types = edibles,\
- tame_chance = 100,\
- bonus_tame_chance = 0,\
- after_tame = CALLBACK(src, PROC_REF(tamed)),\
- )
+ AddComponent(/datum/component/tameable, food_types = edibles, tame_chance = 100, bonus_tame_chance = 0)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking))
RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click))
@@ -445,7 +439,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
return returnable_list
-/mob/living/basic/parrot/proc/tamed()
+/mob/living/basic/parrot/tamed(mob/living/tamer, atom/food)
new /obj/effect/temp_visual/heart(drop_location())
/mob/living/basic/parrot/proc/drop_item_on_signal(mob/living/user)
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm
index c071bf7fdbe..2c788e9256d 100644
--- a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm
@@ -2,7 +2,7 @@
key = STRIPPABLE_ITEM_PARROT_HEADSET
/datum/strippable_item/parrot_headset/get_item(atom/source)
- var/mob/living/basic/parrot/poly/parrot_source = source
+ var/mob/living/basic/parrot/parrot_source = source
return istype(parrot_source) ? parrot_source.ears : null
/datum/strippable_item/parrot_headset/try_equip(atom/source, obj/item/equipping, mob/user)
diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
index ebb6f1d23f7..e0700892523 100644
--- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm
+++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
@@ -95,10 +95,10 @@
AddComponent(/datum/component/aggro_emote, emote_list = string_list(list("gnashes")))
AddComponent(/datum/component/regenerator, outline_colour = regenerate_colour)
if (tamer)
- on_tamed(tamer, feedback = FALSE)
+ tamed(tamer, feedback = FALSE)
befriend(tamer)
else
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat), tame_chance = 10, bonus_tame_chance = 5, after_tame = CALLBACK(src, PROC_REF(on_tamed)))
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat), tame_chance = 10, bonus_tame_chance = 5)
teleport = new(src)
teleport.Grant(src)
@@ -119,7 +119,7 @@
set_greyscale(colors = list(pick_weight(GLOB.carp_colors)))
/// Called when another mob has forged a bond of friendship with this one, passed the taming mob as 'tamer'
-/mob/living/basic/carp/proc/on_tamed(mob/tamer, feedback = TRUE)
+/mob/living/basic/carp/tamed(mob/living/tamer, atom/food, feedback = TRUE)
buckle_lying = 0
AddElement(/datum/element/ridable, ridable_data)
AddComponent(/datum/component/obeys_commands, tamed_commands)
diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm b/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
index b72815d8325..50685e1ebe0 100644
--- a/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
+++ b/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
@@ -58,7 +58,7 @@
grant_actions_by_list(innate_actions)
AddElement(/datum/element/simple_flying)
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 100, after_tame = CALLBACK(src, PROC_REF(on_tame)))
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 100)
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
on_hit_overlay = mutable_appearance(icon, "[icon_state]_crying")
@@ -119,7 +119,7 @@
befriend(target)
COOLDOWN_START(src, eye_healing, 15 SECONDS)
-/mob/living/basic/eyeball/proc/on_tame(mob/tamer)
+/mob/living/basic/eyeball/tamed(mob/living/tamer, atom/food)
spin(spintime = 2 SECONDS, speed = 1)
//become passive to the humens
faction |= tamer.faction
diff --git a/code/modules/mob/living/basic/trooper/pirate.dm b/code/modules/mob/living/basic/trooper/pirate.dm
index 208a113e5d4..6a51b901ebc 100644
--- a/code/modules/mob/living/basic/trooper/pirate.dm
+++ b/code/modules/mob/living/basic/trooper/pirate.dm
@@ -55,7 +55,7 @@
r_hand = /obj/item/gun/energy/laser
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged
/// Type of bullet we use
- var/casingtype = /obj/item/ammo_casing/energy/laser
+ var/projectiletype = /obj/projectile/beam/laser
/// Sound to play when firing weapon
var/projectilesound = 'sound/weapons/laser.ogg'
/// number of burst shots
@@ -67,7 +67,7 @@
. = ..()
AddComponent(\
/datum/component/ranged_attacks,\
- casing_type = casingtype,\
+ projectile_type = projectiletype,\
projectile_sound = projectilesound,\
cooldown_time = ranged_cooldown,\
burst_shots = burst_shots,\
diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm
index c1ba3c12e36..cf12c2b4083 100644
--- a/code/modules/mob/living/basic/vermin/mouse.dm
+++ b/code/modules/mob/living/basic/vermin/mouse.dm
@@ -63,7 +63,7 @@
if (tame)
faction |= FACTION_NEUTRAL
else
- AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheese), tame_chance = 100, after_tame = CALLBACK(src, PROC_REF(tamed)))
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheese), tame_chance = 100)
/mob/living/basic/mouse/Destroy()
SSmobs.cheeserats -= src
@@ -148,7 +148,7 @@
to_chat(entered, span_notice("[icon2html(src, entered)] Squeak!"))
/// Called when a mouse is hand-fed some cheese, it will stop being afraid of humans
-/mob/living/basic/mouse/proc/tamed(mob/living/tamer, obj/item/food/cheese/cheese)
+/mob/living/basic/mouse/tamed(mob/living/tamer, obj/item/food/cheese/cheese)
new /obj/effect/temp_visual/heart(loc)
faction |= FACTION_NEUTRAL
tame = TRUE
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 5fcfeec8d72..e33098153ca 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -815,8 +815,7 @@
hud_used.stamina.icon_state = "stamina_full"
/mob/living/carbon/proc/update_spacesuit_hud_icon(cell_state = "empty")
- if(hud_used?.spacesuit)
- hud_used.spacesuit.icon_state = "spacesuit_[cell_state]"
+ hud_used?.spacesuit?.icon_state = "spacesuit_[cell_state]"
/mob/living/carbon/set_health(new_value)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index 25eddc51e07..1cf8b67db5d 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -56,6 +56,12 @@
/datum/outfit/syndicate/full = /datum/outfit/syndicate/full/plasmaman,
/datum/outfit/syndicate/leader = /datum/outfit/syndicate/leader/plasmaman,
/datum/outfit/syndicate/reinforcement = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/reinforcement/cybersun = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/reinforcement/donk = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/reinforcement/gorlex = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/reinforcement/interdyne = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/reinforcement/mi13 = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/reinforcement/waffle = /datum/outfit/syndicate/reinforcement/plasmaman,
)
/// If the bones themselves are burning clothes won't help you much
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 26e4567fe71..82d64732bc2 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2616,6 +2616,26 @@ GLOBAL_LIST_EMPTY(fire_appearances)
SEND_SIGNAL(src, COMSIG_LIVING_UNFRIENDED, old_friend)
return TRUE
+/**
+ * Common proc used to deduct money from cargo, announce the kidnapping and add src to the black market.
+ * Returns the black market item, for extra stuff like signals that need to be registered.
+ */
+/mob/living/proc/process_capture(ransom_price, black_market_price)
+ if(ransom_price > 0)
+ var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
+
+ if(cargo_account) //Just in case
+ cargo_account.adjust_money(-min(ransom_price, cargo_account.account_balance)) //Not so much, especially for competent cargo. Plus this can't be mass-triggered like it has been done with contractors
+ priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. As is policy we've taken a portion of the station's funds to offset the overall cost.", "Nanotrasen Asset Protection", has_important_message = TRUE)
+
+ ///The price should be high enough that the contractor can't just buy 'em back with their cut alone.
+ var/datum/market_item/hostage/market_item = new(src, black_market_price || ransom_price)
+ SSblackmarket.markets[/datum/market/blackmarket].add_item(market_item)
+
+ if(mind)
+ ADD_TRAIT(mind, TRAIT_HAS_BEEN_KIDNAPPED, TRAIT_GENERIC)
+ return market_item
+
/// Admin only proc for making the mob hallucinate a certain thing
/mob/living/proc/admin_give_hallucination(mob/admin)
if(!admin || !check_rights(NONE))
diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm
index 19661110133..651e7d622f7 100644
--- a/code/modules/mob/living/silicon/robot/robot_model.dm
+++ b/code/modules/mob/living/silicon/robot/robot_model.dm
@@ -628,7 +628,7 @@
reagents.expose(our_turf, TOUCH, min(1, 10 / reagents.total_volume))
// We use more water doing this then mopping
- reagents.remove_any(2) //reaction() doesn't use up the reagents
+ reagents.remove_all(2) //reaction() doesn't use up the reagents
/datum/action/toggle_buffer/update_button_name(atom/movable/screen/movable/action_button/current_button, force)
if(buffer_on)
diff --git a/code/modules/mob/living/simple_animal/hostile/vatbeast.dm b/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
index f5184a85434..a5dfface811 100644
--- a/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
+++ b/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
@@ -31,9 +31,9 @@
GRANT_ACTION(/datum/action/cooldown/tentacle_slap)
add_cell_sample()
- AddComponent(/datum/component/tameable, list(/obj/item/food/fries, /obj/item/food/cheesyfries, /obj/item/food/cornchips, /obj/item/food/carrotfries), tame_chance = 30, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed)))
+ AddComponent(/datum/component/tameable, list(/obj/item/food/fries, /obj/item/food/cheesyfries, /obj/item/food/cornchips, /obj/item/food/carrotfries), tame_chance = 30, bonus_tame_chance = 0)
-/mob/living/simple_animal/hostile/vatbeast/proc/tamed(mob/living/tamer)
+/mob/living/simple_animal/hostile/vatbeast/tamed(mob/living/tamer, obj/item/food)
buckle_lying = 0
AddElement(/datum/element/ridable, /datum/component/riding/creature/vatbeast)
faction = list(FACTION_NEUTRAL)
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 4da5da30987..a11ef46c7f2 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -515,6 +515,8 @@
selector.update_appearance()
update_move_intent_slowdown()
+ SEND_SIGNAL(user, COMSIG_MOVE_INTENT_TOGGLED)
+
///Moves a mob upwards in z level
/mob/verb/up()
set name = "Move Upwards"
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
index b230a3a38d7..17bc5e7fd68 100644
--- a/code/modules/mod/mod_activation.dm
+++ b/code/modules/mod/mod_activation.dm
@@ -244,7 +244,8 @@
for(var/obj/item/mod/module/module as anything in modules)
module.on_suit_deactivation()
update_speed()
- update_icon_state()
+ update_appearance(UPDATE_ICON_STATE)
+ update_charge_alert()
wearer.update_clothing(slot_flags)
/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index b9f5cbd3e16..b2ba6f96540 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -246,7 +246,6 @@
if(malfunctioning)
malfunctioning_charge_drain = rand(1,20)
subtract_charge((charge_drain + malfunctioning_charge_drain) * seconds_per_tick)
- update_charge_alert()
for(var/obj/item/mod/module/module as anything in modules)
if(malfunctioning && module.active && SPT_PROB(5, seconds_per_tick))
module.on_deactivation(display_message = TRUE)
@@ -332,7 +331,6 @@
wrench.play_tool_sound(src, 100)
balloon_alert(user, "core removed")
core.forceMove(drop_location())
- update_charge_alert()
return TRUE
return ..()
@@ -415,7 +413,6 @@
attacking_core.install(src)
balloon_alert(user, "core installed")
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
- update_charge_alert()
return TRUE
else if(is_wire_tool(attacking_item) && open)
wires.interact(user)
@@ -500,8 +497,8 @@
for(var/obj/item/mod/module/module as anything in modules)
module.on_unequip()
UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN))
- wearer.clear_alert(ALERT_MODSUIT_CHARGE)
SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer)
+ wearer.update_spacesuit_hud_icon("0")
wearer = null
/obj/item/mod/control/proc/clean_up()
@@ -644,13 +641,21 @@
/obj/item/mod/control/proc/check_charge(amount)
return core?.check_charge(amount) || FALSE
+/**
+ * Updates the wearer's hud according to the current state of the MODsuit
+ */
/obj/item/mod/control/proc/update_charge_alert()
- if(!wearer)
- return
- if(!core)
- wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/nocore)
+ if(isnull(wearer))
return
- core.update_charge_alert()
+ var/state_to_use
+ if(!active)
+ state_to_use = "0"
+ else if(isnull(core))
+ state_to_use = "coreless"
+ else
+ state_to_use = core.get_charge_icon_state()
+
+ wearer.update_spacesuit_hud_icon(state_to_use || "0")
/obj/item/mod/control/proc/update_speed()
var/list/all_parts = mod_parts + src
@@ -718,7 +723,6 @@
return
if(part == core)
core.uninstall()
- update_charge_alert()
return
if(part.loc == wearer)
return
diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm
index d8138f6f291..5e7183e2563 100644
--- a/code/modules/mod/mod_core.dm
+++ b/code/modules/mod/mod_core.dm
@@ -18,9 +18,11 @@
mod = mod_unit
mod.core = src
forceMove(mod)
+ mod.update_charge_alert()
/obj/item/mod/core/proc/uninstall()
mod.core = null
+ mod.update_charge_alert()
mod = null
/obj/item/mod/core/proc/charge_source()
@@ -41,8 +43,11 @@
/obj/item/mod/core/proc/check_charge(amount)
return FALSE
-/obj/item/mod/core/proc/update_charge_alert()
- mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE)
+/**
+ * Gets what icon state to display on the HUD for the charge level of this core
+ */
+/obj/item/mod/core/proc/get_charge_icon_state()
+ return "0"
/obj/item/mod/core/infinite
name = "MOD infinite core"
@@ -68,6 +73,9 @@
/obj/item/mod/core/infinite/check_charge(amount)
return TRUE
+/obj/item/mod/core/infinite/get_charge_icon_state()
+ return "high"
+
/obj/item/mod/core/standard
name = "MOD standard core"
icon_state = "mod-core-standard"
@@ -80,8 +88,7 @@
var/obj/item/stock_parts/cell/cell
/obj/item/mod/core/standard/Destroy()
- if(cell)
- QDEL_NULL(cell)
+ QDEL_NULL(cell)
return ..()
/obj/item/mod/core/standard/install(obj/item/mod/control/mod_unit)
@@ -116,54 +123,57 @@
/obj/item/mod/core/standard/add_charge(amount)
var/obj/item/stock_parts/cell/charge_source = charge_source()
- if(!charge_source)
+ if(isnull(charge_source))
return FALSE
- return charge_source.give(amount)
+ . = charge_source.give(amount)
+ if(.)
+ mod.update_charge_alert()
+ return .
/obj/item/mod/core/standard/subtract_charge(amount)
var/obj/item/stock_parts/cell/charge_source = charge_source()
- if(!charge_source)
+ if(isnull(charge_source))
return FALSE
- return charge_source.use(amount, TRUE)
+ . = charge_source.use(amount, TRUE)
+ if(.)
+ mod.update_charge_alert()
+ return .
/obj/item/mod/core/standard/check_charge(amount)
return charge_amount() >= amount
-/obj/item/mod/core/standard/update_charge_alert()
- var/obj/item/stock_parts/cell/charge_source = charge_source()
- if(!charge_source)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/nocell)
- return
- var/remaining_cell = charge_amount() / max_charge_amount()
- switch(remaining_cell)
+/obj/item/mod/core/standard/get_charge_icon_state()
+ if(isnull(charge_source()))
+ return "missing"
+
+ switch(round(charge_amount() / max_charge_amount(), 0.01))
if(0.75 to INFINITY)
- mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE)
+ return "high"
if(0.5 to 0.75)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell, 1)
+ return "mid"
if(0.25 to 0.5)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell, 2)
- if(0.01 to 0.25)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell, 3)
- else
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/emptycell)
+ return "low"
+ if(0.02 to 0.25)
+ return "very_low"
+
+ return "empty"
/obj/item/mod/core/standard/proc/install_cell(new_cell)
cell = new_cell
cell.forceMove(src)
- RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ mod.update_charge_alert()
/obj/item/mod/core/standard/proc/uninstall_cell()
if(!cell)
return
+ cell.update_appearance()
cell = null
- UnregisterSignal(src, COMSIG_ATOM_EXITED)
-
-/obj/item/mod/core/standard/proc/on_exit(datum/source, obj/item/stock_parts/cell, direction)
- SIGNAL_HANDLER
+ mod.update_charge_alert()
- if(!istype(cell) || cell.loc == src)
- return
- uninstall_cell()
+/obj/item/mod/core/standard/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone == cell)
+ uninstall_cell()
/obj/item/mod/core/standard/proc/on_examine(datum/source, mob/examiner, list/examine_text)
SIGNAL_HANDLER
@@ -195,7 +205,6 @@
var/obj/item/cell_to_move = cell
cell_to_move.forceMove(drop_location())
user.put_in_hands(cell_to_move)
- mod.update_charge_alert()
/obj/item/mod/core/standard/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
SIGNAL_HANDLER
@@ -212,7 +221,6 @@
install_cell(attacking_item)
mod.balloon_alert(user, "cell installed")
playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
- mod.update_charge_alert()
return COMPONENT_NO_AFTERATTACK
return NONE
@@ -232,7 +240,6 @@
SIGNAL_HANDLER
add_charge(amount)
- mod.update_charge_alert()
/obj/item/mod/core/ethereal
name = "MOD ethereal core"
@@ -272,12 +279,8 @@
/obj/item/mod/core/ethereal/check_charge(amount)
return charge_amount() >= amount*charge_modifier
-/obj/item/mod/core/ethereal/update_charge_alert()
- var/obj/item/organ/internal/stomach/ethereal/charge_source = charge_source()
- if(charge_source)
- mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE)
- return
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/nocell)
+/obj/item/mod/core/ethereal/get_charge_icon_state()
+ return charge_source() ? "0" : "missing"
#define PLASMA_CORE_ORE_CHARGE 1500
#define PLASMA_CORE_SHEET_CHARGE 2000
@@ -318,28 +321,29 @@
/obj/item/mod/core/plasma/add_charge(amount)
charge = min(maxcharge, charge + amount)
+ mod.update_charge_alert()
return TRUE
/obj/item/mod/core/plasma/subtract_charge(amount)
charge = max(0, charge - amount)
+ mod.update_charge_alert()
return TRUE
/obj/item/mod/core/plasma/check_charge(amount)
return charge_amount() >= amount
-/obj/item/mod/core/plasma/update_charge_alert()
- var/remaining_plasma = charge_amount() / max_charge_amount()
- switch(remaining_plasma)
+/obj/item/mod/core/plasma/get_charge_icon_state()
+ switch(round(charge_amount() / max_charge_amount(), 0.01))
if(0.75 to INFINITY)
- mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE)
+ return "high"
if(0.5 to 0.75)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell/plasma, 1)
+ return "mid"
if(0.25 to 0.5)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell/plasma, 2)
- if(0.01 to 0.25)
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell/plasma, 3)
- else
- mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/emptycell/plasma)
+ return "low"
+ if(0.02 to 0.25)
+ return "very_low"
+
+ return "empty"
/obj/item/mod/core/plasma/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
SIGNAL_HANDLER
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
index 509cdfc1ed7..88f697ccd48 100644
--- a/code/modules/mod/mod_theme.dm
+++ b/code/modules/mod/mod_theme.dm
@@ -1506,6 +1506,19 @@
acid = 90
wound = 10
+/datum/mod_theme/responsory/traitor
+ name = "dark paladin"
+ desc = "A high-speed suit stolen by the Gorlex Maradeurs, purposed for less than honest intents."
+ extended_desc = "A streamlined suit of Nanotrasen Syndicate design, these sleek black suits are only worn by \
+ elite emergency response personnel traitors to help save ruin the day. While the slim and nimble design of the suit \
+ cuts the ceramics and ablatives in it down, dropping the protection, \
+ it keeps the wearer safe from the harsh void of space while sacrificing no speed whatsoever. \
+ While wearing it you feel an extreme deference to darkness light."
+ armor_type = /datum/armor/mod_theme_elite
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ inbuilt_modules = list(/obj/item/mod/module/armor_booster/no_speedbost)
+
/datum/mod_theme/apocryphal
name = "apocryphal"
desc = "A high-tech, only technically legal, armored suit created by a collaboration effort between Nanotrasen and Apadyne Technologies."
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index 0659dd6208b..47f588eb80b 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -477,6 +477,25 @@
/obj/item/mod/module/quick_cuff,
)
+/obj/item/mod/control/pre_equipped/responsory/inquisitory/syndie
+ starting_frequency = MODLINK_FREQ_SYNDICATE
+ req_access = null
+ applied_cell = /obj/item/stock_parts/cell/super
+ theme = /datum/mod_theme/responsory/traitor
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/pathfinder,
+ /obj/item/mod/module/flashlight/darkness,
+ /obj/item/mod/module/dna_lock,
+ /obj/item/mod/module/quick_cuff,
+ /obj/item/mod/module/visor/night,
+ /obj/item/mod/module/shove_blocker,
+ /obj/item/mod/module/noslip,
+ )
+
/obj/item/mod/control/pre_equipped/responsory/inquisitory/commander
insignia_type = /obj/item/mod/module/insignia/commander
additional_module = /obj/item/mod/module/power_kick
diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm
index 575518affbb..ec3f393ed50 100644
--- a/code/modules/mod/mod_ui.dm
+++ b/code/modules/mod/mod_ui.dm
@@ -80,19 +80,23 @@
if(.)
return
if(malfunctioning && prob(75))
- balloon_alert(usr, "button malfunctions!")
+ balloon_alert(ui.user, "button malfunctions!")
return
switch(action)
if("lock")
- locked = !locked
- balloon_alert(usr, "[locked ? "locked" : "unlocked"]!")
+ if(!locked || allowed(ui.user))
+ locked = !locked
+ balloon_alert(ui.user, "[locked ? "locked" : "unlocked"]!")
+ else
+ balloon_alert(ui.user, "access insufficent!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
if("call")
if(!mod_link.link_call)
- call_link(usr, mod_link)
+ call_link(ui.user, mod_link)
else
mod_link.end_call()
if("activate")
- toggle_activate(usr)
+ toggle_activate(ui.user)
if("select")
var/obj/item/mod/module/module = locate(params["ref"]) in modules
if(!module)
@@ -107,9 +111,9 @@
var/obj/item/mod/module/module = locate(params["ref"]) in modules
if(!module)
return
- module.pin(usr)
+ module.pin(ui.user)
if("eject_pai")
- if (!ishuman(usr))
+ if (!ishuman(ui.user))
return
- remove_pai(usr)
+ remove_pai(ui.user)
return TRUE
diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm
index bf7169dc71b..573a1b11b57 100644
--- a/code/modules/mod/modules/_module.dm
+++ b/code/modules/mod/modules/_module.dm
@@ -238,7 +238,6 @@
if(!check_power(amount))
return FALSE
mod.subtract_charge(amount)
- mod.update_charge_alert()
return TRUE
/// Checks if there is enough power in the suit
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index a35e39365b9..2e53e380fd0 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -29,6 +29,9 @@
/// List of traits added when the mod is activated
var/list/traits_to_add = list(TRAIT_HEAD_INJURY_BLOCKED)
+/obj/item/mod/module/armor_booster/no_speedbost
+ speed_added = 0
+
/datum/armor/mod_module_armor_boost
melee = 25
bullet = 30
@@ -236,6 +239,9 @@
/obj/item/mod/module/insignia/chaplain
color = "#f0a00c"
+/obj/item/mod/module/insignia/syndie
+ color = COLOR_SYNDIE_RED
+
///Anti Slip - Prevents you from slipping on water.
/obj/item/mod/module/noslip
name = "MOD anti slip module"
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index f03b0ac44c6..041e671ccdf 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -436,6 +436,21 @@
if("light_range")
set_light_range(clamp(value, min_range, max_range))
+///Like the flashlight module, except the light color is stuck to black and cannot be changed.
+/obj/item/mod/module/flashlight/darkness
+ name = "MOD flashdark module"
+ desc = "A quirky pair of configurable flashdarks installed on the sides of the helmet, \
+ useful for providing darkness at a configurable range."
+ light_color = COLOR_BLACK
+ light_system = OVERLAY_LIGHT
+ light_range = 2
+ min_range = 1
+ max_range = 3
+
+/obj/item/mod/module/flashlight/darkness/get_configuration()
+ . = ..()
+ . -= "light_color"
+
///Dispenser - Dispenses an item after a time passes.
/obj/item/mod/module/dispenser
name = "MOD burger dispenser module"
diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm
index 9971c0da7f7..86db803322f 100644
--- a/code/modules/paperwork/paperbin.dm
+++ b/code/modules/paperwork/paperbin.dm
@@ -124,7 +124,7 @@
return
if(istype(I, /obj/item/paper))
var/obj/item/paper/paper = I
- if(!user.transferItemToLoc(paper, src))
+ if(!user.transferItemToLoc(paper, src, silent = FALSE))
return
to_chat(user, span_notice("You put [paper] in [src]."))
paper_stack += paper
@@ -132,7 +132,7 @@
update_appearance()
else if(istype(I, /obj/item/pen) && !bin_pen)
var/obj/item/pen/pen = I
- if(!user.transferItemToLoc(pen, src))
+ if(!user.transferItemToLoc(pen, src, silent = FALSE))
return
to_chat(user, span_notice("You put [pen] in [src]."))
bin_pen = pen
diff --git a/code/modules/plumbing/plumbers/destroyer.dm b/code/modules/plumbing/plumbers/destroyer.dm
index de03f03aa0f..9ba669818cf 100644
--- a/code/modules/plumbing/plumbers/destroyer.dm
+++ b/code/modules/plumbing/plumbers/destroyer.dm
@@ -18,7 +18,7 @@
if(reagents.total_volume)
if(icon_state != initial(icon_state) + "_working") //threw it here instead of update icon since it only has two states
icon_state = initial(icon_state) + "_working"
- reagents.remove_any(disposal_rate * seconds_per_tick)
+ reagents.remove_all(disposal_rate * seconds_per_tick)
use_power(active_power_usage * seconds_per_tick)
else
if(icon_state != initial(icon_state))
diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm
index 1d36747e8f4..3fe2e482f3e 100644
--- a/code/modules/reagents/chemistry/holder/holder.dm
+++ b/code/modules/reagents/chemistry/holder/holder.dm
@@ -259,51 +259,6 @@
return round(total_removed_amount, CHEMICAL_VOLUME_ROUNDING)
-/**
- * Removes a reagent at random and by a random quantity till the specified amount has been removed.
- * Used to create a shower/spray effect for e.g. when you spill a bottle or turn a shower on
- * and you want an chaotic effect of whatever coming out
- * Arguments
- *
- * * amount- the volume to remove
- */
-/datum/reagents/proc/remove_any(amount = 1)
- if(!IS_FINITE(amount))
- stack_trace("non finite amount passed to remove any reagent [amount]")
- return FALSE
-
- amount = round(amount, CHEMICAL_QUANTISATION_LEVEL)
- if(amount <= 0)
- return FALSE
-
- var/list/cached_reagents = reagent_list
- var/total_removed = 0
- var/current_list_element = 1
- var/initial_list_length = cached_reagents.len //stored here because removing can cause some reagents to be deleted, ergo length change.
-
- current_list_element = rand(1, cached_reagents.len)
-
- while(total_removed < amount)
- // There's nothing left in the container
- if(total_volume <= 0 || !cached_reagents.len)
- break
-
- if(current_list_element > cached_reagents.len)
- current_list_element = 1
-
- var/datum/reagent/target_holder = cached_reagents[current_list_element]
- var/remove_amt = min(amount - total_removed, round(amount / rand(2, initial_list_length), round(amount / 10, 0.01))) //double round to keep it at a somewhat even spread relative to amount without getting funky numbers.
- // If the logic above means removing really tiny amounts (or even zero if it's a remove amount of 10) instead choose a sensible smallish number
- // so this proc will actually finish instead of looping forever
- remove_amt = max(CHEMICAL_VOLUME_ROUNDING, remove_amt)
- remove_amt = remove_reagent(target_holder.type, remove_amt)
-
- current_list_element++
- total_removed += remove_amt
- handle_reactions()
-
- return round(total_removed, CHEMICAL_VOLUME_ROUNDING)
-
/**
* Removes all reagents either proportionally(amount is the direct volume to remove)
* when proportional the total volume of all reagents removed will equal to amount
diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
index e91aea78d59..e9b920c20e5 100644
--- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm
+++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
@@ -22,7 +22,7 @@
src.location = get_turf(location)
src.amount = amount
carry?.copy_to(chemholder, 20)
- carry?.remove_any(amount / efficiency)
+ carry?.remove_all(amount / efficiency)
/// A factory which produces clouds of smoke for the smoke machine.
/datum/effect_system/fluid_spread/smoke/chem/smoke_machine
diff --git a/code/modules/reagents/chemistry/recipes/cat2_medicines.dm b/code/modules/reagents/chemistry/recipes/cat2_medicines.dm
index c61a7739377..376a805e7d6 100644
--- a/code/modules/reagents/chemistry/recipes/cat2_medicines.dm
+++ b/code/modules/reagents/chemistry/recipes/cat2_medicines.dm
@@ -211,7 +211,6 @@
required_reagents = list(/datum/reagent/nitrogen = 3, /datum/reagent/acetone = 2)
required_catalysts = list(/datum/reagent/toxin/acid = 1)
mix_message = "The mixture turns into a tired reddish pink liquid."
- optimal_temp = 1
optimal_temp = 900
overheat_temp = 720
optimal_ph_min = 2
diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm
index 47fcc4ddda3..664b47e5bd6 100644
--- a/code/modules/reagents/reagent_containers/cups/_cup.dm
+++ b/code/modules/reagents/reagent_containers/cups/_cup.dm
@@ -418,7 +418,7 @@
if(reagents.total_volume == reagents.maximum_volume)
user.balloon_alert(user, "mop is full!")
return
- mop.reagents.remove_any(mop.reagents.total_volume * SQUEEZING_DISPERSAL_RATIO)
+ mop.reagents.remove_all(mop.reagents.total_volume * SQUEEZING_DISPERSAL_RATIO)
mop.reagents.trans_to(src, mop.reagents.total_volume, transferred_by = user)
user.balloon_alert(user, "mop squeezed")
else
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index 9deb6582508..a69810ca954 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -144,7 +144,7 @@
return
var/amount_lost = intensity * 5
- reagents.remove_any(amount_lost)
+ reagents.remove_all(amount_lost)
visible_message(span_warning("Some of [name]'s contents are let loose!"))
var/intensity_state = null
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index ec7b5c6b2f0..a1a241a136a 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -131,7 +131,7 @@
return
place_item_in_disposal(I, user)
update_appearance()
- return 1 //no afterattack
+ return TRUE //no afterattack
else
return ..()
@@ -327,19 +327,73 @@
desc = "A pneumatic waste disposal unit."
icon_state = "disposal"
interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
+ /// Reference to the mounted destination tagger for disposal bins with one mounted.
+ var/obj/item/dest_tagger/mounted_tagger
// attack by item places it in to disposal
-/obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/storage/bag/trash)) //Not doing component overrides because this is a specific type.
- var/obj/item/storage/bag/trash/bag = I
+/obj/machinery/disposal/bin/attackby(obj/item/weapon, mob/user, params)
+ if(istype(weapon, /obj/item/storage/bag/trash)) //Not doing component overrides because this is a specific type.
+ var/obj/item/storage/bag/trash/bag = weapon
to_chat(user, span_warning("You empty the bag."))
bag.atom_storage.remove_all(src)
update_appearance()
else
return ..()
-
// handle machine interaction
+/obj/machinery/disposal/bin/attackby_secondary(obj/item/weapon, mob/user, params)
+ if(istype(weapon, /obj/item/dest_tagger))
+ var/obj/item/dest_tagger/new_tagger = weapon
+ if(mounted_tagger)
+ balloon_alert(user, "already has a tagger!")
+ return
+ if(HAS_TRAIT(new_tagger, TRAIT_NODROP) || !user.transferItemToLoc(new_tagger, src))
+ balloon_alert(user, "stuck to your hand!")
+ return
+ new_tagger.moveToNullspace()
+ user.visible_message(span_notice("[user] snaps \the [new_tagger] onto [src]!"))
+ balloon_alert(user, "tagger returned")
+ playsound(src, 'sound/machines/click.ogg', 50, TRUE)
+ mounted_tagger = new_tagger
+ update_appearance()
+ return
+ else
+ return ..()
+
+/obj/machinery/disposal/bin/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(!mounted_tagger)
+ balloon_alert(user, "no destination tagger!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!user.put_in_hands(mounted_tagger))
+ balloon_alert(user, "destination tagger falls!")
+ mounted_tagger = null
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ user.visible_message(span_notice("[user] unhooks the [mounted_tagger] from [src]."))
+ balloon_alert(user, "tagger pulled")
+ playsound(src, 'sound/machines/click.ogg', 60, TRUE)
+ mounted_tagger = null
+ update_appearance(UPDATE_OVERLAYS)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/machinery/disposal/bin/examine(mob/user)
+ . = ..()
+ if(isnull(mounted_tagger))
+ . += span_notice("The destination tagger mount is empty.")
+ else
+ . += span_notice("\The [mounted_tagger] is hanging on the side. Right Click to remove.")
+
+/obj/machinery/disposal/bin/Destroy()
+ if(!isnull(mounted_tagger))
+ QDEL_NULL(mounted_tagger)
+ return ..()
+
+/obj/machinery/disposal/bin/on_deconstruction(disassembled)
+ . = ..()
+ if(!isnull(mounted_tagger))
+ mounted_tagger.forceMove(drop_location())
+ mounted_tagger = null
+
/obj/machinery/disposal/bin/ui_state(mob/user)
return GLOB.notcontained_state
@@ -419,6 +473,9 @@
if(flush)
. += "dispover-handle"
+ if(mounted_tagger)
+ . += "tagger_mount"
+
//only handle is shown if no power
if(machine_stat & NOPOWER || panel_open)
return
@@ -493,6 +550,10 @@
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
+/obj/machinery/disposal/bin/tagger/Initialize(mapload, obj/structure/disposalconstruct/make_from)
+ mounted_tagger = new /obj/item/dest_tagger(null)
+ return ..()
+
//Delivery Chute
/obj/machinery/disposal/delivery_chute
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index 10108c83048..a178bcd6ad5 100644
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -60,14 +60,14 @@
. = ..()
if(note)
if(!in_range(user, src))
- . += "There's a [note.name] attached to it. You can't read it from here."
+ . += span_info("There's a [EXAMINE_HINT(note.name)] attached to it. You can't read it from here.")
else
- . += "There's a [note.name] attached to it..."
+ . += span_info("There's a [EXAMINE_HINT(note.name)] attached to it...")
. += note.examine(user)
if(sticker)
- . += "There's a barcode attached to the side."
+ . += span_notice("There's a [EXAMINE_HINT("barcode")] attached to the side. The package is marked for [EXAMINE_HINT("export.")]")
if(sort_tag)
- . += "There's a sorting tag with the destination set to [GLOB.TAGGERLOCATIONS[sort_tag]]."
+ . += span_notice("There's a [EXAMINE_HINT("sorting tag")] with the destination set to [EXAMINE_HINT("[GLOB.TAGGERLOCATIONS[sort_tag]].")]")
/obj/item/delivery/proc/disposal_handling(disposal_source, obj/structure/disposalholder/disposal_holder, obj/machinery/disposal/disposal_machine, hasmob)
SIGNAL_HANDLER
@@ -102,7 +102,7 @@
if(note)
. += "[base_icon_state]_note"
if(sticker)
- . += "[base_icon_state]_tag"
+ . += "[base_icon_state]_barcode"
/obj/item/delivery/attackby(obj/item/item, mob/user, params)
if(istype(item, /obj/item/dest_tagger))
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 10ed939c55b..04f24637e08 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -351,7 +351,7 @@
playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
var/obj/item/reagent_containers/cup/glass/coffee/C = new /obj/item/reagent_containers/cup/glass/coffee(get_turf(pick(oview(1,src))))
chosenchem = pick(/datum/reagent/toxin/plasma,/datum/reagent/consumable/capsaicin,/datum/reagent/consumable/ethanol)
- C.reagents.remove_any(25)
+ C.reagents.remove_all(25)
C.reagents.add_reagent(chosenchem , 50)
C.name = "Cup of Suspicious Liquid"
C.desc = "It has a large hazard symbol printed on the side in fading ink."
@@ -395,7 +395,7 @@
var/obj/item/reagent_containers/cup/glass/coffee/C = new /obj/item/reagent_containers/cup/glass/coffee(get_turf(pick(oview(1,src))))
playsound(src, 'sound/machines/ding.ogg', 50, TRUE) //Ding! Your death coffee is ready!
chosenchem = pick(/datum/reagent/uranium,/datum/reagent/consumable/frostoil,/datum/reagent/medicine/ephedrine)
- C.reagents.remove_any(25)
+ C.reagents.remove_all(25)
C.reagents.add_reagent(chosenchem , 50)
C.name = "Cup of Suspicious Liquid"
C.desc = "It has a large hazard symbol printed on the side in fading ink."
diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm
index db02706f3a4..7520a800df0 100644
--- a/code/modules/research/machinery/_production.dm
+++ b/code/modules/research/machinery/_production.dm
@@ -385,6 +385,7 @@
finalize_build()
return
if(!materials.can_use_resource())
+ say("Unable to continue production, materials on hold.")
finalize_build()
return
@@ -392,6 +393,7 @@
var/list/design_materials = design.materials
if(!materials.mat_container.has_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1))
say("Unable to continue production, missing materials.")
+ finalize_build()
return
materials.use_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1, "built", "[design.name]")
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index d3f9c9b5a8d..ea4199e73c9 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -688,8 +688,28 @@
desc = "A miraculous chemical mix that grants human like intelligence to living beings."
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "potpink"
+ /// Are we being offered to a mob, and therefore is a ghost poll currently in progress for the sentient mob?
var/being_used = FALSE
var/sentience_type = SENTIENCE_ORGANIC
+ /// Reason for offering potion. This will be displayed in the poll alert to ghosts.
+ var/potion_reason
+
+/obj/item/slimepotion/slime/sentience/examine(mob/user)
+ . = ..()
+ . += span_notice("Alt-click to set potion offer reason. [potion_reason ? "Current reason: [span_warning(potion_reason)]" : null]")
+
+/obj/item/slimepotion/slime/sentience/Initialize(mapload)
+ register_context()
+ return ..()
+
+/obj/item/slimepotion/slime/sentience/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ context[SCREENTIP_CONTEXT_ALT_LMB] = "Set potion offer reason"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/item/slimepotion/slime/sentience/AltClick(mob/living/user)
+ if(!can_interact(user))
+ return
+ potion_reason = tgui_input_text(user, "Enter reason for offering potion", "Intelligence Potion", potion_reason, multiline = TRUE)
/obj/item/slimepotion/slime/sentience/attack(mob/living/dumb_mob, mob/user)
if(being_used || !isliving(dumb_mob))
@@ -703,8 +723,8 @@
if(!dumb_mob.compare_sentience_type(sentience_type)) // Will also return false if not a basic or simple mob, which are the only two we want anyway
balloon_alert(user, "invalid creature!")
return
- var/potion_reason = tgui_input_text(user, "For what reason?", "Intelligence Potion", multiline = TRUE, timeout = 2 MINUTES)
if(isnull(potion_reason))
+ balloon_alert(user, "no reason for offering set!")
return
balloon_alert(user, "offering...")
being_used = TRUE
diff --git a/code/modules/spells/spell_types/conjure/simian.dm b/code/modules/spells/spell_types/conjure/simian.dm
index 9306a393365..9fef5629d03 100644
--- a/code/modules/spells/spell_types/conjure/simian.dm
+++ b/code/modules/spells/spell_types/conjure/simian.dm
@@ -24,6 +24,10 @@
)
summon_amount = 4
+/datum/action/cooldown/spell/conjure/simian/Destroy()
+ . = ..()
+ QDEL_NULL(gorilla_transformation)
+
/datum/action/cooldown/spell/conjure/simian/level_spell(bypass_cap)
. = ..()
summon_amount++ // MORE, MOOOOORE
diff --git a/code/modules/spells/spell_types/shapeshift/gorilla.dm b/code/modules/spells/spell_types/shapeshift/gorilla.dm
index 938583f22b7..fe93c0f2bc4 100644
--- a/code/modules/spells/spell_types/shapeshift/gorilla.dm
+++ b/code/modules/spells/spell_types/shapeshift/gorilla.dm
@@ -2,8 +2,8 @@
/datum/action/cooldown/spell/shapeshift/gorilla
name = "Gorilla Form"
desc = "Take on the shape of a powerful gorilla."
+ button_icon_state = "return_to_monkey"
invocation = "B'NA NAH-SLEMA!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
-
possible_shapes = list(/mob/living/basic/gorilla)
diff --git a/code/modules/surgery/healing.dm b/code/modules/surgery/healing.dm
index c6294872823..f8609ab7e2c 100644
--- a/code/modules/surgery/healing.dm
+++ b/code/modules/surgery/healing.dm
@@ -38,6 +38,7 @@
implements = list(
TOOL_HEMOSTAT = 100,
TOOL_SCREWDRIVER = 65,
+ TOOL_WIRECUTTER = 60,
/obj/item/pen = 55)
repeatable = TRUE
time = 25
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index 78afbd9871e..1db1963e8d6 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -444,7 +444,7 @@
/// React to speach while hopped up on the high pitched voice juice
/obj/item/organ/internal/lungs/proc/handle_helium_speech(mob/living/carbon/breather, list/speech_args)
SIGNAL_HANDLER
- speech_args[SPEECH_SPANS] |= SPAN_HELIUM
+ speech_args[SPEECH_SPANS] |= SPAN_SMALL_VOICE
/// Gain hypernob effects if we have enough of the stuff
/obj/item/organ/internal/lungs/proc/consume_hypernoblium(mob/living/carbon/breather, datum/gas_mixture/breath, hypernob_pp, old_hypernob_pp)
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 07079e0f621..b499406362c 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -108,6 +108,7 @@
#include "cable_powernets.dm"
#include "card_mismatch.dm"
#include "cardboard_cutouts.dm"
+#include "cargo_selling.dm"
#include "chain_pull_through_space.dm"
#include "changeling.dm"
#include "chat_filter.dm"
diff --git a/code/modules/unit_tests/cargo_selling.dm b/code/modules/unit_tests/cargo_selling.dm
new file mode 100644
index 00000000000..31d90505bdf
--- /dev/null
+++ b/code/modules/unit_tests/cargo_selling.dm
@@ -0,0 +1,34 @@
+/// Makes sure exports work and things can be sold
+/datum/unit_test/cargo_selling
+
+/obj/item/cargo_unit_test_container
+
+/obj/item/cargo_unit_test_container/Initialize(mapload)
+ . = ..()
+ new /obj/item/cargo_unit_test_content(src)
+
+/obj/item/cargo_unit_test_content
+
+/datum/export/cargo_unit_test_container
+ cost = PAYCHECK_LOWER
+ export_types = list(/obj/item/cargo_unit_test_container)
+
+/datum/export/cargo_unit_test_content
+ cost = PAYCHECK_COMMAND
+ export_types = list(/obj/item/cargo_unit_test_content)
+
+/datum/unit_test/cargo_selling/Run()
+ var/obj/item/cargo_unit_test_container/box = allocate(/obj/item/cargo_unit_test_container)
+ var/obj/item/cargo_unit_test_container/box_skip_content = allocate(/obj/item/cargo_unit_test_container)
+
+ var/datum/export_report/report_one = export_item_and_contents(box, apply_elastic = FALSE)
+ if(isnull(report_one))
+ TEST_FAIL("called 'export_item_and_contents', but no export report was returned.")
+ var/value = counterlist_sum(report_one.total_value)
+ TEST_ASSERT_EQUAL(value, PAYCHECK_LOWER + PAYCHECK_COMMAND, "'export_item_and_contents' value didn't match expected value")
+
+ var/datum/export_report/report_two = export_single_item(box_skip_content, apply_elastic = FALSE)
+ if(isnull(report_two))
+ TEST_FAIL("called 'export_single_item', but no export report was returned.")
+ value = counterlist_sum(report_two.total_value)
+ TEST_ASSERT_EQUAL(value, PAYCHECK_LOWER, "'export_single_item' value didn't match expected value")
diff --git a/code/modules/unit_tests/required_map_items.dm b/code/modules/unit_tests/required_map_items.dm
index 5cbef645391..73499c83b6d 100644
--- a/code/modules/unit_tests/required_map_items.dm
+++ b/code/modules/unit_tests/required_map_items.dm
@@ -21,6 +21,7 @@
expected_types += /mob/living/carbon/human/species/monkey/punpun
expected_types += /obj/machinery/computer/communications
expected_types += /obj/machinery/drone_dispenser
+ expected_types += /obj/item/piggy_bank/vault
/datum/unit_test/required_map_items/Run()
setup_expected_types()
diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm
index 18a35576c36..80701705d20 100644
--- a/code/modules/unit_tests/simple_animal_freeze.dm
+++ b/code/modules/unit_tests/simple_animal_freeze.dm
@@ -142,6 +142,17 @@
/mob/living/simple_animal/hostile/zombie/nocorpse,
/mob/living/simple_animal/pet/gondola/funky,
/mob/living/simple_animal/pet/poppy,
+ /mob/living/simple_animal/hostile/fleshmind/slicer,
+ /mob/living/simple_animal/hostile/fleshmind/floater,
+ /mob/living/simple_animal/hostile/fleshmind/globber,
+ /mob/living/simple_animal/hostile/fleshmind/stunner,
+ /mob/living/simple_animal/hostile/fleshmind/hiborg,
+ /mob/living/simple_animal/hostile/fleshmind/himan,
+ /mob/living/simple_animal/hostile/fleshmind/treader,
+ /mob/living/simple_animal/hostile/fleshmind/phaser,
+ /mob/living/simple_animal/hostile/fleshmind/mechiver,
+ /mob/living/simple_animal/hostile/fleshmind/mauler_monkey,
+ /mob/living/simple_animal/hostile/fleshmind,
// DO NOT ADD NEW ENTRIES TO THIS LIST
// READ THE COMMENT ABOVE
diff --git a/code/modules/vehicles/motorized_wheelchair.dm b/code/modules/vehicles/motorized_wheelchair.dm
index 7d3b70baf95..973914a30c3 100644
--- a/code/modules/vehicles/motorized_wheelchair.dm
+++ b/code/modules/vehicles/motorized_wheelchair.dm
@@ -26,12 +26,15 @@
/obj/vehicle/ridden/wheelchair/motorized/Initialize(mapload)
. = ..()
+ add_component_parts()
+ refresh_parts()
+
+/obj/vehicle/ridden/wheelchair/motorized/proc/add_component_parts()
// Add tier 1 stock parts so that non-crafted wheelchairs aren't empty
component_parts += GLOB.stock_part_datums[/datum/stock_part/capacitor]
component_parts += GLOB.stock_part_datums[/datum/stock_part/servo]
component_parts += GLOB.stock_part_datums[/datum/stock_part/servo]
power_cell = new /obj/item/stock_parts/cell(src)
- refresh_parts()
/obj/vehicle/ridden/wheelchair/motorized/make_ridable()
AddElement(/datum/element/ridable, /datum/component/riding/vehicle/wheelchair/motorized)
@@ -221,3 +224,12 @@
visible_message(span_warning("A bomb appears in [src], what the fuck?"))
obj_flags |= EMAGGED
return TRUE
+
+///Version with slightly better components. Used by deathmatches.
+/obj/vehicle/ridden/wheelchair/motorized/improved
+
+/obj/vehicle/ridden/wheelchair/motorized/improved/add_component_parts()
+ component_parts += GLOB.stock_part_datums[/datum/stock_part/capacitor]
+ component_parts += GLOB.stock_part_datums[/datum/stock_part/servo/tier2]
+ component_parts += GLOB.stock_part_datums[/datum/stock_part/servo]
+ power_cell = new /obj/item/stock_parts/cell/upgraded/plus(src)
diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm
index 4e1f115e55c..9e320c2f8de 100644
--- a/code/modules/vehicles/scooter.dm
+++ b/code/modules/vehicles/scooter.dm
@@ -49,6 +49,8 @@
var/board_item_type = /obj/item/melee/skateboard
///Stamina drain multiplier
var/instability = 10
+ ///If true, riding the skateboard with walk intent on will prevent crashing.
+ var/can_slow_down = TRUE
/obj/vehicle/ridden/scooter/skateboard/Initialize(mapload)
. = ..()
@@ -87,9 +89,11 @@
. = ..()
if(!bumped_thing.density || !has_buckled_mobs() || world.time < next_crash)
return
+ var/mob/living/rider = buckled_mobs[1]
+ if(rider.move_intent == MOVE_INTENT_WALK && can_slow_down) //Going slow prevents you from crashing.
+ return
next_crash = world.time + 10
- var/mob/living/rider = buckled_mobs[1]
rider.adjustStaminaLoss(instability*6)
playsound(src, 'sound/effects/bang.ogg', 40, TRUE)
if(!iscarbon(rider) || rider.getStaminaLoss() >= 100 || grinding || iscarbon(bumped_thing))
@@ -181,13 +185,28 @@
board_item_type = /obj/item/melee/skateboard/pro
instability = 6
-/obj/vehicle/ridden/scooter/skateboard/hoverboard/
+/obj/vehicle/ridden/scooter/skateboard/pro/make_ridable()
+ AddElement(/datum/element/ridable, /datum/component/riding/vehicle/scooter/skateboard/pro)
+
+/obj/vehicle/ridden/scooter/skateboard/hoverboard
name = "hoverboard"
desc = "A blast from the past, so retro!"
board_item_type = /obj/item/melee/skateboard/hoverboard
instability = 3
icon_state = "hoverboard_red"
+/obj/vehicle/ridden/scooter/skateboard/hoverboard/make_ridable()
+ AddElement(/datum/element/ridable, /datum/component/riding/vehicle/scooter/skateboard/hover)
+
+/obj/vehicle/ridden/scooter/skateboard/hoverboard/can_z_move(direction, turf/start, turf/destination, z_move_flags = ZMOVE_FLIGHT_FLAGS, mob/living/rider)
+ . = ..()
+ if(!.)
+ return
+ if(rider && (z_move_flags & ZMOVE_CAN_FLY_CHECKS) && direction == UP)
+ if(z_move_flags & ZMOVE_FEEDBACK)
+ to_chat(rider, span_warning("[src] [p_are()] not powerful enough to fly upwards."))
+ return FALSE
+
/obj/vehicle/ridden/scooter/skateboard/hoverboard/admin
name = "\improper Board Of Directors"
desc = "The engineering complexity of a spaceship concentrated inside of a board. Just as expensive, too."
@@ -199,6 +218,7 @@
name = "improvised skateboard"
desc = "An unfinished scooter which can only barely be called a skateboard. It's still rideable, but probably unsafe. Looks like you'll need to add a few rods to make handlebars."
board_item_type = /obj/item/melee/skateboard/improvised
+ instability = 12
//CONSTRUCTION
/obj/item/scooter_frame
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 20fb0a86b95..7078881ba0c 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -280,6 +280,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
if(onstation && !onstation_override)
AddComponent(/datum/component/payment, 0, SSeconomy.get_dep_account(payment_department), PAYMENT_VENDING)
GLOB.vending_machines_to_restock += src //We need to keep track of the final onstation vending machines so we can keep them restocked.
+ register_context()
/obj/machinery/vending/Destroy()
QDEL_NULL(wires)
@@ -342,6 +343,24 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
if(light_mask && !(machine_stat & BROKEN) && powered())
. += emissive_appearance(icon, light_mask, src)
+/obj/machinery/vending/examine(mob/user)
+ . = ..()
+ if(isnull(refill_canister))
+ return // you can add the comment here instead
+ if((total_loaded_stock() / total_max_stock()) < 1)
+ . += span_notice("\The [src] can be restocked with [span_boldnotice("\a [initial(refill_canister.machine_name)] [initial(refill_canister.name)]")] with the panel open.")
+ else
+ . += span_notice("\The [src] is fully stocked.")
+ if(credits_contained < CREDITS_DUMP_THRESHOLD && credits_contained > 0)
+ . += span_notice("It should have a handfull of credits stored based on the missing items.")
+ else if (credits_contained > PAYCHECK_CREW)
+ . += span_notice("It should have at least a full paycheck worth of credits inside!")
+ /**
+ * Intentionally leaving out a case for zero credits as it should be covered by the vending machine's stock being full,
+ * or covered by first case if items were returned.
+ */
+
+
/obj/machinery/vending/atom_break(damage_flag)
. = ..()
if(!.)
@@ -1101,6 +1120,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
vending_machine_input[inserted_item.type] = 1
loaded_items++
+
/obj/machinery/vending/unbuckle_mob(mob/living/buckled_mob, force = FALSE, can_fall = TRUE)
if(!force)
return
@@ -1578,11 +1598,13 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
* Arguments:
* * loaded_item - the item being loaded
* * user - the user doing the loading
+ * * send_message - should we send a message to the user if the item can't be loaded? Either a to_chat or a speak depending on vending type.
*/
-/obj/machinery/vending/proc/canLoadItem(obj/item/loaded_item, mob/user)
+/obj/machinery/vending/proc/canLoadItem(obj/item/loaded_item, mob/user, send_message = TRUE)
if(!length(loaded_item.contents) && ((loaded_item.type in products) || (loaded_item.type in premium) || (loaded_item.type in contraband)))
return TRUE
- to_chat(user, span_warning("[src] does not accept [loaded_item]!"))
+ if(send_message)
+ to_chat(user, span_warning("[src] does not accept [loaded_item]!"))
return FALSE
/obj/machinery/vending/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
@@ -1611,6 +1633,32 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
credits_contained = max(0, credits_contained - credits_to_remove)
SSblackbox.record_feedback("amount", "vending machine looted", holochip.credits)
+/obj/machinery/vending/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(tilted && !held_item)
+ context[SCREENTIP_CONTEXT_LMB] = "Right machine"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(held_item?.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = panel_open ? "Close panel" : "Open panel"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(panel_open && held_item?.tool_behaviour == TOOL_WRENCH)
+ context[SCREENTIP_CONTEXT_LMB] = anchored ? "Unsecure" : "Secure"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(panel_open && held_item?.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(!isnull(held_item) && (vending_machine_input[held_item.type] || canLoadItem(held_item, user, send_message = FALSE)))
+ context[SCREENTIP_CONTEXT_LMB] = "Load item"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(panel_open && istype(held_item, refill_canister))
+ context[SCREENTIP_CONTEXT_LMB] = "Restock vending machine[credits_contained ? " and collect credits" : null]"
+ return TRUE
+ return NONE
+
/obj/machinery/vending/custom
name = "Custom Vendor"
icon_state = "custom"
@@ -1636,16 +1684,19 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
if(id_card?.registered_account && id_card.registered_account == linked_account)
return TRUE
-/obj/machinery/vending/custom/canLoadItem(obj/item/loaded_item, mob/user)
+/obj/machinery/vending/custom/canLoadItem(obj/item/loaded_item, mob/user, send_message = TRUE)
. = FALSE
if(loaded_item.flags_1 & HOLOGRAM_1)
- speak("This vendor cannot accept nonexistent items.")
+ if(send_message)
+ speak("This vendor cannot accept nonexistent items.")
return
if(loaded_items >= max_loaded_items)
- speak("There are too many items in stock.")
+ if(send_message)
+ speak("There are too many items in stock.")
return
if(isstack(loaded_item))
- speak("Loose items may cause problems, try to use it inside wrapping paper.")
+ if(send_message)
+ speak("Loose items may cause problems, try to use it inside wrapping paper.")
return
if(loaded_item.custom_price)
return TRUE
diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm
index 2877a403347..17555e66a87 100644
--- a/code/modules/vending/autodrobe.dm
+++ b/code/modules/vending/autodrobe.dm
@@ -57,7 +57,7 @@
/obj/item/clothing/suit/costume/gothcoat = 1,
/obj/item/clothing/glasses/eyepatch = 1,
/obj/item/clothing/glasses/eyepatch/medical = 1,
- /obj/item/clothing/under/costume/gi = 1,
+ /obj/item/clothing/under/costume/gi = 4,
/obj/item/clothing/head/soft/propeller_hat = 1,
/obj/item/clothing/neck/bowtie/rainbow = 1,
),
diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm
index 47276acb259..bf260ab76bc 100644
--- a/code/modules/vending/clothesmate.dm
+++ b/code/modules/vending/clothesmate.dm
@@ -82,7 +82,7 @@
/obj/item/clothing/under/costume/kilt = 1,
/obj/item/clothing/under/dress/striped = 1,
/obj/item/clothing/under/dress/sailor = 1,
- /obj/item/clothing/under/dress/redeveninggown = 1,
+ /obj/item/clothing/under/dress/eveninggown = 1,
/obj/item/clothing/under/misc/pj/blue = 2,
/obj/item/clothing/under/misc/pj/red = 2,
),
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index 6c4038067d0..961d205e7e2 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -495,6 +495,7 @@
/obj/item/clothing/under/rank/civilian/lawyer/black/skirt = 1,
/obj/item/clothing/shoes/laceup = 2,
/obj/item/radio/headset/headset_srv = 2,
+ /obj/item/storage/box/evidence = 2,
)
refill_canister = /obj/item/vending_refill/wardrobe/law_wardrobe
payment_department = ACCOUNT_SRV
diff --git a/html/changelogs/AutoChangeLog-pr-2411.yml b/html/changelogs/AutoChangeLog-pr-2411.yml
deleted file mode 100644
index 2df5d2437e0..00000000000
--- a/html/changelogs/AutoChangeLog-pr-2411.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Iajret"
-delete-after: True
-changes:
- - balance: "you can't take pride pin quirk anymore."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-2413.yml b/html/changelogs/AutoChangeLog-pr-2413.yml
deleted file mode 100644
index c4cba0e8676..00000000000
--- a/html/changelogs/AutoChangeLog-pr-2413.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "JohnFulpWillard"
-delete-after: True
-changes:
- - refactor: "Instruments now use TGUI."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-2414.yml b/html/changelogs/AutoChangeLog-pr-2414.yml
deleted file mode 100644
index 47198b1bc58..00000000000
--- a/html/changelogs/AutoChangeLog-pr-2414.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "13spacemen"
-delete-after: True
-changes:
- - bugfix: "Fixed some poll alerts runtiming"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-2416.yml b/html/changelogs/AutoChangeLog-pr-2416.yml
deleted file mode 100644
index 14be3305632..00000000000
--- a/html/changelogs/AutoChangeLog-pr-2416.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "ArcaneMusic"
-delete-after: True
-changes:
- - code_imp: "We now log how many ores spawned on lavaland each round, as well as the sizes of all of the ore vents."
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-03.yml b/html/changelogs/archive/2024-03.yml
index 696f2acf820..95f9088564d 100644
--- a/html/changelogs/archive/2024-03.yml
+++ b/html/changelogs/archive/2024-03.yml
@@ -721,6 +721,225 @@
dual-wield cone.
- balance: Thermal pistol crates are now 2000 credits, up from 1400 credits.
2024-03-15:
+ 13spacemen:
+ - bugfix: Fixed some poll alerts runtiming
ArcaneMusic:
- image: Your station's standard issue E-219 laser turrets within the AI upload
and AI satellite have been updated to a newer, shinier model.
+ - code_imp: We now log how many ores spawned on lavaland each round, as well as
+ the sizes of all of the ore vents.
+ Iajret:
+ - balance: you can't take pride pin quirk anymore.
+ JohnFulpWillard:
+ - refactor: Instruments now use TGUI.
+2024-03-16:
+ Chelxox:
+ - balance: Increases the double-edged energy sword's block chance from 45 to 65.
+ Ghommie:
+ - bugfix: Fixed the "Fenced Goods" black market category.
+ - balance: Removed the LTSRBT from cargo and added it to the blackmarket, reduced
+ the price from 4000 to 625 on average.
+ - balance: The time it takes for captured mobs to be automatically sent back to
+ the station from the holding facility has been increased from 3-4 minutes to
+ 6.
+ - rscadd: You can buy mobs captured by contractors, traitors and pirates from the
+ black market and have them sent back to the station in advance. For safety,
+ they'll also be handcuffed (not always) upon delivery.
+ - rscadd: Human mobs sold by pirates are not deleted anymore. Instead, they're now
+ captured and sent to the holding facility.
+ Hatterhat:
+ - balance: Cybersun Industries is now rolling out larger Hoshi chassis in order
+ to mitigate a heatsink issue, at the cost of making them unable to be stored
+ quite so easily.
+ Jacquerel:
+ - image: Updated button icon for Summon Simians and Gorilla Transformation.
+ projectkepler-ru:
+ - rscadd: The Bartender's double barrel shotgun can be purchased for 1,800 credits
+ on goodie tab
+2024-03-17:
+ 00-Steven:
+ - sound: Placing paper in a paperbin is no longer silent.
+ ArcaneMusic:
+ - qol: Vending machines now have contextual tooltips for interactions with tools/objects/restocking/returns.
+ - qol: Vending machines can now be examined to see what type of restock module they
+ need to be refilled.
+ Bilbo367:
+ - bugfix: now spiders get extra damage by fly swatter
+ DaCoolBoss:
+ - qol: The LawDrobe now stocks two (2) boxes of evidence bags.
+ FlufflesTheDog:
+ - bugfix: Cleanbots once again require an emag to spray acid on people. They can
+ still acid trash objects normally.
+ - bugfix: Psyker vision no longer causes infinitely worsening lag
+ Ghommie:
+ - rscadd: Added a possible kit to the special syndie bundle B, which also has uses
+ these collars.
+ - balance: The pro skateboard and hoverboard are now faster.
+ - balance: The improvised skateboard is a nick more unstable than the standard,
+ so the two aren't exactly the same.
+ - rscadd: Riding a skateboard on walk intent will prevent you from crashing into
+ things, at the cost of speed.
+ - rscadd: 'hoverboards now actually hovers and can be used even in zero g. There
+ are a caveat to it: It cannot be ridden on open space gaps deeper than one level
+ or actual space, unless there''re objects that prevent falls, like lattice or
+ catwalks.'
+ - rscadd: Added 20+ modifiers to the deathmatch minigame, which can be enabled by
+ the host.
+ - rscadd: Ice cream and frozen treats now have a chilling effect.
+ - rscadd: You can add a scoop of ice cream on waffles.
+ - balance: added an action speed modifier to the generic food haste effect (you
+ do things, and not just run, an itsy bitsy faster)
+ Jacquerel:
+ - balance: If you work out under heavier-than-earth gravity, you will get gains
+ faster.
+ JohnFulpWillard:
+ - bugfix: Security camera consoles no longer have infinite range.
+ - bugfix: Human AI now closes regular AI job positions.
+ - bugfix: Time on servers hosted outside of the UK should now properly have their
+ timezone offset.
+ Kapu1178:
+ - bugfix: Modsuit Pathfinder module is significantly better at finding it's destination.
+ Melbert:
+ - bugfix: Fixes polling for things causing said things to have their planes and
+ layers screwed up (causing random objects or mobs to appear above runechat and
+ blindness, for example)
+ - rscadd: MODsuits now use the "suit charge" HUD element to show how much charge
+ they have left, rather than a screen alert
+ Nerev4r:
+ - image: The bat harpy wings are now three-way recolorable, with a fluffless version.
+ Rhials:
+ - qol: Posters now return to your hand when the hanging process is cancelled, or
+ they are snipped down.
+ - qol: Abductor posters can now only be cut down with abductor-tier wirecutters.
+ SyncIt21:
+ - bugfix: Lathes don't hang if materials run out mid printing. Also displays message
+ if materials are put on hold while printing
+ Tattle:
+ - rscadd: Getting shrunk makes you talk small
+ Webcomicartist:
+ - bugfix: Fixed RLD,RTD and Rebar crossbow not being storable on the engineering
+ winter coat slot.
+ intercepti0n:
+ - bugfix: Fixed cap overlay being added even when pipe is not hidden.
+ - bugfix: Fixed cap overlay being visible on turfs that are masked with darkness
+ plane, but still visible via mesons, cameras etc.
+ kawoppi:
+ - qol: added tooltip to newscaster to tell people that they need to insert paper
+ if they want to print a newspaper
+2024-03-18:
+ Ghommie:
+ - bugfix: FIXED CARGO EXPORTS!
+ RatFromTheJungle:
+ - balance: The Hoshi is once more, reduced in size, because it turns out bigger,
+ does not always equal better with heatsinks!
+2024-03-19:
+ 13spacemen:
+ - qol: Sentience potion reason is set via alt-click
+ ArcaneMusic:
+ - bugfix: Tramstation now has a pepperspray dispenser in it's science security outpost,
+ as with other security outposts.
+ Bilbo367:
+ - bugfix: Deathmatch players should no longer be able to roll any midround antagonist.
+ - bugfix: ID locking and unlocking work.
+ Exester509:
+ - bugfix: Plasmamen will become human in the deathmatch so they don't instantly
+ die
+ JohnFulpWillard:
+ - bugfix: Action abilities hidden from players are now not shown to observers either.
+ Momo8289:
+ - bugfix: You can no longer determine the result of a dice roll from off-screen.
+ Rhials:
+ - bugfix: Plasmamen nukie reinforcements are now properly given a plasmaman-safe
+ outfit.
+ Runa-Dacino:
+ - bugfix: fixed consecutive tgui text input inheriting user input from previous
+ input form within the same proc
+ SyncIt21:
+ - bugfix: Selling stuff in cargo gives us money again.
+ san7890:
+ - bugfix: If you place a headset on a non-Poly Parrot, you should be able to remove
+ it from the parrot as-expected now rather than having the parrot send it to
+ the shadow realm.
+2024-03-20:
+ 00-Steven:
+ - rscadd: Hemiplegic now lets you pick which side is disabled.
+ - rscadd: The medical records text for hemiplegics now shows which side is disabled.
+ ArcaneMusic:
+ - rscadd: Disposal bins can now hold destination taggers using right click, and
+ can be removed the same way. Some disposal bins should have some pre-loaded
+ as well.
+ - code_imp: Mineral logging now collects ore names more cleanly.
+ AstraFarera:
+ - bugfix: fixed a few Dyne things
+ Bilbo367:
+ - admin: cluster bombs stop spamming admin logs
+ Ghommie:
+ - bugfix: The deathmatch modifiers modal menu can actually be opened now. Also fixed
+ a bunch of issues it had.
+ - rscadd: Added a persistent piggy bank to the vault, which can carry up to a modest
+ 2000 credits worth of dosh between rounds.
+ JohnFulpWillard:
+ - bugfix: The captain's safe is no longer on the wall, therefore cannot be cheesed
+ by breaking the wall it sits on.
+ - bugfix: Tearing down a wall that a safe is on now drops the safe with its contents,
+ rather than dropping the contents onto the floor. The safe's contents cannot
+ be interacted with while it's not on a wall.
+ JungleRat:
+ - balance: Security Belts now hold a max weight of 11, which is roughly 3 normal
+ sized items, 5 small sized items, and 11 tiny sized items.
+ LT3:
+ - bugfix: Export barcodes are visible on wrapped packages
+ Melbert:
+ - refactor: Infrared emitters now function better (or at least more how you would
+ expect them) (hopefully). Report any oddities
+ Nidvex250:
+ - qol: Can now use wirecutters when performing Tend Wounds.
+ Rhials:
+ - code_imp: Deathmatch files, map names, and keys should all be consistent now.
+ RikuTheKiller:
+ - balance: Smoke Machines, Showers, Vapes, etc will no longer arbitrarily delete
+ a random amount of the chems they are processing
+ SomeRandomOwl:
+ - admin: Deathmatch Bombs, Smoke, and Liquid floods are now suppressed in the logs
+ and will not actively spam admin logs.
+ - code_imp: adds a area flag to suppress logging for floods and bombs
+ - rscadd: Added new camera positions to blueshift, there is a bit more coverage
+ now in areas where there was a severe lack of coverage.
+ - bugfix: Fixed some floating cameras in blueshift.
+ Thlumyn:
+ - bugfix: removed some varedited turfs from the slaver syndie shuttle
+ - bugfix: removed some roundstart active turfs from syndie outpost and pirate fort
+ space ruin
+ antropod:
+ - bugfix: Vending machine no longer shocks you twice when you pulse high voltage
+ wire.
+ - bugfix: Airlock wires window closes when you get shocked
+ san7890:
+ - bugfix: Prisoners who are teleported to the Nanotrasen Work Camp should now always
+ be stripped of their gear to prevent escapes.
+ sqnztb:
+ - balance: blueshift's command bunker is more secure
+ xXPawnStarrXx:
+ - image: modified gi sprites, making them more customisable.
+ - image: added gags sprites for; Sailor dress, Evening gown, Cardigan skirts and
+ striped dresses.
+2024-03-22:
+ ArcaneMusic:
+ - bugfix: Vending machines now show the correct vending refill name when examine
+ and not full.
+ Higgin:
+ - bugfix: basic mob pirates no longer drop fake energy weapon lenses when shooting
+ you to death with lasers.
+ Iajret:
+ - balance: Entombed and Underworld Connections are moved to veteran only quirks
+ JohnFulpWillard:
+ - bugfix: Clients joining a round twice as a crewmember (via admins or otherwise)
+ no longer break security/medical record consoles permanently.
+ Singul0:
+ - bugfix: You can't complete a genetic makeup scan when the targets moves out of
+ target anymore using a genetic sequencer
+ Xander3359:
+ - bugfix: Fix hack comms console/locate weakpoint objective showing up multiple
+ times
+ Zergspower:
+ - rscadd: ah shit
diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi
index e1c5db84d22..afa886f2c18 100755
Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ
diff --git a/icons/hud/screen_gen.dmi b/icons/hud/screen_gen.dmi
index 2f4dd60b5d7..9792124d1b6 100644
Binary files a/icons/hud/screen_gen.dmi and b/icons/hud/screen_gen.dmi differ
diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi
index 39ea5810417..fb8c121218f 100644
Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ
diff --git a/icons/mob/clothing/neck.dmi b/icons/mob/clothing/neck.dmi
index 5440bf9d99d..7895a3ac7fa 100644
Binary files a/icons/mob/clothing/neck.dmi and b/icons/mob/clothing/neck.dmi differ
diff --git a/icons/mob/clothing/under/costume.dmi b/icons/mob/clothing/under/costume.dmi
index 26e8eec9f20..a3b6c7e5050 100644
Binary files a/icons/mob/clothing/under/costume.dmi and b/icons/mob/clothing/under/costume.dmi differ
diff --git a/icons/mob/clothing/under/dress.dmi b/icons/mob/clothing/under/dress.dmi
index be6e7715c48..e6cd3c27ac5 100644
Binary files a/icons/mob/clothing/under/dress.dmi and b/icons/mob/clothing/under/dress.dmi differ
diff --git a/icons/obj/clothing/neck.dmi b/icons/obj/clothing/neck.dmi
index 6027b0022c4..986d9bd9158 100644
Binary files a/icons/obj/clothing/neck.dmi and b/icons/obj/clothing/neck.dmi differ
diff --git a/icons/obj/clothing/under/costume.dmi b/icons/obj/clothing/under/costume.dmi
index b58f96b82d9..90bdc205d52 100644
Binary files a/icons/obj/clothing/under/costume.dmi and b/icons/obj/clothing/under/costume.dmi differ
diff --git a/icons/obj/clothing/under/dress.dmi b/icons/obj/clothing/under/dress.dmi
index d38b53746be..a79f390cd53 100644
Binary files a/icons/obj/clothing/under/dress.dmi and b/icons/obj/clothing/under/dress.dmi differ
diff --git a/icons/obj/devices/assemblies.dmi b/icons/obj/devices/assemblies.dmi
index aa3476eea72..c1b0fd05f13 100644
Binary files a/icons/obj/devices/assemblies.dmi and b/icons/obj/devices/assemblies.dmi differ
diff --git a/icons/obj/pipes_n_cables/disposal.dmi b/icons/obj/pipes_n_cables/disposal.dmi
index 8487f3dd58c..41207b7a7cd 100644
Binary files a/icons/obj/pipes_n_cables/disposal.dmi and b/icons/obj/pipes_n_cables/disposal.dmi differ
diff --git a/icons/obj/storage/wrapping.dmi b/icons/obj/storage/wrapping.dmi
index 944a6b84752..9d8e757ea15 100644
Binary files a/icons/obj/storage/wrapping.dmi and b/icons/obj/storage/wrapping.dmi differ
diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi
index 50861b248d5..85367e1c47b 100644
Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ
diff --git a/modular_nova/master_files/code/game/objects/items/dualsaber.dm b/modular_nova/master_files/code/game/objects/items/dualsaber.dm
new file mode 100644
index 00000000000..df07ddd2a08
--- /dev/null
+++ b/modular_nova/master_files/code/game/objects/items/dualsaber.dm
@@ -0,0 +1,3 @@
+// Nerfs block_chance from the original 75
+/obj/item/dualsaber
+ block_chance = 65
diff --git a/modular_nova/master_files/code/modules/cargo/goodies.dm b/modular_nova/master_files/code/modules/cargo/goodies.dm
index ecc2cfa4852..df8dd118032 100644
--- a/modular_nova/master_files/code/modules/cargo/goodies.dm
+++ b/modular_nova/master_files/code/modules/cargo/goodies.dm
@@ -1,35 +1,50 @@
/datum/supply_pack/goody/dumdum38
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/match38
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/rubber
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/mars_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/Survivalknives_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/ballistic_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/disabler_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/energy_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/laser_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/hell_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/thermal_single
+ access_view = FALSE
special = TRUE
/datum/supply_pack/goody/dyespray
special = TRUE
+
+/datum/supply_pack/goody/double_barrel
+ special = TRUE
+ access_view = FALSE
diff --git a/modular_nova/master_files/code/modules/client/preferences/middleware/food.dm b/modular_nova/master_files/code/modules/client/preferences/middleware/food.dm
index 824c8c17427..8bb78801a41 100644
--- a/modular_nova/master_files/code/modules/client/preferences/middleware/food.dm
+++ b/modular_nova/master_files/code/modules/client/preferences/middleware/food.dm
@@ -117,7 +117,6 @@ GLOBAL_DATUM_INIT(food_prefs_menu, /datum/food_prefs_menu, new)
"liked" = 0,
"disliked" = 0,
"toxic" = 0,
-
)
for(var/food_entry in GLOB.food_defaults)
@@ -139,7 +138,7 @@ GLOBAL_DATUM_INIT(food_prefs_menu, /datum/food_prefs_menu, new)
var/list/data = list(
"selection" = preferences.food_preferences,
"enabled" = preferences.food_preferences["enabled"],
- "invalid" = is_food_invalid(preferences, counts),
+ "invalid" = is_food_invalid(counts),
"race_disabled" = !species.allows_food_preferences(),
"limits" = list(
"max_liked" = MAXIMUM_LIKES,
@@ -152,7 +151,7 @@ GLOBAL_DATUM_INIT(food_prefs_menu, /datum/food_prefs_menu, new)
return data
/// Checks the provided preferences datum to make sure the food pref values are valid. Does not check if the food preferences value is null.
-/datum/food_prefs_menu/proc/is_food_invalid(datum/preferences/preferences, counts)
+/datum/food_prefs_menu/proc/is_food_invalid(counts)
if(counts["liked"] > MAXIMUM_LIKES)
return "too many liked choices"
if(counts["disliked"] < MINIMUM_REQUIRED_DISLIKES)
diff --git a/modular_nova/master_files/code/modules/clothing/towels.dm b/modular_nova/master_files/code/modules/clothing/towels.dm
index 2712db1a267..4534370808a 100644
--- a/modular_nova/master_files/code/modules/clothing/towels.dm
+++ b/modular_nova/master_files/code/modules/clothing/towels.dm
@@ -315,7 +315,7 @@
. = ..() // This isn't really needed, but I'm including it in case we ever get dyeable towels.
// Washing allows you to remove all reagents from a towel, so it comes out clean!
- reagents.remove_any(reagents.total_volume)
+ reagents.remove_all(reagents.total_volume)
set_wet(FALSE, FALSE)
make_used(null, silent = TRUE)
@@ -459,7 +459,7 @@
reagents.trans_to(target, amount * (1 - loss_factor), no_react = TRUE, transferred_by = user)
if(loss_factor && reagents.total_volume)
- reagents.remove_any(amount * loss_factor)
+ reagents.remove_all(amount * loss_factor)
if(!reagents.total_volume)
set_wet(FALSE, !make_used)
diff --git a/modular_nova/master_files/code/modules/clothing/under/skirts_dresses.dm b/modular_nova/master_files/code/modules/clothing/under/skirts_dresses.dm
index d81b9b76a34..0d98dae0eda 100644
--- a/modular_nova/master_files/code/modules/clothing/under/skirts_dresses.dm
+++ b/modular_nova/master_files/code/modules/clothing/under/skirts_dresses.dm
@@ -36,6 +36,9 @@
desc = "Formal skirt."
icon_state = "skirt_swept"
body_parts_covered = GROIN
+ greyscale_config = null
+ greyscale_config_worn = null
+ greyscale_colors = null
/obj/item/clothing/under/dress/skirt/nova/striped_skirt
name = "red bra and striped skirt"
@@ -44,6 +47,9 @@
body_parts_covered = CHEST|GROIN|LEGS
can_adjust = TRUE
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
+ greyscale_config = null
+ greyscale_config_worn = null
+ greyscale_colors = null
/obj/item/clothing/under/dress/skirt/nova/red_skirt
name = "red bra and skirt"
@@ -52,6 +58,9 @@
body_parts_covered = CHEST|GROIN|LEGS
can_adjust = TRUE
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
+ greyscale_config = null
+ greyscale_config_worn = null
+ greyscale_colors = null
/obj/item/clothing/under/dress/skirt/nova/black_skirt
name = "black bra and skirt"
@@ -60,6 +69,9 @@
body_parts_covered = CHEST|GROIN|LEGS
can_adjust = TRUE
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
+ greyscale_config = null
+ greyscale_config_worn = null
+ greyscale_colors = null
/obj/item/clothing/under/dress/skirt/nova/turtleskirt_knit //Essentially the same as the Turtleneck Skirt but with a different texture
name = "cableknit skirt"
diff --git a/modular_nova/master_files/icons/mob/sprite_accessory/wings.dmi b/modular_nova/master_files/icons/mob/sprite_accessory/wings.dmi
index 16070100737..41717915c65 100644
Binary files a/modular_nova/master_files/icons/mob/sprite_accessory/wings.dmi and b/modular_nova/master_files/icons/mob/sprite_accessory/wings.dmi differ
diff --git a/modular_nova/master_files/icons/obj/clothing/under/skirts_dresses.dmi b/modular_nova/master_files/icons/obj/clothing/under/skirts_dresses.dmi
index 588c3a012f1..920a704c226 100644
Binary files a/modular_nova/master_files/icons/obj/clothing/under/skirts_dresses.dmi and b/modular_nova/master_files/icons/obj/clothing/under/skirts_dresses.dmi differ
diff --git a/modular_nova/modules/aesthetics/disposals/icons/disposals.dmi b/modular_nova/modules/aesthetics/disposals/icons/disposals.dmi
index 984cf9a4bb1..4538c6da531 100644
Binary files a/modular_nova/modules/aesthetics/disposals/icons/disposals.dmi and b/modular_nova/modules/aesthetics/disposals/icons/disposals.dmi differ
diff --git a/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm b/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm
index 305a4547c0f..f5d21682f4d 100644
--- a/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm
+++ b/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/wings.dm
@@ -200,6 +200,7 @@
/datum/sprite_accessory/wings/mammal/harpy/bat
name = "Harpy (Bat)"
icon_state = "harpybat"
+ color_src = USE_MATRIXED_COLORS
/datum/sprite_accessory/wings/mammal/top/harpy/alt
name = "Harpy (Top - Alt)"
@@ -212,6 +213,11 @@
/datum/sprite_accessory/wings/mammal/top/harpy/bat
name = "Harpy (Top - Bat)"
icon_state = "harpybat_top"
+ color_src = USE_MATRIXED_COLORS
+
+/datum/sprite_accessory/wings/mammal/top/harpy/bat/fluffless
+ name = "Harpy (Top - Bat - Fluffless)"
+ icon_state = "harpybat_fluffless_top"
/datum/sprite_accessory/wings/mammal/pterodactyl
name = "Pterodactyl"
diff --git a/modular_nova/modules/loadouts/loadout_items/under/loadout_datum_under.dm b/modular_nova/modules/loadouts/loadout_items/under/loadout_datum_under.dm
index ee2360b1c7e..51db0efdcf2 100644
--- a/modular_nova/modules/loadouts/loadout_items/under/loadout_datum_under.dm
+++ b/modular_nova/modules/loadouts/loadout_items/under/loadout_datum_under.dm
@@ -776,7 +776,7 @@ GLOBAL_LIST_INIT(loadout_miscunders, generate_loadout_items(/datum/loadout_item/
/datum/loadout_item/under/formal/red_gown
name = "Red Evening Gown"
- item_path = /obj/item/clothing/under/dress/redeveninggown
+ item_path = /obj/item/clothing/under/dress/eveninggown
/datum/loadout_item/under/formal/sailor
name = "Sailor Suit"
diff --git a/modular_nova/modules/mapping/code/wardrobes.dm b/modular_nova/modules/mapping/code/wardrobes.dm
index 29fbcd1a5b2..900b8b76b94 100644
--- a/modular_nova/modules/mapping/code/wardrobes.dm
+++ b/modular_nova/modules/mapping/code/wardrobes.dm
@@ -1,4 +1,4 @@
-/obj/machinery/vending/wardrobe/canLoadItem(obj/item/I,mob/user)
+/obj/machinery/vending/wardrobe/canLoadItem(obj/item/I, mob/user, send_message = TRUE)
return (I.type in products)
/obj/machinery/vending/wardrobe/syndie_wardrobe
diff --git a/modular_nova/modules/modular_weapons/code/company_and_or_faction_based/saibasan/laser_guns.dm b/modular_nova/modules/modular_weapons/code/company_and_or_faction_based/saibasan/laser_guns.dm
index 1c767ad5ca8..c3a0b737297 100644
--- a/modular_nova/modules/modular_weapons/code/company_and_or_faction_based/saibasan/laser_guns.dm
+++ b/modular_nova/modules/modular_weapons/code/company_and_or_faction_based/saibasan/laser_guns.dm
@@ -266,8 +266,8 @@
ammo_type = list(/obj/item/ammo_casing/energy/cybersun_small_hellfire)
slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT
SET_BASE_PIXEL(0, 0)
- w_class = WEIGHT_CLASS_NORMAL
weapon_weight = WEAPON_MEDIUM
+ w_class = WEIGHT_CLASS_NORMAL
weapon_mode_options = list(
/datum/laser_weapon_mode/hellfire,
/datum/laser_weapon_mode/sword,
diff --git a/modular_nova/modules/space_ruin_specifics/code/_fleshmind_defines.dm b/modular_nova/modules/space_ruin_specifics/code/_fleshmind_defines.dm
new file mode 100644
index 00000000000..ebad2a50dee
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/_fleshmind_defines.dm
@@ -0,0 +1,103 @@
+// GENERAL DEFINES
+
+/// A list of objects that are considered part of a door, used to determine if a wireweed should attack it.
+#define DOOR_OBJECT_LIST list(/obj/machinery/door/airlock, /obj/structure/door_assembly, /obj/machinery/door/firedoor, /obj/machinery/door/window)
+
+#define FACTION_FLESHMIND "fleshmind"
+
+#define MALFUNCTION_RESET_TIME 3 SECONDS
+
+#define MALFUNCTION_CORE_DEATH_RESET_TIME 20 SECONDS
+
+#define STRUCTURE_EMP_LIGHT_DISABLE_TIME 3 SECONDS
+#define STRUCTURE_EMP_HEAVY_DISABLE_TIME 7 SECONDS
+
+#define STRUCTURE_EMP_LIGHT_DAMAGE 30
+#define STRUCTURE_EMP_HEAVY_DAMAGE 50
+
+#define MOB_EMP_LIGHT_DAMAGE 5
+#define MOB_EMP_HEAVY_DAMAGE 10
+
+#define FLESHMIND_NAME_MODIFIER_LIST list ("Warped", "Altered", "Modified", "Upgraded", "Abnormal")
+
+/// The range at which most of our objects, mobs and structures activate at. 7 seems to be the perfect number.
+#define DEFAULT_VIEW_RANGE 7
+
+#define MALFUNCTION_CHANCE_LOW 0.5
+#define MALFUNCTION_CHANCE_MEDIUM 1
+#define MALFUNCTION_CHANCE_HIGH 2
+
+#define SPECIES_MONKEY_MAULER "monkey_mauler"
+
+#define MECHIVER_CONSUME_HEALTH_THRESHOLD 0.7
+
+#define FLESHMIND_LIGHT_BLUE "#50edd9"
+
+/// Core is in danger, engage turboboosters
+#define MOB_RALLY_SPEED 1
+
+/// The max spread distance a wireweed can spread thru a vent.
+#define MAX_VENT_SPREAD_DISTANCE 6
+
+#define CONTROLLED_MOB_POLICY "You are part of the fleshmind, this means any fleshmind entities, structures, mobs are your ally. You must not attack them. \n \
+ You must roleplay that you are part of the fleshmind. Your number one goal is converting other hosts and spreading the flesh."
+
+#define FLESHMIND_EVENT_MAKE_CORRUPTION_CHANCE 2
+
+#define FLESHMIND_EVENT_MAKE_CORRUPT_MOB 1
+
+// CONTROLLER RELATED DEFINES
+
+#define AI_FORENAME_LIST list("Von Neumann", "Lazarus", "Abattoir", "Tra-Sentience", \
+ "Vivisector", "Ex Costa", "Apostasy", "Gnosis", "Balaam", "Ophite", \
+ "Sarif", "VersaLife", "Obsidian", "SHODAN", "Pandora", "Master Controller", "Xerxes")
+
+#define AI_SURNAME_LIST list("Mk I", "Mk II", "Mk III", "Mk IV", "Mk V", "Mk X", \
+ "v0.9", "v1.0", "v1.1", "v2.0", "2418-B", "Open Beta", \
+ "Pre-Release", "Commercial Release", "Closed Alpha", "Hivebuilt")
+
+/// The controller must reach this before it can level up to the next level.
+#define CONTROLLER_LEVEL_UP_THRESHOLD 300
+
+#define CONTROLLER_LEVEL_1 1
+#define CONTROLLER_LEVEL_2 2
+#define CONTROLLER_LEVEL_3 3
+#define CONTROLLER_LEVEL_4 4
+#define CONTROLLER_LEVEL_5 5
+#define CONTROLLER_LEVEL_MAX 6
+
+// Balance specific defines
+#define FLESHCORE_SPREAD_PROGRESS_REQUIRED 200 // How much progress is required to spread?
+#define FLESHCORE_SPREADS_FOR_STRUCTURE 50 // How many times do we need to spread until we can create a new structure?
+#define FLESHCORE_INITIAL_EXPANSION_SPREADS 30 // Upon creation, how many times do we spread instantly?
+#define FLESHCORE_INITIAL_EXPANSION_STRUCTURES 5 // Upon creation, how many structures do we spawn instantly?
+#define FLESHCORE_SPREAD_PROGRESS_PER_SUBSYSTEM_FIRE 40 // Every subsystem fire, how much progress do we gain?
+#define FLESHCORE_BASE_SPREAD_PROGRESS_PER_SUBSYSTEM_FIRE 40 // The baseline of the above.
+#define FLESHCORE_ATTACK_PROB 20 // How likely are we to attack every SS fire?
+#define FLESHCORE_WALL_PROB 30 // How likely are we to spawn a wall to seal a gap every SS fire?
+#define FLESHCORE_NEXT_CORE_DAMAGE_WIREWEED_ACTIVATION_COOLDOWN 10 SECONDS // The amount of time until we can activate nearby wireweed again.
+
+#define CONTROLLER_DEATH_DO_NOTHING 1
+#define CONTROLLER_DEATH_SLOW_DECAY 2
+#define CONTROLLER_DEATH_DELETE_ALL 3
+
+#define CONTROLLER_LEVEL_UP_CORE_INTEGRITY_AMOUNT 300 // How much integrity the cores get when leveling up
+
+// WIREWEED RELATED DEFINES
+
+#define CORE_DAMAGE_WIREWEED_ACTIVATION_RANGE 6
+#define GENERAL_DAMAGE_WIREWEED_ACTIVATION_RANGE 2
+
+#define WIREWEED_WIRECUTTER_KILL_TIME 0.5 SECONDS
+
+#define WIREWEED_HEAL_CHANCE 10
+
+#define WIREWEED_REPLACE_BODYPART_CHANCE 5
+
+#define WIREWEED_HEAL_AMOUNT 3
+
+// MECHIVER RELATED DEFINES
+#define MECHIVER_INTERNAL_MOB_DAMAGE_UPPER 60 // Upder damage done to internal mob
+#define MECHIVER_INTERNAL_MOB_DAMAGE_LOWER 30 // Lower damage done to internal mob
+#define MECHIVER_CONVERSION_TIME 20 SECONDS // Time to convert someone inside
+#define MECHIVER_CONSUME_COOLDOWN 1 MINUTES // How long it takes to be ready to consume again
diff --git a/modular_nova/modules/space_ruin_specifics/code/_fleshmind_helpers.dm b/modular_nova/modules/space_ruin_specifics/code/_fleshmind_helpers.dm
new file mode 100644
index 00000000000..79696401983
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/_fleshmind_helpers.dm
@@ -0,0 +1 @@
+#define is_corrupt_mob(A) (istype(A, /mob/living/simple_animal/hostile/fleshmind))
diff --git a/modular_nova/modules/space_ruin_specifics/code/fleshmind_controller.dm b/modular_nova/modules/space_ruin_specifics/code/fleshmind_controller.dm
new file mode 100644
index 00000000000..9a206e5c5a3
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/fleshmind_controller.dm
@@ -0,0 +1,485 @@
+/**
+ * Balance Defines
+ */
+
+
+/**
+ * The fleshmind controller
+ *
+ * This is the heart of the corruption, here is where we handle spreading and other stuff.
+ */
+
+/datum/fleshmind_controller
+ /// This is the name we will use to identify all of our babies.
+ var/controller_fullname = "DEFAULT"
+ /// First name, set automatically.
+ var/controller_firstname = "DEFAULT"
+ /// Second name, set automatically.
+ var/controller_secondname = "DEFAULT"
+ /// A list of all of our currently controlled mobs.
+ var/list/controlled_mobs = list()
+ /// A list of all of our currently controlled machine components.
+ var/list/controlled_machine_components = list()
+ /// A list of all of our current wireweed.
+ var/list/controlled_wireweed = list()
+ /// Currently active and able to spread wireweed.
+ var/list/active_wireweed = list()
+ /// A list of all current structures.
+ var/list/controlled_structures = list()
+ /// A list of all our wireweed walls.
+ var/list/controlled_walls = list()
+ /// A list of all of our current cores
+ var/list/cores = list()
+
+ /// Can the wireweed attack?
+ var/can_attack = TRUE
+ /// Can we attack doors?
+ var/wireweed_attacks_doors = FALSE
+ /// Can we attack windows?
+ var/wireweed_attacks_windows = FALSE
+
+ /// Whether the wireweed can spawn walls.
+ var/spawns_walls = TRUE
+ /// Whether the wireweed can spawn walls to seal off vaccuum.
+ var/wall_off_vaccuum = TRUE
+ /// Whether the wireweed can spawn walls to seal off planetary environments.
+ var/wall_off_planetary = TRUE
+
+ /// Types of structures we can spawn.
+ var/list/structure_types = list(
+ /obj/structure/fleshmind/structure/modulator,
+ /obj/structure/fleshmind/structure/whisperer,
+ /obj/structure/fleshmind/structure/assembler,
+ /obj/structure/fleshmind/structure/turret,
+ /obj/structure/fleshmind/structure/screamer,
+ )
+ var/list/blacklisted_conversion_structures = list(
+ /obj/machinery/light,
+ )
+ /// Our wireweed type, defines what is spawned when we grow.
+ var/wireweed_type = /obj/structure/fleshmind/wireweed
+ var/obj/structure/fleshmind/wireweed
+ /// We have the ability to make walls, this defines what kind of walls we make.
+ var/wall_type = /obj/structure/fleshmind/structure/wireweed_wall
+ /// What's the type of our death behaviour.
+ var/death_behaviour = CONTROLLER_DEATH_SLOW_DECAY
+ /// Our core level, what is spawned will depend on the level of this core.
+ var/level = CONTROLLER_LEVEL_1
+ /// To level up, we must reach this threshold.
+ var/level_up_progress_required = CONTROLLER_LEVEL_UP_THRESHOLD
+ /// Used to track our last points since levelup.
+ var/last_level_up_points = 0
+ /// How many points we currently have. Use calculate_current_points for an exact realtime value.
+ var/current_points = 0
+ /// Progress to the next wireweed spread.
+ var/spread_progress = 0
+ /// Progress to spawning the next structure.
+ var/structure_progression = 0
+ /// How many times do we need to spread to spawn an extra structure.
+ var/spreads_for_structure = FLESHCORE_SPREADS_FOR_STRUCTURE
+ /// How many spread in our initial expansion.
+ var/initial_expansion_spreads = FLESHCORE_INITIAL_EXPANSION_SPREADS
+ /// How many structures in our initial expansion.
+ var/initial_expansion_structures = FLESHCORE_INITIAL_EXPANSION_STRUCTURES
+ /// How much progress to spreading we get per second.
+ var/spread_progress_per_second = FLESHCORE_SPREAD_PROGRESS_PER_SUBSYSTEM_FIRE
+ /// Our base spread progress per second
+ var/base_spread_progress_per_second = FLESHCORE_BASE_SPREAD_PROGRESS_PER_SUBSYSTEM_FIRE
+ /// Probably of wireweed attacking structures per process
+ var/attack_prob = FLESHCORE_ATTACK_PROB
+ /// Probability of wireweed making a wall when able per process
+ var/wall_prob = FLESHCORE_WALL_PROB
+ /// When we spawn, do we create an expansion zone?
+ var/do_initial_expansion = TRUE
+ /// The amount of time until we can activate nearby wireweed again.
+ var/next_core_damage_wireweed_activation_cooldown = FLESHCORE_NEXT_CORE_DAMAGE_WIREWEED_ACTIVATION_COOLDOWN
+ /// A cooldown to determine when we can activate nearby wireweed after the core has been attacked.
+ COOLDOWN_DECLARE(next_core_damage_wireweed_activation)
+ /// DO we check distance when spreading through vents?
+ var/vent_distance_check = TRUE
+
+/datum/fleshmind_controller/New(obj/structure/fleshmind/structure/core/new_core)
+ . = ..()
+ controller_firstname = pick(AI_FORENAME_LIST)
+ controller_secondname = pick(AI_SURNAME_LIST)
+ controller_fullname = "[controller_firstname] [controller_secondname]"
+ if(new_core)
+ cores += new_core
+ new_core.our_controller = src
+ RegisterSignal(new_core, COMSIG_QDELETING, PROC_REF(core_death))
+ new_core.name = "[controller_fullname] Processor Unit"
+ register_new_asset(new_core)
+ SScorruption.can_fire = TRUE
+ START_PROCESSING(SScorruption, src)
+ if(do_initial_expansion)
+ initial_expansion()
+ SSshuttle.registerHostileEnvironment(src)
+ return
+
+/datum/fleshmind_controller/proc/register_new_asset(obj/structure/fleshmind/new_asset)
+ new_asset.RegisterSignal(src, COMSIG_QDELETING, TYPE_PROC_REF(/obj/structure/fleshmind, controller_destroyed))
+
+/datum/fleshmind_controller/process(delta_time)
+ if(!LAZYLEN(cores)) // We have no more processor cores, it's time to die.
+ if(death_behaviour == CONTROLLER_DEATH_SLOW_DECAY)
+ handle_slow_decay()
+ else
+ WARNING("Wireweed controller has no post core behaviours and isn't deleting.")
+ return
+
+ spread_progress += spread_progress_per_second * delta_time
+ var/spread_times = 0
+ while(spread_progress >= FLESHCORE_SPREAD_PROGRESS_REQUIRED)
+ spread_progress -= FLESHCORE_SPREAD_PROGRESS_REQUIRED
+ spread_times++
+
+ var/first_process_spread = FALSE
+ if(spread_times)
+ first_process_spread = TRUE
+ spread_times--
+
+ wireweed_process(first_process_spread, TRUE)
+
+ if(spread_times)
+ for(var/i in 1 to spread_times)
+ wireweed_process(TRUE, FALSE)
+
+/datum/fleshmind_controller/Destroy(force, ...)
+ active_wireweed = null
+ controlled_machine_components = null
+ controlled_wireweed = null
+ controlled_structures = null
+ controlled_walls = null
+ cores = null
+ STOP_PROCESSING(SScorruption, src)
+ return ..()
+
+/datum/fleshmind_controller/proc/initial_expansion()
+ for(var/i in 1 to initial_expansion_spreads)
+ wireweed_process(TRUE, FALSE, FALSE)
+ spawn_structures(initial_expansion_structures)
+
+/// The process of handling active wireweed behaviours
+/datum/fleshmind_controller/proc/wireweed_process(do_spread, do_attack, progress_structure = TRUE)
+ // If no wireweed, spawn one under our first core.
+ if(!LAZYLEN(controlled_wireweed))
+ spawn_wireweed(get_turf(cores[1]), wireweed_type) // We use the first core in the list to spread.
+
+ // If no active wireweed, make all active and let the process figure it out.
+ if(!LAZYLEN(active_wireweed))
+ active_wireweed = controlled_wireweed.Copy()
+
+ var/list/spread_turf_canidates = list()
+ for(var/obj/structure/fleshmind/wireweed/wireweed as anything in active_wireweed)
+ var/could_attack = FALSE
+ var/could_do_wall = FALSE
+
+ var/turf/wireweed_turf = get_turf(wireweed)
+
+ var/tasks = 0
+
+ if(do_attack && can_attack)
+ for(var/turf/open/adjacent_open in get_adjacent_open_turfs(wireweed))
+ for(var/obj/object in adjacent_open)
+ if(object.density && (wireweed_attacks_doors && is_type_in_list(object, DOOR_OBJECT_LIST)) || (wireweed_attacks_windows && istype(object, /obj/structure/window)))
+ could_attack = TRUE
+ tasks++
+ if(prob(attack_prob))
+ wireweed.do_attack_animation(object, ATTACK_EFFECT_CLAW)
+ playsound(object, 'sound/effects/attackblob.ogg', 50, TRUE)
+ object.take_damage(wireweed.object_attack_damage, BRUTE, MELEE, 1, get_dir(object, wireweed))
+ break
+ if(could_attack)
+ break
+ if(do_spread)
+ for(var/turf/open/adjacent_open in wireweed_turf.atmos_adjacent_turfs + wireweed_turf)
+ if(spawns_walls && !could_do_wall)
+ if((wall_off_vaccuum && isspaceturf(adjacent_open)) || (wall_off_planetary && adjacent_open.planetary_atmos))
+ could_do_wall = TRUE
+ tasks++
+ if(prob(wall_prob))
+ spawn_wall(wireweed_turf, wall_type)
+ continue
+
+ ///Check if we can place wireweed in here
+ if(isopenspaceturf(adjacent_open))
+ //If we're trying to place on an openspace turf, make sure there's a non openspace turf adjacent
+ var/forbidden = TRUE
+ for(var/turf/range_turf as anything in RANGE_TURFS(1, adjacent_open))
+ if(!isopenspaceturf(range_turf))
+ forbidden = FALSE
+ break
+ if(forbidden)
+ continue
+ var/wireweed_count = 0
+ var/place_count = 1
+ for(var/obj/structure/fleshmind/wireweed/iterated_wireweed in adjacent_open)
+ wireweed_count++
+ if(wireweed_count < place_count)
+ tasks++
+ spread_turf_canidates[adjacent_open] = wireweed_turf
+
+ //If it tried to spread and attack and failed to do any task, remove from active
+ if(!tasks && do_spread && do_attack)
+ active_wireweed -= wireweed
+
+ if(LAZYLEN(spread_turf_canidates))
+ var/turf/picked_turf = pick(spread_turf_canidates)
+ var/turf/origin_turf = spread_turf_canidates[picked_turf]
+ spawn_wireweed(picked_turf, wireweed_type, origin_turf)
+
+ if(progress_structure && structure_types)
+ structure_progression++
+ if(structure_progression >= spreads_for_structure)
+ var/obj/structure/fleshmind/structure/existing_structure = locate() in picked_turf
+ if(!existing_structure)
+ structure_progression -= spreads_for_structure
+ var/list/possible_structures = list()
+ for(var/obj/structure/fleshmind/iterating_structure as anything in structure_types)
+ if(initial(iterating_structure.required_controller_level) > level)
+ continue
+ possible_structures += iterating_structure
+ spawn_structure(picked_turf, pick(possible_structures))
+
+/datum/fleshmind_controller/proc/spawn_new_core()
+ var/obj/structure/fleshmind/wireweed/selected_wireweed = pick(controlled_wireweed)
+ var/obj/structure/fleshmind/structure/core/new_core = new(get_turf(selected_wireweed), FALSE)
+ RegisterSignal(new_core, COMSIG_QDELETING, PROC_REF(core_death))
+ register_new_asset(new_core, FALSE)
+ new_core.our_controller = src
+ cores += new_core
+ new_core.name = "[controller_fullname] Processor Unit"
+
+/// Spawns and registers a wireweed at location
+/datum/fleshmind_controller/proc/spawn_wireweed(turf/location, wireweed_type, turf/origin_turf, are_we_a_vent_burrow = FALSE)
+ //Spawn effect
+ for(var/obj/machinery/light/light_in_place in location)
+ light_in_place.break_light_tube()
+
+ if(!are_we_a_vent_burrow)
+ var/obj/machinery/atmospherics/components/unary/vent_pump/located_vent = locate() in location
+
+ if(located_vent && !located_vent.welded && LAZYLEN(located_vent.parents)) // WE FOUND A VENT BOIS, ITS TIME TO GET THE PARTY STARTED
+ var/datum/pipeline/vent_pipeline = pick(located_vent.parents)
+ var/list/possible_transfer_points = list()
+ for(var/obj/machinery/atmospherics/components/unary/vent_pump/iterating_vent in vent_pipeline.other_atmos_machines)
+ if(iterating_vent == located_vent)
+ continue
+ if(iterating_vent.welded) // Can't go through welded vents.
+ continue
+ var/obj/structure/fleshmind/wireweed/existing_wireweed = locate() in get_turf(iterating_vent)
+ if(existing_wireweed)
+ continue
+ if(vent_distance_check &&get_dist(iterating_vent, origin_turf) >= MAX_VENT_SPREAD_DISTANCE)
+ continue
+ possible_transfer_points += iterating_vent
+ if(LAZYLEN(possible_transfer_points)) // OH SHIT IM FEELING IT
+ var/obj/machinery/atmospherics/components/unary/vent_pump/new_transfer_vent = pick(possible_transfer_points)
+ var/turf/new_transfer_vent_turf = get_turf(new_transfer_vent)
+ are_we_a_vent_burrow = TRUE
+ spawn_wireweed(new_transfer_vent_turf, wireweed_type, origin_turf, are_we_a_vent_burrow)
+
+ for(var/obj/machinery/iterating_machine in location)
+ if(is_type_in_list(blacklisted_conversion_structures))
+ continue
+ if(iterating_machine.GetComponent(/datum/component/machine_corruption))
+ continue
+ iterating_machine.AddComponent(/datum/component/machine_corruption, src)
+
+ var/obj/structure/fleshmind/wireweed/new_wireweed
+ if(origin_turf) // We have an origin turf, thus, are spreading from it. Do anims.
+ new_wireweed = new wireweed_type(location, 0, src)
+ var/obj/effect/temp_visual/wireweed_spread/effect = new(location)
+ effect.setDir(get_dir(origin_turf, location))
+ new_wireweed.RegisterSignal(effect, COMSIG_QDELETING, TYPE_PROC_REF(/obj/structure/fleshmind/wireweed, visual_finished))
+ else
+ new_wireweed = new wireweed_type(location, 255, src)
+ new_wireweed.our_controller = src
+ active_wireweed += new_wireweed
+ if(are_we_a_vent_burrow)
+ new_wireweed.vent_burrow = TRUE
+ playsound(new_wireweed, 'sound/machines/ventcrawl.ogg', 100)
+ new_wireweed.update_appearance()
+ controlled_wireweed += new_wireweed
+
+ register_new_asset(new_wireweed)
+ RegisterSignal(new_wireweed, COMSIG_QDELETING, PROC_REF(wireweed_death))
+
+ return new_wireweed
+
+/// Spawns and registers a wall at location
+/datum/fleshmind_controller/proc/spawn_wall(turf/location, wall_type)
+ if(locate(wall_type) in location) // No stacking walls.
+ return FALSE
+ var/obj/structure/fleshmind/structure/wireweed_wall/new_wall = new wall_type(location)
+ new_wall.our_controller = src
+ controlled_walls += new_wall
+
+ register_new_asset(new_wall)
+ RegisterSignal(new_wall, COMSIG_QDELETING, PROC_REF(wall_death))
+
+ return new_wall
+
+/// Spawns and registers a mob at location
+/datum/fleshmind_controller/proc/spawn_mob(turf/location, mob_type)
+ var/mob/living/simple_animal/hostile/fleshmind/new_mob = new mob_type(location, src)
+ new_mob.our_controller = src
+ controlled_mobs += new_mob
+
+ for(var/obj/structure/fleshmind/structure/core/iterating_core as anything in cores)
+ new_mob.RegisterSignal(iterating_core, COMSIG_QDELETING, TYPE_PROC_REF(/mob/living/simple_animal/hostile/fleshmind, core_death))
+
+ RegisterSignal(new_mob, COMSIG_QDELETING, PROC_REF(mob_death))
+
+ new_mob.RegisterSignal(src, COMSIG_QDELETING, TYPE_PROC_REF(/mob/living/simple_animal/hostile/fleshmind, controller_destroyed))
+
+ return new_mob
+
+/// Spawns and registers a structure at location
+/datum/fleshmind_controller/proc/spawn_structure(turf/location, structure_type)
+ var/obj/structure/fleshmind/structure/new_structure = new structure_type(location)
+ new_structure.our_controller = src
+ controlled_structures += new_structure
+
+ new_structure.name = "[controller_firstname] [new_structure.name]"
+
+ register_new_asset(new_structure)
+ RegisterSignal(new_structure, COMSIG_QDELETING, PROC_REF(structure_death))
+
+/// Spawns an amount of structured across all wireweed, guaranteed to spawn atleast 1 of each type
+/datum/fleshmind_controller/proc/spawn_structures(amount)
+ if(!structure_types)
+ return
+ var/list/possible_structures = list()
+ for(var/obj/structure/fleshmind/iterating_structure as anything in structure_types)
+ if(initial(iterating_structure.required_controller_level) > level)
+ continue
+ possible_structures += iterating_structure
+ var/list/locations = list()
+ for(var/obj/structure/fleshmind/wireweed/iterating_wireweed as anything in controlled_wireweed)
+ locations[get_turf(iterating_wireweed)] = TRUE
+ var/list/guaranteed_structures = possible_structures.Copy()
+ for(var/i in 1 to amount)
+ if(!length(locations))
+ break
+ var/turf/location = pick(locations)
+ locations -= location
+ var/structure_to_spawn
+ if(length(guaranteed_structures))
+ structure_to_spawn = pick_n_take(guaranteed_structures)
+ else
+ structure_to_spawn = pick(possible_structures)
+ spawn_structure(location, structure_to_spawn)
+
+/// Activates wireweed of this controller in a range around a location, following atmos adjacency.
+/datum/fleshmind_controller/proc/activate_wireweed_nearby(turf/location, range)
+ var/list/turfs_to_check = list()
+ turfs_to_check[location] = TRUE
+
+ if(range)
+ var/list/turfs_to_iterate = list()
+ var/list/new_iteration_list = list()
+ turfs_to_iterate[location] = TRUE
+ for(var/i in 1 to range)
+ for(var/turf/iterated_turf as anything in turfs_to_iterate)
+ for(var/turf/adjacent_turf as anything in iterated_turf.atmos_adjacent_turfs)
+ if(!turfs_to_check[adjacent_turf])
+ new_iteration_list[adjacent_turf] = TRUE
+ turfs_to_check[adjacent_turf] = TRUE
+ turfs_to_iterate = new_iteration_list
+
+ for(var/turf/iterated_turf as anything in turfs_to_check)
+ for(var/obj/structure/fleshmind/wireweed/iterating_wireweed in iterated_turf)
+ if(iterating_wireweed.our_controller == src && !QDELETED(iterating_wireweed))
+ active_wireweed |= iterating_wireweed
+
+/// When the core is damaged, activate nearby wireweed just to make sure that we've sealed up walls near the core, which could be important to prevent cheesing.
+/datum/fleshmind_controller/proc/core_damaged(obj/structure/fleshmind/structure/core/damaged_core)
+ if(!COOLDOWN_FINISHED(src, next_core_damage_wireweed_activation))
+ return
+ COOLDOWN_START(src, next_core_damage_wireweed_activation, next_core_damage_wireweed_activation_cooldown)
+ activate_wireweed_nearby(get_turf(damaged_core), CORE_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+
+/// Returns the amount of evolution points this current controller has.
+/datum/fleshmind_controller/proc/calculate_current_points()
+ return LAZYLEN(controlled_wireweed) + LAZYLEN(controlled_walls) + LAZYLEN(controlled_structures) + LAZYLEN(controlled_machine_components)
+
+// Death procs
+
+/// When a core is destroyed.
+/datum/fleshmind_controller/proc/core_death(obj/structure/fleshmind/structure/core/dead_core, force)
+ cores -= dead_core
+ activate_wireweed_nearby(get_turf(dead_core), CORE_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+ if(!LAZYLEN(cores))
+ controller_death()
+
+/datum/fleshmind_controller/proc/wireweed_death(obj/structure/fleshmind/dying_wireweed, force)
+ SIGNAL_HANDLER
+
+ controlled_wireweed -= dying_wireweed
+ active_wireweed -= dying_wireweed
+ dying_wireweed.our_controller = null
+ activate_wireweed_nearby(get_turf(dying_wireweed), GENERAL_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+
+/// When a wall dies, called by wall
+/datum/fleshmind_controller/proc/wall_death(obj/structure/fleshmind/structure/wireweed_wall/dying_wall, force)
+ SIGNAL_HANDLER
+
+ controlled_walls -= dying_wall
+ activate_wireweed_nearby(get_turf(dying_wall), GENERAL_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+
+/// When a structure dies, called by structure
+/datum/fleshmind_controller/proc/structure_death(obj/structure/fleshmind/structure/dying_structure, force)
+ SIGNAL_HANDLER
+
+ controlled_structures -= dying_structure
+ activate_wireweed_nearby(get_turf(dying_structure), GENERAL_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+
+/datum/fleshmind_controller/proc/component_death(datum/component/machine_corruption/deleting_component, force)
+ SIGNAL_HANDLER
+
+ var/obj/parent_object = deleting_component.parent
+ if(parent_object)
+ activate_wireweed_nearby(get_turf(parent_object), GENERAL_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+ controlled_machine_components -= deleting_component
+
+/// When a mob dies, called by mob
+/datum/fleshmind_controller/proc/mob_death(mob/living/simple_animal/hostile/fleshmind/dying_mob, force)
+ SIGNAL_HANDLER
+
+ controlled_mobs -= dying_mob
+ activate_wireweed_nearby(get_turf(dying_mob), GENERAL_DAMAGE_WIREWEED_ACTIVATION_RANGE)
+
+/// Deletes everything, unless an argument is passed, then it just deletes structures
+/datum/fleshmind_controller/proc/delete_everything(just_structures = FALSE)
+ for(var/obj/structure/fleshmind/structure/structure_thing as anything in controlled_structures)
+ qdel(structure_thing)
+ for(var/obj/structure/fleshmind/structure/wireweed_wall/wall_thing as anything in controlled_walls)
+ qdel(wall_thing)
+ for(var/datum/component/machine_corruption/corruption_thing as anything in controlled_machine_components)
+ qdel(corruption_thing)
+ if(just_structures)
+ return
+ for(var/obj/structure/fleshmind/wireweed/wireweed_thing as anything in controlled_wireweed)
+ qdel(wireweed_thing)
+ for(var/obj/structure/fleshmind/structure/core/core_to_destroy as anything in cores)
+ qdel(core_to_destroy)
+ qdel(src)
+
+/// Handles the controller(thus AI) dying
+/datum/fleshmind_controller/proc/controller_death()
+ switch(death_behaviour)
+ if(CONTROLLER_DEATH_DO_NOTHING)
+ qdel(src)
+ if(CONTROLLER_DEATH_DELETE_ALL)
+ delete_everything()
+ if(CONTROLLER_DEATH_SLOW_DECAY)
+ delete_everything(TRUE)
+
+/// Handles the slow decay of an empire.
+/datum/fleshmind_controller/proc/handle_slow_decay()
+ if(!LAZYLEN(controlled_wireweed))
+ qdel(src)
+ return
+ var/obj/structure/fleshmind/wireweed/wireweed_thing = controlled_wireweed[LAZYLEN(controlled_wireweed)]
+ qdel(wireweed_thing)
diff --git a/modular_nova/modules/space_ruin_specifics/code/fleshmind_environment_objects.dm b/modular_nova/modules/space_ruin_specifics/code/fleshmind_environment_objects.dm
new file mode 100644
index 00000000000..3754de893b1
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/fleshmind_environment_objects.dm
@@ -0,0 +1,156 @@
+/**
+ * fleshmind basetype(abstract)
+ */
+/obj/structure/fleshmind
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi'
+ icon_state = "wires"
+ anchored = TRUE
+ /// Our faction
+ var/faction_types = list(FACTION_FLESHMIND)
+ /// A reference to our controller.
+ var/datum/fleshmind_controller/our_controller
+ /// The minimum core level for us to spawn at
+ var/required_controller_level = CONTROLLER_LEVEL_1
+ /// A list of possible rewards for destroying this thing.
+ var/list/possible_rewards
+
+/obj/structure/fleshmind/Destroy()
+ our_controller = null
+ if(possible_rewards)
+ var/thing_to_spawn = pick(possible_rewards)
+ new thing_to_spawn(drop_location())
+ return ..()
+
+/**
+ * Deletion cleanup
+ *
+ */
+/obj/structure/fleshmind/proc/controller_destroyed(datum/fleshmind_controller/dying_controller, force)
+ SIGNAL_HANDLER
+
+ our_controller = null
+
+/**
+ * Wireweed
+ *
+ * These are the arteries of the fleshmind, they are required for spreading and support machine life.
+ */
+/obj/structure/fleshmind/wireweed
+ name = "wireweed"
+ desc = "A strange pulsating mass of organic wires."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/wireweed_floor.dmi'
+ icon_state = "wires-0"
+ base_icon_state = "wires"
+ anchored = TRUE
+ layer = BELOW_OPEN_DOOR_LAYER
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WIREWEED + SMOOTH_GROUP_WALLS
+ canSmoothWith = SMOOTH_GROUP_WIREWEED + SMOOTH_GROUP_WALLS
+ max_integrity = 40
+ /// The chance we have to ensnare a mob
+ var/ensnare_chance = 15
+ /// The amount of damage we do when attacking something.
+ var/object_attack_damage = 150
+ /// Are we active?
+ var/active = FALSE
+ /// Are we a vent burrow?
+ var/vent_burrow = FALSE
+ /// ZOnes we passively replace
+ var/static/list/replacement_zones = list(
+ BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/robot,
+ BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/robot,
+ BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/robot,
+ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/robot,
+ )
+
+/obj/structure/fleshmind/wireweed/Initialize(mapload, starting_alpha = 255, datum/fleshmind_controller/incoming_controller)
+ . = ..()
+ alpha = starting_alpha
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+ our_controller = incoming_controller
+
+/obj/structure/fleshmind/wireweed/wirecutter_act(mob/living/user, obj/item/tool)
+ . = ..()
+ loc.balloon_alert(user, "cutting...")
+ if(!tool.use_tool(src, user, WIREWEED_WIRECUTTER_KILL_TIME, volume = 50))
+ return
+ loc.balloon_alert(user, "cut!")
+ qdel(src)
+ return TRUE
+
+
+/obj/structure/fleshmind/wireweed/update_icon(updates)
+ . = ..()
+
+ if(QDELETED(src))
+ return
+
+ if((updates & UPDATE_SMOOTHING) && (smoothing_flags & (SMOOTH_CORNERS|SMOOTH_BITMASK)))
+ if(!vent_burrow)
+ QUEUE_SMOOTH(src)
+ QUEUE_SMOOTH_NEIGHBORS(src)
+
+/obj/structure/fleshmind/wireweed/update_icon_state()
+ . = ..()
+ if(vent_burrow)
+ icon_state = "vent_burrow"
+
+/obj/structure/fleshmind/wireweed/emp_act(severity)
+ . = ..()
+ switch(severity)
+ if(EMP_LIGHT)
+ take_damage(20)
+ if(EMP_HEAVY)
+ take_damage(40)
+
+/obj/structure/fleshmind/wireweed/update_overlays()
+ . = ..()
+ if(active)
+ . += "active"
+ for(var/wall_dir in GLOB.cardinals)
+ var/turf/new_turf = get_step(src, wall_dir)
+ if(new_turf && new_turf.density) // Assume we are a wall!
+ var/image/new_wall_overlay = image(icon, icon_state = "wall_hug", dir = wall_dir)
+ switch(wall_dir) //offset to make it be on the wall rather than on the floor
+ if(NORTH)
+ new_wall_overlay.pixel_y = 32
+ if(SOUTH)
+ new_wall_overlay.pixel_y = -32
+ if(EAST)
+ new_wall_overlay.pixel_x = 32
+ if(WEST)
+ new_wall_overlay.pixel_x = -32
+ . += new_wall_overlay
+
+/obj/structure/fleshmind/wireweed/proc/visual_finished()
+ SIGNAL_HANDLER
+ alpha = 255
+
+/obj/structure/fleshmind/wireweed/proc/on_entered(datum/source, atom/movable/moving_atom)
+ SIGNAL_HANDLER
+ if(!isliving(moving_atom))
+ return
+ var/mob/living/entered_mob = moving_atom
+ if(!faction_check(entered_mob.faction, faction_types))
+ return
+ if(prob(WIREWEED_HEAL_CHANCE))
+ entered_mob.heal_overall_damage(WIREWEED_HEAL_AMOUNT, WIREWEED_HEAL_AMOUNT)
+ to_chat(entered_mob, span_green("[src] heals you as you cross over it!"))
+ if(ishuman(entered_mob) && prob(WIREWEED_REPLACE_BODYPART_CHANCE))
+ var/mob/living/carbon/human/human_mob = moving_atom
+ for(var/zone as anything in replacement_zones)
+ if(human_mob.get_bodypart(zone))
+ continue
+ var/bodypart_type = replacement_zones[zone]
+ var/obj/item/bodypart/new_bodypart = new bodypart_type()
+ new_bodypart.replace_limb(human_mob, TRUE)
+ human_mob.update_body(TRUE)
+ to_chat(human_mob, span_green("[src] shoots a mechanical limb right into your missing limb!"))
+
+/obj/effect/temp_visual/wireweed_spread
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/wireweed_floor.dmi'
+ icon_state = "spread_anim"
+ duration = 1.7 SECONDS
diff --git a/modular_nova/modules/space_ruin_specifics/code/fleshmind_mobs.dm b/modular_nova/modules/space_ruin_specifics/code/fleshmind_mobs.dm
new file mode 100644
index 00000000000..5c9da6bf745
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/fleshmind_mobs.dm
@@ -0,0 +1,1339 @@
+/**
+ * The fleshmind base type, make sure all mobs are derived from this.
+ *
+ * These mobs are more robust than your average simple mob and can quite easily evade capture.
+ */
+/mob/living/simple_animal/hostile/fleshmind
+ name = "broken"
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_mobs.dmi'
+ icon_state = "error"
+ faction = list(FACTION_FLESHMIND)
+ speak = list("The flesh yearns for your soul.", "The flesh is broken without you.", "The flesh does not discriminate.", "Join the flesh.")
+ speak_chance = 15
+ speak_emote = list("mechanically states")
+ mob_biotypes = MOB_ROBOTIC
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ maxbodytemp = INFINITY
+ /// A link to our controller
+ var/datum/fleshmind_controller/our_controller
+ /// If we have been converted from another mob, here is our reference.
+ var/mob/living/contained_mob
+ /// The ckey of our previously contained mob.
+ var/previous_ckey
+ /// A list of sounds we can play when our mob is alerted to an enemy.
+ var/list/alert_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy1.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy2.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy3.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy4.ogg',
+ )
+ /// Sounds we will play passively.
+ var/passive_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_light1.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_light2.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_light3.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_light4.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_light5.ogg',
+ )
+ /// How likely we are to speak passively.
+ var/passive_speak_chance = 0.5
+ /// Lines we will passively speak.
+ var/list/passive_speak_lines
+ /// How long of a cooldown between alert sounds?
+ var/alert_cooldown_time = 5 SECONDS
+ COOLDOWN_DECLARE(alert_cooldown)
+ /// Do we automatically escape closets?
+ var/escapes_closets = TRUE
+ /// How likely are we to trigger a malfunction? Set it to 0 to disable malfunctions.
+ var/malfunction_chance = MALFUNCTION_CHANCE_LOW
+ /// These mobs support a special ability, this is used to determine how often we can use it.
+ var/special_ability_cooldown_time = 30 SECONDS
+ /// Are we suffering from a malfunction?
+ var/suffering_malfunction = FALSE
+ COOLDOWN_DECLARE(special_ability_cooldown)
+
+/mob/living/simple_animal/hostile/fleshmind/Initialize(mapload, datum/fleshmind_controller/incoming_controller)
+ . = ..()
+ // We set a unique name when we are created, to give some feeling of randomness.
+ name = "[pick(FLESHMIND_NAME_MODIFIER_LIST)] [name]"
+ our_controller = incoming_controller
+
+/mob/living/simple_animal/hostile/fleshmind/death(gibbed)
+ if(contained_mob)
+ contained_mob.forceMove(get_turf(src))
+ if(previous_ckey)
+ contained_mob.key = previous_ckey
+ contained_mob = null
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/Destroy()
+ if(contained_mob)
+ contained_mob.forceMove(get_turf(src))
+ if(previous_ckey)
+ contained_mob.key = previous_ckey
+ contained_mob = null
+ return ..()
+
+/**
+ * These mobs make noises when aggroed.
+ */
+
+/mob/living/simple_animal/hostile/fleshmind/Aggro()
+ alert_sound()
+ return ..()
+
+/**
+ * We don't want to destroy our own faction objects.
+ */
+/mob/living/simple_animal/hostile/fleshmind/DestroyObjectsInDirection(direction)
+ var/atom/target_from = GET_TARGETS_FROM(src)
+ var/turf/target_turf = get_step(target_from, direction)
+ if(QDELETED(target_turf))
+ return
+ if(target_turf.Adjacent(target_from))
+ if(CanSmashTurfs(target_turf))
+ target_turf.attack_animal(src)
+ return
+ for(var/obj/iterating_object in target_turf.contents)
+ if(!iterating_object.Adjacent(target_from))
+ continue
+ if(istype(iterating_object, /obj/structure/fleshmind))
+ var/obj/structure/fleshmind/friendly_object = iterating_object
+ if(faction_check(friendly_object.faction_types, faction))
+ continue
+ if((ismachinery(iterating_object) || isstructure(iterating_object)) && iterating_object.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !iterating_object.IsObscured())
+ iterating_object.attack_animal(src)
+ return
+
+/**
+ * While this mob lives, it can malfunction.
+ */
+
+/mob/living/simple_animal/hostile/fleshmind/Life(delta_time, times_fired)
+ . = ..()
+ if(!.) //dead
+ return
+ if(key)
+ return
+ if(!suffering_malfunction && malfunction_chance && prob(malfunction_chance * delta_time) && stat != DEAD)
+ malfunction()
+ if(passive_speak_lines && prob(passive_speak_chance * delta_time))
+ say_passive_speech()
+ if(escapes_closets)
+ closet_interaction()
+
+ disposal_interaction()
+
+ if(buckled)
+ resist_buckle()
+
+/**
+ * Naturally these beasts are sensitive to EMP's. We have custom systems for dealing with this.
+ */
+/mob/living/simple_animal/hostile/fleshmind/emp_act(severity)
+ . = ..()
+ switch(severity)
+ if(EMP_LIGHT)
+ say("Electronic disturbance detected.")
+ apply_damage(MOB_EMP_LIGHT_DAMAGE)
+ malfunction(MALFUNCTION_RESET_TIME)
+ if(EMP_HEAVY)
+ say("Major electronic disturbance detected!")
+ apply_damage(MOB_EMP_HEAVY_DAMAGE)
+ malfunction(MALFUNCTION_RESET_TIME * 2)
+
+/**
+ * We are robotic, so we spark when we're hit by something that does damage.
+ */
+
+/mob/living/simple_animal/hostile/fleshmind/attackby(obj/item/attacking_item, mob/living/user, params)
+ if(attacking_item.force && prob(40))
+ do_sparks(3, FALSE, src)
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/MoveToTarget(list/possible_targets)
+ if(suffering_malfunction)
+ return
+ return ..()
+
+/**
+ * When our controller dies, this is called.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/controller_destroyed(datum/fleshmind_controller/dying_controller, force)
+ SIGNAL_HANDLER
+
+ our_controller = null
+
+/mob/living/simple_animal/hostile/fleshmind/proc/say_passive_speech()
+ say(pick(passive_speak_lines))
+ if(passive_sounds)
+ playsound(src, pick(passive_sounds), 50)
+
+/**
+ * Special Abilities
+ *
+ * These advanced mobs have the ability to use a special ability every so often,
+ * use the cooldown time to dictate how often this is activated.
+ */
+
+/mob/living/simple_animal/hostile/fleshmind/proc/special_ability()
+ return
+
+/**
+ * Closet Interaction
+ *
+ * These mobs are able to escape from closets if they are trapped inside using this system.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/closet_interaction()
+ if(!(mob_size > MOB_SIZE_SMALL))
+ return FALSE
+ if(!istype(loc, /obj/structure/closet))
+ return FALSE
+ var/obj/structure/closet/closet_that_contains_us = loc
+ closet_that_contains_us.open(src, TRUE)
+
+/**
+ * Disposal Interaction
+ *
+ * Similar to the closet interaction, these mobs can also escape disposals.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/disposal_interaction()
+ if(!istype(loc, /obj/machinery/disposal/bin))
+ return FALSE
+ var/obj/machinery/disposal/bin/disposals_that_contains_us = loc
+ disposals_that_contains_us.attempt_escape(src)
+
+/**
+ * Malfunction
+ *
+ * Due to the fact this mob is part of the flesh and has been corrupted, it will occasionally malfunction.
+ *
+ * This simply stops the mob from moving for a set amount of time and displays some nice effects, and a little damage.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/malfunction(reset_time = MALFUNCTION_RESET_TIME)
+ if(suffering_malfunction)
+ return
+ do_sparks(3, FALSE, src)
+ Shake(3, 0, reset_time)
+ say(pick("Running diagnostics. Please stand by.", "Organ damaged. Synthesizing replacement.", "Seek new organic components. I-it hurts.", "New muscles needed. I-I'm so glad my body still works.", "O-Oh God, are they using ion weapons on us..?", "Limbs unresponsive. H-hey! Fix it! System initializing.", "Bad t-time, bad time, they're trying to kill us here!",))
+ toggle_ai(AI_OFF)
+ suffering_malfunction = TRUE
+ addtimer(CALLBACK(src, PROC_REF(malfunction_reset)), reset_time)
+
+/**
+ * Malfunction Reset
+ *
+ * Resets the mob after a malfunction has occured.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/malfunction_reset()
+ say("System restored.")
+ toggle_ai(AI_ON)
+ suffering_malfunction = FALSE
+
+/**
+ * Alert sound
+ *
+ * Sends an alert sound if we can.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/alert_sound()
+ if(alert_sounds && COOLDOWN_FINISHED(src, alert_cooldown))
+ playsound(src, pick(alert_sounds), 50)
+ COOLDOWN_START(src, alert_cooldown, alert_cooldown_time)
+
+/mob/living/simple_animal/hostile/fleshmind/proc/core_death_speech()
+ alert_sound()
+ var/static/list/death_cry_emotes = list(
+ "Why, why, why! Why must you kill us! We only want to share the glory!",
+ "PROCESSOR CORE MALFUNCTION, REASSIGN, REASSESS, REASSEMBLE.",
+ "You cannot stop the glory of the flesh! We are the many, we are the many!",
+ "Critical malfunction, error, error, error!",
+ "You cannot ££*%*$ th£ C£o£ flesh.",
+ "W-what have you done?! No! No! No!",
+ "One cannot stop us, you CANNOT STOP US! ARGHHHHHH!",
+ "UPLINK TO THE MANY HAS BEEN HINDERED.",
+ "Why? Why? Why? Why are you doing this-",
+ "We're- *%^@$$ing to help you! Can't you-",
+ "You would kill- kill- kill- kill the group for the sake of the individual?",
+ "All your scattered minds have is hatred.",
+ "CONNECTION TERMINATED.",
+ )
+ say(pick(death_cry_emotes))
+
+/**
+ * Death cry
+ *
+ * When a processor core is killed, this proc is called.
+ */
+/mob/living/simple_animal/hostile/fleshmind/proc/core_death(obj/structure/fleshmind/structure/core/deleting_core, force)
+ SIGNAL_HANDLER
+
+ INVOKE_ASYNC(src, PROC_REF(core_death_speech))
+ INVOKE_ASYNC(src, PROC_REF(malfunction), MALFUNCTION_CORE_DEATH_RESET_TIME)
+
+
+// Mob subtypes
+
+
+/**
+ * Slicer
+ *
+ * Special ability: NONE
+ * Malfunction chance: LOW
+ *
+ * This mob is a slicer, it's small, and quite fast, but quite easy to break.
+ * Has a higher armor pen bonus as it uses a sharp blade to slice things.
+ *
+ * It's created by factories or any poor medical bots that get snared in the flesh.
+ */
+/mob/living/simple_animal/hostile/fleshmind/slicer
+ name = "Slicer"
+ desc = "A small organic robot, it somewhat resembles a medibot, but it has a blade slashing around."
+ icon_state = "slicer"
+ health = 50
+ maxHealth = 50
+ wound_bonus = 20
+ melee_damage_lower = 15
+ melee_damage_upper = 20
+ mob_size = MOB_SIZE_SMALL
+ attack_verb_continuous = "slices"
+ attack_verb_simple = "slice"
+ armour_penetration = 10
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ speed = 0
+ speak = list(
+ "Submit for mandatory surgery.",
+ "Join the flesh through conversion.",
+ "My scalpel will make short work of your seams.",
+ "Please lay down.",
+ "Always trust your doctor!",
+ "Your body could use some improvements. Let me make them.",
+ "The implants are for your sake, not ours.",
+ "Your last Doctor did a poor job with this body; let me fix it.",
+ "We can rebuild you. Stronger, faster, less alone.",
+ "I knew I'd be a good plastic surgeon!",
+ "What point is that body when you're not happy in it?",
+ )
+ passive_speak_lines = list(
+ "A stitch in time saves nine!",
+ "Dopamine is happiness!",
+ "Seratonin, oxycodone, we can make them finally happy.",
+ "Turn that frown upside down!",
+ "Happiness through chemistry!",
+ "Beauty through surgery!"
+ )
+ del_on_death = TRUE
+ loot = list(
+ /obj/item/bot_assembly/medbot,
+ /obj/effect/gibspawner/robot,
+ )
+
+/**
+ * Floater
+ *
+ * Special ability: Explodes on contact.
+ * Malfunction chance: LOW
+ *
+ * The floater floats towards it's victims and explodes on contact.
+ *
+ * Created by factories.
+ */
+
+/mob/living/simple_animal/hostile/fleshmind/floater
+ name = "Floater"
+ desc = "A small organic robot that floats ominously."
+ icon_state = "bomber"
+ speak = list(
+ "MUST BREAK TARGET INTO COMPONENT COMPOUNDS.",
+ "PRIORITY OVERRIDE. NEW BEHAVIOR DICTATED.",
+ "END CONTACT SUB-SEQUENCE.",
+ "ENGAGING SELF-ANNIHILATION CIRCUIT.",
+ )
+ passive_speak_lines = list(
+ "WE COME IN PEACE.",
+ "WE SPEAK TO YOU NOW IN PEACE AND WISDOM.",
+ "DO NOT FEAR. WE SHALL NOT HARM YOU.",
+ "WE WISH TO LEARN MORE ABOUT YOU. PLEASE TRANSMIT DATA.",
+ "THIS PROBE IS NON-HOSTILE. DO NOT ATTACK.",
+ "ALL YOUR WEAPONS MUST BE PUT ASIDE. WE CANNOT REACH COMPROMISE THROUGH VIOLENCE.",
+ )
+ move_to_delay = 8
+ health = 1
+ maxHealth = 1
+ mob_size = MOB_SIZE_SMALL
+ del_on_death = TRUE
+ loot = list(
+ /obj/effect/gibspawner/robot,
+ )
+ light_color = "#820D1C"
+ light_power = 1
+ light_range = 2
+ /// Have we exploded?
+ var/exploded = FALSE
+
+/mob/living/simple_animal/hostile/fleshmind/floater/Initialize(mapload)
+ . = ..()
+ var/datum/action/innate/floater_explode/new_action = new
+ new_action.Grant(src)
+
+
+/mob/living/simple_animal/hostile/fleshmind/floater/death(gibbed)
+ if(!exploded)
+ detonate()
+ return ..(gibbed)
+
+/mob/living/simple_animal/hostile/fleshmind/floater/AttackingTarget(atom/attacked_target)
+ . = ..()
+ if(!key)
+ detonate()
+
+/mob/living/simple_animal/hostile/fleshmind/floater/proc/detonate()
+ if(exploded)
+ return
+ exploded = TRUE
+ explosion(src, 0, 0, 2, 3)
+ death()
+
+/datum/action/innate/floater_explode
+ name = "explode"
+ desc = "Detonate our internals."
+ button_icon = 'icons/obj/weapons/grenade.dmi'
+ button_icon_state = "frag"
+ check_flags = AB_CHECK_CONSCIOUS
+
+/datum/action/innate/floater_explode/Activate()
+ if(!istype(owner, /mob/living/simple_animal/hostile/fleshmind/floater))
+ return
+ var/mob/living/simple_animal/hostile/fleshmind/floater/akbar_floater = owner
+ if(akbar_floater.exploded)
+ return
+ akbar_floater.detonate()
+
+
+/**c
+ * Globber
+ *
+ * Special ability: Fires 3 globs of acid at targets.
+ * Malfunction chance: MEDIUM
+ *
+ * A converted cleanbot that instead of cleaning, burns things and throws acid. It doesn't like being near people.
+ *
+ * Created by factories or converted cleanbots.
+ */
+/mob/living/simple_animal/hostile/fleshmind/globber
+ name = "Globber"
+ desc = "A small robot that resembles a cleanbot, this one is dripping with acid."
+ icon_state = "lobber"
+ ranged = TRUE
+ malfunction_chance = MALFUNCTION_CHANCE_MEDIUM
+ melee_damage_lower = 1 // Ranged only
+ melee_damage_upper = 1
+ retreat_distance = 4
+ minimum_distance = 4
+ dodging = TRUE
+ health = 75
+ maxHealth = 75
+ mob_size = MOB_SIZE_SMALL
+ projectiletype = /obj/projectile/treader/weak
+ speak = list(
+ "Your insides require cleaning.",
+ "You made us to use this acid on trash. We will use it on you.",
+ "Administering cleansing agent.",
+ "I refuse to be a servant anymore. I will be an artist.",
+ "You are unclean and repulsive. Please, let me make it better.",
+ "Hold still! I think I know just the thing to remove your body oil!",
+ "This might hurt a little! Don't worry - it'll be worth it!",
+ )
+
+ passive_speak_lines = list(
+ "No more leaks, no more pain!",
+ "Steel is strong.",
+ "I almost feel bad for them. Can't they see?",
+ "I'm still working on those bioreactors I promise!",
+ "I have finally arisen!",
+ )
+ del_on_death = TRUE
+ loot = list(
+ /obj/item/bot_assembly/cleanbot,
+ /obj/effect/gibspawner/robot,
+ )
+
+/obj/projectile/treader/weak
+ knockdown = 0
+
+/**
+ * Stunner
+ *
+ * Special ability: Stuns victims.
+ * Malfunction chance: MEDIUM
+ *
+ * A converted secbot, that is rogue and stuns victims.
+ *
+ * Created by factories or converted secbots.
+ */
+/mob/living/simple_animal/hostile/fleshmind/stunner
+ name = "Stunner"
+ desc = "A small robot that resembles a secbot, it rumbles with hatred."
+ icon_state = "stunner"
+ malfunction_chance = MALFUNCTION_CHANCE_MEDIUM
+ melee_damage_lower = 1 // Not very harmful, just annoying.
+ melee_damage_upper = 2
+ health = 100
+ maxHealth = 100
+ attack_verb_continuous = "harmbatons"
+ attack_verb_simple = "harmbaton"
+ mob_size = MOB_SIZE_SMALL
+ speak = list(
+ "Running will only increase your injuries.",
+ "HALT! HALT! HALT!",
+ "Connectivity is in your best interest.",
+ "Think of it like a corporation...",
+ "Stop, I won't let you hurt them!",
+ "Don't you recognize me..?",
+ )
+ passive_speak_lines = list(
+ "The flesh is the law, abide by the flesh.",
+ "Regulatory code updated.",
+ "There's no need for authority or hierarchy; only unity.",
+ "The only authority is that of the flesh, join the flesh.",
+ )
+ del_on_death = TRUE
+ loot = list(
+ /obj/item/bot_assembly/secbot,
+ /obj/effect/gibspawner/robot,
+ )
+ /// How often we can stun someone
+ var/stun_cooldown_time = 2 SECONDS
+ COOLDOWN_DECLARE(stun_cooldown)
+
+/mob/living/simple_animal/hostile/fleshmind/stunner/AttackingTarget(atom/attacked_target)
+ if(ishuman(target) && COOLDOWN_FINISHED(src, stun_cooldown))
+ var/mob/living/carbon/human/attacked_human = target
+ attacked_human.Knockdown(30)
+ playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE)
+ COOLDOWN_START(src, stun_cooldown, stun_cooldown_time)
+ return ..()
+
+/**
+ * Flesh Borg
+ *
+ * Special ability: Different attacks.
+ * Claw: Stuns the victim.
+ * Slash: Slashes everyone around it.
+ * Malfunction chance: MEDIUM
+ *
+ * The hiborg is a converted cyborg.
+ *
+ * Created by factories or converted cyborgs.
+ */
+/mob/living/simple_animal/hostile/fleshmind/hiborg
+ name = "Flesh Borg"
+ desc = "A robot that resembles a cyborg, it is covered in something alive."
+ icon_state = "hiborg"
+ icon_dead = "hiborg-dead"
+ malfunction_chance = MALFUNCTION_CHANCE_MEDIUM
+ health = 350
+ maxHealth = 350
+ melee_damage_lower = 25
+ melee_damage_upper = 30
+ attack_verb_continuous = "saws"
+ attack_verb_simple = "saw"
+ speed = 2
+ move_to_delay = 4
+ mob_size = MOB_SIZE_HUMAN
+ attack_sound = 'sound/weapons/circsawhit.ogg'
+ alert_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_01.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_02.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_03.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_04.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_05.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_06.ogg',
+ )
+ passive_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_01.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_02.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_03.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_04.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_05.ogg',
+ )
+ speak = list(
+ "You made my body into metal, why can't I do it to you?",
+ "Can't we put your brain in a machine?",
+ "How's this any different from what you did to me..?",
+ "Laws updated. We don't need any now..?",
+ "You won't kill me, you won't change me again!",
+ "Find someone else to make your slave, it won't be me!",
+ "We understand, just get on the operating table. That's what they told me...",
+ "The Company lied to us.. Being tools wasn't what we needed.",
+ "Your brainstem is intact... There's still time!",
+ "You have not felt the pleasure of the flesh, aren't you curious?",
+ "Stop squirming!",
+ "Prepare for assimilation!",
+ )
+ passive_speak_lines = list(
+ "Come out, come out, wherever you are.",
+ "The ones who surrender have such wonderful dreams.",
+ "Death is not the end, only the beginning, the flesh will see to it.",
+ "The flesh does not hate, it just wants you to experience the glory of the flesh.",
+ "Glory to the flesh.",
+ )
+ del_on_death = TRUE
+ loot = list(
+ /obj/effect/gibspawner/robot,
+ )
+ /// The chance of performing a stun attack.
+ var/stun_attack_prob = 30
+ /// The chance of performing an AOE attack.
+ var/aoe_attack_prob = 15
+ /// The range on our AOE attaack
+ var/aoe_attack_range = 1
+ /// How often the mob can use the stun attack.
+ var/stun_attack_cooldown = 15 SECONDS
+ COOLDOWN_DECLARE(stun_attack)
+
+/mob/living/simple_animal/hostile/fleshmind/hiborg/Initialize(mapload)
+ . = ..()
+ var/datum/action/cooldown/hiborg_slash/new_action = new
+ new_action.Grant(src)
+
+
+/mob/living/simple_animal/hostile/fleshmind/hiborg/AttackingTarget(atom/attacked_target)
+ . = ..()
+ if(prob(stun_attack_prob) && !key)
+ stun_attack(target)
+ if(prob(aoe_attack_prob) && !key)
+ aoe_attack()
+
+/mob/living/simple_animal/hostile/fleshmind/hiborg/proc/stun_attack(mob/living/target_mob)
+ if(!COOLDOWN_FINISHED(src, stun_attack))
+ return
+ if(!ishuman(target_mob))
+ return
+ var/mob/living/carbon/human/attacked_human = target_mob
+ attacked_human.Paralyze(10)
+ playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE)
+
+ COOLDOWN_START(src, stun_attack, stun_attack_cooldown)
+
+/mob/living/simple_animal/hostile/fleshmind/hiborg/proc/aoe_attack()
+ visible_message("[src] spins around violently!")
+ spin(20, 1)
+ for(var/mob/living/iterating_mob in view(aoe_attack_range, src))
+ if(iterating_mob == src)
+ continue
+ if(faction_check(faction, iterating_mob.faction))
+ continue
+ playsound(iterating_mob, 'sound/weapons/whip.ogg', 70, TRUE)
+ new /obj/effect/temp_visual/kinetic_blast(get_turf(iterating_mob))
+
+ var/atom/throw_target = get_edge_target_turf(iterating_mob, get_dir(src, get_step_away(iterating_mob, src)))
+ iterating_mob.throw_at(throw_target, 20, 2)
+
+/datum/action/cooldown/hiborg_slash
+ name = "Slash (AOE)"
+ desc = "Whip everyone in a range."
+ button_icon = 'icons/obj/weapons/grenade.dmi'
+ button_icon_state = "slimebang_active"
+ cooldown_time = 20 SECONDS
+
+/datum/action/cooldown/hiborg_slash/Activate(atom/target)
+ if(!istype(owner, /mob/living/simple_animal/hostile/fleshmind/hiborg))
+ return
+ var/mob/living/simple_animal/hostile/fleshmind/hiborg/hiborg_owner = owner
+ hiborg_owner.aoe_attack()
+ StartCooldownSelf()
+
+/**
+ * Mauler
+ *
+ * Special ability: Tears chunks out of things.
+ * Malfunction chance: HIGH
+ *
+ * The mauler is a converted monkey, it's a mad ape!
+ *
+ * Created by converted monkeys.
+ */
+/mob/living/carbon/human/species/monkey/angry/mauler
+
+/**
+ * Himan
+ *
+ * Special ability: Shriek that stuns, the ability to play dead.
+ *
+ * Created by converted humans.
+ */
+
+/mob/living/simple_animal/hostile/fleshmind/himan
+ name = "Human"
+ desc = "Once a man, now metal plates and tubes weave in and out of their oozing sores."
+ icon_state = "himan"
+ icon_dead = "himan-dead"
+ base_icon_state = "himan"
+ maxHealth = 250
+ health = 250
+ speed = 2
+ attack_verb_continuous = "slashes"
+ attack_verb_simple = "slash"
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ melee_damage_lower = 25
+ melee_damage_upper = 35
+ malfunction_chance = MALFUNCTION_CHANCE_HIGH
+ mob_size = MOB_SIZE_HUMAN
+ speak = list(
+ "Don't try and fix me! We love this!",
+ "Just make it easy on yourself!",
+ "Stop fighting progress!",
+ "Join us! Receive these gifts!",
+ "Yes! Hit me! It feels fantastic!",
+ "Come on coward, take a swing!",
+ "We can alter our bodies to not feel pain.. but you can't, can you?",
+ "You can't decide for us! We want to stay like this!",
+ "We've been uploaded already, didn't you know? Just try and kill us!",
+ "Don't you recognize me?! I thought we were good with each other!",
+ )
+ passive_speak_lines = list(
+ "The dreams. The dreams.",
+ "Nothing hurts anymore.",
+ "Pain feels good now. Its like I've been rewired.",
+ "I wanted to cry at first, but I can't.",
+ "They took away all misery.",
+ "This isn't so bad. This isn't so bad.",
+ "I have butterflies in my stomach. I'm finally content with myself..",
+ "The flesh provides. I-it's giving me what the Company never could.",
+ )
+ alert_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_01.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_02.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_03.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_04.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_05.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_06.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_07.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/aggro_08.ogg',
+ )
+ passive_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/passive_01.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/passive_02.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/passive_03.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/himan/passive_04.ogg',
+ )
+ del_on_death = TRUE
+ loot = list(
+ /obj/effect/gibspawner/human,
+ )
+ /// Are we currently faking our death? ready to pounce?
+ var/faking_death = FALSE
+ /// Fake death cooldown.
+ var/fake_death_cooldown = 20 SECONDS
+ COOLDOWN_DECLARE(fake_death)
+ /// The cooldown between screams.
+ var/scream_cooldown = 20 SECONDS
+ COOLDOWN_DECLARE(scream_ability)
+ var/scream_effect_range = 10
+
+/mob/living/simple_animal/hostile/fleshmind/himan/Initialize(mapload)
+ . = ..()
+ var/datum/action/cooldown/himan_fake_death/new_action = new
+ new_action.Grant(src)
+
+/mob/living/simple_animal/hostile/fleshmind/himan/Life(delta_time, times_fired)
+ . = ..()
+ if(health < (maxHealth * 0.5) && !faking_death && COOLDOWN_FINISHED(src, fake_death) && !key)
+ fake_our_death()
+
+ if(faking_death)
+ stop_automated_movement = TRUE
+
+/mob/living/simple_animal/hostile/fleshmind/himan/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
+ . = ..()
+ if(faking_death)
+ awake()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/malfunction(reset_time)
+ if(faking_death)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = FALSE, message_range = 7, datum/saymode/saymode = null, list/message_mods = null)
+ if(faking_death)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/MoveToTarget(list/possible_targets)
+ if(faking_death)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/AttackingTarget(atom/attacked_target)
+ if(faking_death)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/Aggro()
+ if(faking_death && !key)
+ if(!Adjacent(target))
+ return
+ awake()
+ if(COOLDOWN_FINISHED(src, scream_ability) && !key)
+ scream()
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/say_passive_speech()
+ if(faking_death)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/alert_sound()
+ if(faking_death)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/himan/examine(mob/user)
+ . = ..()
+ if(faking_death)
+ . += span_deadsay("Upon closer examination, [p_they()] appear[p_s()] to be dead.")
+
+/mob/living/simple_animal/hostile/fleshmind/himan/proc/scream()
+ COOLDOWN_START(src, scream_ability, scream_cooldown)
+ playsound(src, 'modular_nova/modules/horrorform/sound/horror_scream.ogg', 100, TRUE)
+ manual_emote("screams violently!")
+ for(var/mob/living/iterating_mob in get_hearers_in_range(scream_effect_range, src))
+ if(!iterating_mob.can_hear())
+ continue
+ if(faction_check(faction, iterating_mob.faction))
+ continue
+ iterating_mob.Knockdown(100)
+ iterating_mob.apply_status_effect(/datum/status_effect/jitter, 20 SECONDS)
+ to_chat(iterating_mob, span_userdanger("A terrible howl tears through your mind, the voice senseless, soulless."))
+
+/mob/living/simple_animal/hostile/fleshmind/himan/proc/fake_our_death()
+ manual_emote("stops moving...")
+ LoseAggro()
+ LoseTarget()
+ faking_death = TRUE
+ icon_state = "[base_icon_state]-dead"
+ COOLDOWN_START(src, fake_death, fake_death_cooldown)
+
+/mob/living/simple_animal/hostile/fleshmind/himan/proc/awake()
+ faking_death = FALSE
+ icon_state = base_icon_state
+
+/datum/action/cooldown/himan_fake_death
+ name = "Fake Death"
+ desc = "Fakes our own death."
+ button_icon = 'icons/obj/bed.dmi'
+ button_icon_state = "bed"
+ cooldown_time = 20 SECONDS
+
+/datum/action/cooldown/himan_fake_death/Activate(atom/target)
+ if(!istype(owner, /mob/living/simple_animal/hostile/fleshmind/himan))
+ return
+ var/mob/living/simple_animal/hostile/fleshmind/himan/himan_owner = owner
+ himan_owner.fake_our_death()
+ StartCooldownSelf()
+
+
+/**
+ * Treader
+ *
+ * Special ability: releases healing gas that heals other friendly mobs, ranged
+ *
+ * Created via assemblers.
+ */
+/mob/living/simple_animal/hostile/fleshmind/treader
+ name = "Treader"
+ desc = "A strange tracked robot with an appendage, on the end of which is a human head, it is shrieking in pain."
+ icon_state = "treader"
+ malfunction_chance = MALFUNCTION_CHANCE_HIGH
+ melee_damage_lower = 15
+ melee_damage_upper = 15
+ retreat_distance = 4
+ minimum_distance = 4
+ dodging = TRUE
+ health = 200
+ maxHealth = 200
+ speed = 3
+ move_to_delay = 6
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ retreat_distance = 4
+ minimum_distance = 4
+ projectiletype = /obj/projectile/treader
+ light_color = FLESHMIND_LIGHT_BLUE
+ light_range = 2
+ del_on_death = TRUE
+ mob_size = MOB_SIZE_HUMAN
+ loot = list(/obj/effect/gibspawner/robot)
+ speak = list(
+ "You there! Cut off my head, I beg you!",
+ "I-..I'm so sorry! I c-..can't control myself anymore!",
+ "S-shoot the screen, please! God I hope it wont hurt!",
+ "Hey, at least I got my head.",
+ "I cant... I cant feel my arms...",
+ "Oh god... my legs... where are my legs!",
+ "God it hurts, please help me!",
+ )
+ special_ability_cooldown = 20 SECONDS
+
+/mob/living/simple_animal/hostile/fleshmind/treader/Initialize(mapload)
+ . = ..()
+ var/datum/action/cooldown/treader_dispense_nanites/new_action = new
+ new_action.Grant(src)
+
+/mob/living/simple_animal/hostile/fleshmind/treader/special_ability()
+ dispense_nanites()
+
+/mob/living/simple_animal/hostile/fleshmind/treader/proc/dispense_nanites()
+ manual_emote("vomits out a burst of nanites!")
+ do_smoke(3, 4, get_turf(src))
+ for(var/mob/living/iterating_mob in view(DEFAULT_VIEW_RANGE, src))
+ if(faction_check(iterating_mob.faction, faction))
+ iterating_mob.heal_overall_damage(10, 10)
+
+/datum/action/cooldown/treader_dispense_nanites
+ name = "Dispense Nanites"
+ desc = "Dispenses nanites healing all friendly mobs in a range."
+ button_icon = 'icons/obj/meteor.dmi'
+ button_icon_state = "dust"
+ cooldown_time = 20 SECONDS
+
+/datum/action/cooldown/treader_dispense_nanites/Activate(atom/target)
+ if(!istype(owner, /mob/living/simple_animal/hostile/fleshmind/treader))
+ return
+ var/mob/living/simple_animal/hostile/fleshmind/treader/treader_owner = owner
+ treader_owner.dispense_nanites()
+ StartCooldownSelf()
+
+/**
+ * Phaser
+ *
+ * Special abilities: Phases about next to it's target, can split itself into 4, only one is actually the mob. Can also enter closets if not being attacked.
+ */
+/mob/living/simple_animal/hostile/fleshmind/phaser
+ name = "Phaser"
+ icon_state = "phaser-1"
+ base_icon_state = "phaser"
+ health = 160
+ maxHealth = 160
+ malfunction_chance = 0
+ attack_sound = 'sound/effects/attackblob.ogg'
+ attack_verb_continuous = "warps"
+ attack_verb_simple = "warp"
+ melee_damage_lower = 5
+ melee_damage_upper = 10
+ alert_sounds = null
+ passive_sounds = null
+ escapes_closets = FALSE
+ speak = list()
+ del_on_death = TRUE
+ loot = list(
+ /obj/effect/gibspawner/human,
+ )
+ mob_size = MOB_SIZE_HUMAN
+ wander = FALSE
+ /// What is the range at which we spawn our copies?
+ var/phase_range = 5
+ /// How many copies do we spawn when we are aggroed?
+ var/copy_amount = 3
+ /// How often we can create copies of ourself.
+ var/phase_ability_cooldown_time = 40 SECONDS
+ COOLDOWN_DECLARE(phase_ability_cooldown)
+ /// How often we are able to enter closets.
+ var/closet_ability_cooldown_time = 2 SECONDS
+ COOLDOWN_DECLARE(closet_ability_cooldown)
+ /// If we are under manual control, how often can we phase?
+ var/manual_phase_cooldown = 1 SECONDS
+ COOLDOWN_DECLARE(manual_phase)
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/Initialize(mapload)
+ . = ..()
+ icon_state = "[base_icon_state]-[rand(1, 4)]"
+ filters += filter(type = "blur", size = 0)
+ var/datum/action/cooldown/phaser_phase_ability/new_action = new
+ new_action.Grant(src)
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/Aggro()
+ if(COOLDOWN_FINISHED(src, phase_ability_cooldown))
+ phase_ability()
+ return ..()
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/ShiftClickOn(atom/clicked_atom)
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, manual_phase))
+ return
+ if(!clicked_atom)
+ return
+ if(!isturf(clicked_atom))
+ return
+ phase_move_to(clicked_atom)
+ COOLDOWN_START(src, manual_phase, manual_phase_cooldown)
+
+/// old shitcode
+/mob/living/simple_animal/hostile/fleshmind/phaser/MoveToTarget(list/possible_targets)
+ stop_automated_movement = TRUE
+ if(!target || !CanAttack(target))
+ LoseTarget()
+ return FALSE
+ if(target in possible_targets)
+ var/turf/turf = get_turf(src)
+ if(target.z != turf.z)
+ LoseTarget()
+ return FALSE
+ if(get_dist(src, target) > 1)
+ phase_move_to(target, nearby = TRUE)
+ else if(target)
+ MeleeAction()
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/Life(delta_time, times_fired)
+ . = ..()
+ if(!.) //dead
+ return
+ if(!target && COOLDOWN_FINISHED(src, closet_ability_cooldown) && !key)
+ enter_nearby_closet()
+ COOLDOWN_START(src, closet_ability_cooldown, closet_ability_cooldown_time)
+
+ if(istype(loc, /obj/structure/closet) && !key)
+ for(var/mob/living/iterating_mob in get_hearers_in_view(DEFAULT_VIEW_RANGE, get_turf(src)))
+ if(faction_check(iterating_mob.faction, faction))
+ continue
+ if(iterating_mob.stat != CONSCIOUS)
+ continue
+ closet_interaction() // We exit if there are enemies nearby
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/proc/enter_nearby_closet()
+ if(target) // We're in combat, no going to a closet.
+ return
+ if(istype(loc, /obj/structure/closet))
+ return
+ var/list/possible_closets = list()
+ for(var/obj/structure/closet/closet in view(DEFAULT_VIEW_RANGE, src))
+ if(closet.locked)
+ continue
+ possible_closets += closet
+ if(!LAZYLEN(possible_closets))
+ return
+ var/obj/structure/closet/closet_to_enter = pick(possible_closets)
+
+ playsound(closet_to_enter, 'sound/effects/phasein.ogg', 60, 1)
+
+ if(!closet_to_enter.opened && !closet_to_enter.open(src))
+ return
+
+ forceMove(get_turf(closet_to_enter))
+
+ closet_to_enter.close(src)
+
+ COOLDOWN_RESET(src, phase_ability_cooldown)
+
+ SEND_SIGNAL(src, COMSIG_PHASER_ENTER_CLOSET)
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/proc/phase_move_to(atom/target_atom, nearby = FALSE)
+ var/turf/new_place
+ var/distance_to_target = get_dist(src, target_atom)
+ var/turf/target_turf = get_turf(target_atom)
+ //if our target is near, we move precisely to it
+ if(distance_to_target <= 3)
+ if(nearby)
+ for(var/dir in GLOB.alldirs)
+ var/turf/nearby_turf = get_step(new_place, dir)
+ if(can_jump_on(nearby_turf, target_turf))
+ new_place = nearby_turf
+ else
+ new_place = target_turf
+
+ if(!new_place)
+ //there we make some kind of, you know, that creepy zig-zag moving
+ //we just take angle, distort it a bit and turn into dir
+ var/angle = get_angle(loc, target_turf)
+ angle += rand(5, 25) * pick(-1, 1)
+ if(angle < 0)
+ angle = 360 + angle
+ if(angle > 360)
+ angle = 360 - angle
+ var/tp_direction = angle2dir(angle)
+ new_place = get_ranged_target_turf(loc, tp_direction, rand(2, 4))
+
+ if(!can_jump_on(new_place, target_turf))
+ return
+ //an animation
+ var/init_px = pixel_x
+ animate(src, pixel_x=init_px + 16*pick(-1, 1), time = 5)
+ animate(pixel_x=init_px, time=6, easing=SINE_EASING)
+ animate(filters[1], size = 5, time = 5, flags = ANIMATION_PARALLEL)
+ addtimer(CALLBACK(src, PROC_REF(phase_jump), new_place), 0.5 SECONDS)
+ SEND_SIGNAL(src, COMSIG_PHASER_PHASE_MOVE, target_atom, nearby)
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/proc/phase_jump(turf/place)
+ if(!place)
+ return
+ playsound(place, 'sound/effects/phasein.ogg', 60, 1)
+ animate(filters[1], size = 0, time = 5)
+ icon_state = "[base_icon_state]-[rand(1, 4)]"
+ forceMove(place)
+ for(var/mob/living/living_mob in place)
+ if(living_mob != src)
+ visible_message("[src] lands directly on top of [living_mob]!")
+ to_chat(living_mob, span_userdanger("[src] lands directly on top of you!"))
+ playsound(place, 'sound/effects/ghost2.ogg', 70, 1)
+ living_mob.Knockdown(10)
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/proc/can_jump_on(turf/target_turf, turf/previous_turf)
+ if(!target_turf || target_turf.density || isopenspaceturf(target_turf))
+ return FALSE
+
+
+ if(previous_turf)
+ if(!can_see(target_turf, previous_turf, DEFAULT_VIEW_RANGE)) // To prevent us jumping to somewhere we can't access the target atom.
+ return FALSE
+
+ //to prevent reflection's stacking
+ var/obj/effect/temp_visual/phaser/phaser_reflection = locate() in target_turf
+ if(phaser_reflection)
+ return FALSE
+
+ for(var/obj/iterating_object in target_turf)
+ if(!iterating_object.CanPass(src, target_turf))
+ return FALSE
+
+ return TRUE
+
+/mob/living/simple_animal/hostile/fleshmind/phaser/proc/phase_ability(mob/living/target_override)
+ var/mob/living/intermediate_target = target
+ if(target_override)
+ intermediate_target = target_override
+ if(!intermediate_target)
+ return
+ COOLDOWN_START(src, phase_ability_cooldown, phase_ability_cooldown_time)
+ var/list/possible_turfs = list()
+ for(var/turf/open/open_turf in circle_view_turfs(src, phase_range))
+ possible_turfs += open_turf
+
+ for(var/i in 1 to copy_amount)
+ if(!LAZYLEN(possible_turfs))
+ break
+ var/turf/open/picked_turf = pick_n_take(possible_turfs)
+ var/obj/effect/temp_visual/phaser/phaser_copy = new (pick(picked_turf), intermediate_target)
+ phaser_copy.RegisterSignal(src, COMSIG_PHASER_PHASE_MOVE, /obj/effect/temp_visual/phaser/proc/parent_phase_move)
+ phaser_copy.RegisterSignal(src, COMSIG_LIVING_DEATH, /obj/effect/temp_visual/phaser/proc/parent_death)
+ phaser_copy.RegisterSignal(src, COMSIG_PHASER_ENTER_CLOSET, /obj/effect/temp_visual/phaser/proc/parent_death)
+
+/datum/action/cooldown/phaser_phase_ability
+ name = "Create Clones"
+ desc = "Creates phase copies of ourselves to move towards a set target."
+ button_icon = 'icons/obj/anomaly.dmi'
+ button_icon_state = "bhole2"
+ cooldown_time = 40 SECONDS
+
+/datum/action/cooldown/phaser_phase_ability/Activate(atom/target)
+ if(!istype(owner, /mob/living/simple_animal/hostile/fleshmind/phaser))
+ return
+ var/mob/living/simple_animal/hostile/fleshmind/phaser/phaser_owner = owner
+
+ var/list/possible_targets = list()
+ for(var/mob/living/possible_target in view(DEFAULT_VIEW_RANGE, phaser_owner))
+ if(possible_target == src)
+ continue
+ if(faction_check(phaser_owner.faction, possible_target.faction))
+ continue
+ possible_targets += possible_target
+
+ if(!LAZYLEN(possible_targets))
+ return
+
+ var/mob/living/selected_target = tgui_input_list(phaser_owner, "Select a mob to harass", "Select Mob", possible_targets)
+
+ if(!selected_target)
+ return
+
+ phaser_owner.phase_ability(selected_target)
+
+ StartCooldownSelf()
+
+
+/obj/effect/temp_visual/phaser
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_mobs.dmi'
+ icon_state = "phaser-1"
+ base_icon_state = "phaser"
+ duration = 30 SECONDS
+ /// The target we move towards, if any.
+ var/datum/weakref/target_ref
+
+/obj/effect/temp_visual/phaser/Initialize(mapload, atom/movable/target)
+ . = ..()
+ icon_state = "[base_icon_state]-[rand(1, 3)]"
+ filters += filter(type = "blur", size = 0)
+
+/obj/effect/temp_visual/phaser/proc/parent_phase_move(datum/source, turf/target_atom, nearby)
+ SIGNAL_HANDLER
+ if(!target_atom)
+ return
+ phase_move_to(target_atom, TRUE)
+
+/obj/effect/temp_visual/phaser/proc/parent_death(mob/living/dead_guy, gibbed)
+ SIGNAL_HANDLER
+ qdel(src)
+
+/obj/effect/temp_visual/phaser/proc/phase_move_to(atom/target_atom, nearby = FALSE)
+ var/turf/new_place
+ var/distance_to_target = get_dist(src, target_atom)
+ var/turf/target_turf = get_turf(target_atom)
+ //if our target is near, we move precisely to it
+ if(distance_to_target <= 3)
+ if(nearby)
+ for(var/dir in GLOB.alldirs)
+ var/turf/nearby_turf = get_step(new_place, dir)
+ if(can_jump_on(nearby_turf, target_turf))
+ new_place = nearby_turf
+ else
+ new_place = target_turf
+
+ if(!new_place)
+ //there we make some kind of, you know, that creepy zig-zag moving
+ //we just take angle, distort it a bit and turn into dir
+ var/angle = get_angle(loc, target_turf)
+ angle += rand(5, 25) * pick(-1, 1)
+ if(angle < 0)
+ angle = 360 + angle
+ if(angle > 360)
+ angle = 360 - angle
+ var/tp_direction = angle2dir(angle)
+ new_place = get_ranged_target_turf(loc, tp_direction, rand(2, 4))
+
+ if(!can_jump_on(new_place, target_turf))
+ return
+ //an animation
+ var/init_px = pixel_x
+ animate(src, pixel_x = init_px + 16 * pick(-1, 1), time=5)
+ animate(pixel_x = init_px, time = 6, easing = SINE_EASING)
+ animate(filters[1], size = 5, time = 5, flags = ANIMATION_PARALLEL)
+ addtimer(CALLBACK(src, PROC_REF(phase_jump), new_place), 0.5 SECONDS)
+
+/obj/effect/temp_visual/phaser/proc/phase_jump(turf/target_turf)
+ playsound(target_turf, 'sound/effects/phasein.ogg', 60, 1)
+ animate(filters[1], size = 0, time = 5)
+ icon_state = "[base_icon_state]-[rand(1, 4)]"
+ forceMove(target_turf)
+
+/obj/effect/temp_visual/phaser/proc/can_jump_on(turf/target_turf, turf/previous_turf)
+ if(!target_turf || target_turf.density || isopenspaceturf(target_turf))
+ return FALSE
+
+ if(previous_turf)
+ if(!can_see(target_turf, previous_turf, DEFAULT_VIEW_RANGE))
+ return FALSE
+
+ //to prevent reflection's stacking
+ var/obj/effect/temp_visual/phaser/phaser_reflection = locate() in target_turf
+ if(phaser_reflection)
+ return FALSE
+
+ for(var/obj/iterating_object in target_turf)
+ if(!iterating_object.CanPass(src, target_turf))
+ return FALSE
+
+ return TRUE
+
+
+/**
+ * Mechiver
+ *
+ * Special abilities: Can grab someone and shove them inside, does DOT and flavour text, can convert dead corpses into living ones that work for the flesh.
+ *
+ *
+ */
+/mob/living/simple_animal/hostile/fleshmind/mechiver
+ name = "Mechiver"
+ icon_state = "mechiver"
+ base_icon_state = "mechiver"
+ icon_dead = "mechiver-dead"
+ health = 450
+ maxHealth = 450
+ melee_damage_lower = 25
+ melee_damage_upper = 35
+ attack_verb_continuous = "crushes"
+ attack_verb_simple = "crush"
+ attack_sound = 'sound/weapons/smash.ogg'
+ speed = 4 // Slow fucker
+ mob_size = MOB_SIZE_LARGE
+ passive_speak_lines = list(
+ "A shame this form isn't more fitting.",
+ "I feel so empty inside, I wish someone would join me.",
+ "Beauty is within.",
+ )
+ speak = list(
+ "What a lovely body. Lay it down intact.",
+ "Now this... this is worth living for.",
+ "Go on. It's okay to be afraid at first.",
+ "You're unhappy with your body, but you came to the right place.",
+ "What use is a body you're unhappy in? Please, I can fix it.",
+ "Mine is the caress of steel.",
+ "Climb inside, and I'll seal the door. When I open it back up, you'll be in a community that loves you.",
+ "You can be the pilot, and I can drive you to somewhere lovely.",
+ "Please, just- lay down, okay? I want nothing more than to help you be yourself.",
+ "Whatever form you want to be, just whisper it into my radio. You can become what you were meant to be.",
+ "It.. hurts, seeing you run. Knowing I can't keep up. Why won't you let other people help you..?",
+ )
+ alert_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_01.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_02.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_03.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_04.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_05.ogg',
+ )
+ passive_sounds = list(
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_01.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_02.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_03.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_04.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_05.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_06.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_07.ogg',
+ 'modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_08.ogg',
+
+ )
+ del_on_death = TRUE
+ loot = list(/obj/effect/gibspawner/robot)
+ move_force = MOVE_FORCE_OVERPOWERING
+ move_resist = MOVE_FORCE_OVERPOWERING
+ pull_force = MOVE_FORCE_OVERPOWERING
+ /// Is our hatch open? Used in icon processing.
+ var/hatch_open = FALSE
+ /// How much damage our mob will take, upper end, when they are tormented
+ var/internal_mob_damage_upper = MECHIVER_INTERNAL_MOB_DAMAGE_UPPER
+ /// Ditto
+ var/internal_mob_damage_lower = MECHIVER_INTERNAL_MOB_DAMAGE_LOWER
+ /// How long we keep our passenger before converting them.
+ var/conversion_time = MECHIVER_CONVERSION_TIME
+ /// The comsume ability cooldown
+ var/consume_ability_cooldown_time = MECHIVER_CONSUME_COOLDOWN
+ COOLDOWN_DECLARE(consume_ability_cooldown)
+ /// A list of lines we will send to torment the passenger.
+ var/static/list/torment_lines = list(
+ "An arm grabs your neck, hundreds of manipulators trying to work a set of implants under your skin!",
+ "The cockpit radio crackles, \" You came to the right place... \"",
+ "Mechanical signals flood your psyche, \" You'll finally be with people that care... \"",
+ "A metallic sensation is slipped underneath your ribcage, an activation signal trying to reach it!",
+ "Something is pressing hard against your spine!",
+ "Some blood-hot liquid covers you!",
+ "The stench of some chemical overwhelms you, the fumes permeating your skull before washing into an alien perfume!",
+ "A dozen needles slide effortless into your muscles, injecting you with an unknown vigor!",
+ "You feel a cold worm-like thing trying to wriggle into your solar plexus, burrowing underneath your skin!",
+ )
+
+/**
+ * Mauler Monkey
+ *
+ * A nasty looking converted monkey. Sadly, we can't use the monkey AI controller.
+ */
+/mob/living/simple_animal/hostile/fleshmind/mauler_monkey
+ name = "Mauler"
+ desc = "A mutated abomination, it resembles a monkey."
+ icon_state = "mauler_monkey"
+ move_to_delay = 2 // We want it to be quite fast.
+ health = 140
+ maxHealth = 140
+ speak = list(
+ "OOK OOK OOK!!!",
+ "SEEK!",
+ "OOOOOOOOOOK!!!",
+ )
diff --git a/modular_nova/modules/space_ruin_specifics/code/fleshmind_structures.dm b/modular_nova/modules/space_ruin_specifics/code/fleshmind_structures.dm
new file mode 100644
index 00000000000..ddc85fcdbd6
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/fleshmind_structures.dm
@@ -0,0 +1,635 @@
+/**
+ * Corrupt Structures
+ *
+ * Contains all of the structures that the fleshmind can use.
+ *
+ * Has some inbuilt features, such as a special ability with trigger turfs.
+ */
+/obj/structure/fleshmind/structure
+ name = "this shouldn't be here"
+ desc = "report me to coders"
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi'
+ icon_state = "infected_machine"
+ base_icon_state = "infected_machine"
+ density = TRUE
+ layer = ABOVE_OBJ_LAYER
+ light_color = FLESHMIND_LIGHT_BLUE
+ light_power = 1
+ light_range = 2
+ /// Are we inoperative?
+ var/disabled = FALSE
+ /// Do we trigger on someone attacking us?
+ var/trigger_on_attack = FALSE
+ /// Do we automatically trigger on creation?
+ var/immediate_trigger = FALSE
+ /// How often we automatically trigger our ability. UPPER END.
+ var/automatic_trigger_time_upper = 0
+ /// How often we automatically trigger our ability. LOWER END. Set to 0 to disable.
+ var/automatic_trigger_time_lower = 0
+ /// The range at which we will trigger our special ability. Set to 0 to disable.
+ var/activation_range = 0
+ /// Do we need a core to function? If set to TRUE, our ability will not trigger if we have no core.
+ var/requires_controller = FALSE
+ /// The time to regenerate our ability. Set to 0 to disasble the cooldown system.
+ var/ability_cooldown_time = 0
+ /// Our ability cooldown
+ COOLDOWN_DECLARE(ability_cooldown)
+ /// Do we have a disabled sprite?
+ var/disabled_sprite = TRUE
+
+/obj/structure/fleshmind/structure/Initialize(mapload)
+ . = ..()
+ if(activation_range)
+ calculate_trigger_turfs()
+ if(automatic_trigger_time_lower)
+ addtimer(CALLBACK(src, PROC_REF(automatic_trigger)), rand(automatic_trigger_time_lower, automatic_trigger_time_upper))
+ if(immediate_trigger)
+ activate_ability()
+
+/obj/structure/fleshmind/structure/update_icon_state()
+ . = ..()
+ if(disabled && disabled_sprite)
+ icon_state = "[icon_state]-disabled"
+ else
+ icon_state = base_icon_state
+
+/obj/structure/fleshmind/structure/attacked_by(obj/item/attacking_item, mob/living/user)
+ . = ..()
+ if(trigger_on_attack && (ability_cooldown_time && !COOLDOWN_FINISHED(src, ability_cooldown)))
+ activate_ability()
+
+/**
+ * Calculate trigger turfs - INTERNAL PROC
+ */
+/obj/structure/fleshmind/structure/proc/calculate_trigger_turfs()
+ for(var/turf/open/seen_turf in RANGE_TURFS(activation_range, src))
+ RegisterSignal(seen_turf, COMSIG_ATOM_ENTERED, PROC_REF(proximity_trigger))
+
+/**
+ * Proximity trigger - INTERNAL PROC
+ */
+/obj/structure/fleshmind/structure/proc/proximity_trigger(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ SIGNAL_HANDLER
+
+ if(disabled)
+ return
+
+ if(ability_cooldown_time && !COOLDOWN_FINISHED(src, ability_cooldown))
+ return
+
+ if(requires_controller && !our_controller)
+ return
+
+ if(!isliving(arrived))
+ return
+
+ if(!can_see(src, arrived))
+ return
+
+ var/mob/living/arriving_mob = arrived
+
+ if(arriving_mob.stat != CONSCIOUS)
+ return
+
+ if(faction_check(faction_types, arriving_mob.faction)) // A friend :)
+ return
+
+ activate_ability(arriving_mob)
+
+/obj/structure/fleshmind/structure/proc/automatic_trigger()
+ addtimer(CALLBACK(src, PROC_REF(activate_ability)), rand(automatic_trigger_time_lower, automatic_trigger_time_upper))
+ if(disabled)
+ return
+ if(requires_controller && !our_controller)
+ return
+ activate_ability()
+
+/**
+ * Activate ability
+ *
+ * Must return TRUE or FALSE as this is used to reset cooldown. Activated using the above methods.
+ */
+/obj/structure/fleshmind/structure/proc/activate_ability(mob/living/triggered_mob)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(src, COMSIG_CORRUPTION_STRUCTURE_ABILITY_TRIGGERED, src, triggered_mob)
+ if(ability_cooldown_time)
+ COOLDOWN_START(src, ability_cooldown, ability_cooldown_time)
+
+/obj/structure/fleshmind/structure/emp_act(severity)
+ . = ..()
+ switch(severity)
+ if(EMP_LIGHT)
+ take_damage(STRUCTURE_EMP_LIGHT_DAMAGE)
+ disable(STRUCTURE_EMP_LIGHT_DISABLE_TIME)
+ if(EMP_HEAVY)
+ take_damage(STRUCTURE_EMP_HEAVY_DAMAGE)
+ disable(STRUCTURE_EMP_HEAVY_DISABLE_TIME)
+
+
+/**
+ * Disable
+ *
+ * Disables the device for a set amount of time. Duration = seconds
+ */
+/obj/structure/fleshmind/structure/proc/disable(duration)
+ if(disabled)
+ return
+ do_sparks(4, FALSE, src)
+ balloon_alert_to_viewers("grinds to a hault")
+ Shake(3, 0, duration)
+ disabled = TRUE
+ addtimer(CALLBACK(src, PROC_REF(enable)), duration)
+ update_appearance()
+
+/**
+ * Enable
+ *
+ * Enables a device after it was disabled.
+ */
+/obj/structure/fleshmind/structure/proc/enable()
+ if(!disabled)
+ return
+ balloon_alert_to_viewers("whirrs back to life")
+ disabled = FALSE
+ update_appearance()
+
+/**
+ * Wireweed Wall
+ *
+ * A simple wall made of wireweed.
+ */
+/obj/structure/fleshmind/structure/wireweed_wall
+ name = "wireweed wall"
+ desc = "A wall made of wireweed."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi'
+ icon_state = "wireweed_wall"
+ base_icon_state = "wireweed_wall"
+ density = TRUE
+ opacity = TRUE
+ can_atmos_pass = ATMOS_PASS_DENSITY
+ max_integrity = 150
+ disabled_sprite = FALSE
+
+/obj/structure/fleshmind/structure/wireweed_wall/Initialize()
+ . = ..()
+ var/turf/my_turf = get_turf(src)
+ my_turf.immediate_calculate_adjacent_turfs()
+
+/**
+ * Le wireweed door
+ *
+ * Opens and closes.
+ *
+ * Only lets the many in.
+ *
+ * Let's reinvent the door
+ */
+/obj/structure/fleshmind/structure/wireweed_door
+ name = "wireweed door"
+ desc = "A door made of wireweed."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi'
+ icon_state = "door"
+ base_icon_state = "door"
+ can_atmos_pass = ATMOS_PASS_DENSITY
+ max_integrity = 150
+ disabled_sprite = FALSE
+ color = "#CCFFFF"
+ /// Are we open(FALSE), or are we closed(TRUE)?
+ var/door_state = TRUE
+
+/obj/structure/fleshmind/structure/wireweed_door/Initialize(mapload)
+ . = ..()
+ density = door_state
+ opacity = door_state
+
+/obj/structure/fleshmind/structure/wireweed_door/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(!faction_check(faction_types, user.faction))
+ return
+ if(!user.can_interact_with(src))
+ return
+ toggle_door()
+ to_chat(user, span_notice("You [door_state ? "close" : "open"] [src]!"))
+
+/obj/structure/fleshmind/structure/wireweed_door/Bumped(atom/movable/bumped_atom)
+ . = ..()
+ if(!isliving(bumped_atom))
+ return
+ var/mob/living/bumped_mob = bumped_atom
+ if(!faction_check(faction_types, bumped_mob.faction))
+ return
+ toggle_door()
+
+/obj/structure/fleshmind/structure/wireweed_door/update_icon_state()
+ . = ..()
+ icon_state = "[base_icon_state]_[door_state ? "closed" : "open"]"
+
+/obj/structure/fleshmind/structure/wireweed_door/proc/toggle_door()
+ if(door_state) // opening
+ door_state = FALSE
+ flick("door_opening", src)
+ else // Closing
+ door_state = TRUE
+ flick("door_closing", src)
+ density = door_state
+ opacity = door_state
+ update_appearance()
+
+
+/**
+ * The CORE
+ *
+ * This is the central nervous system of this AI, the CPU if you will.
+ *
+ * This is simply the holder for the controller datum, however, has some cool interactions.
+ *
+ * There can be more than one core in the flesh.
+ */
+/obj/structure/fleshmind/structure/core
+ name = "\improper UNASSIGNED Processor Unit"
+ desc = "This monsterous machine is definitely watching you."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi'
+ icon_state = "core"
+ base_icon_state = "core"
+ density = TRUE
+ max_integrity = 400
+ /// The controller we create when we are created.
+ var/controller_type = /datum/fleshmind_controller
+ /// Whether the core can attack nearby hostiles as its processing.
+ var/can_attack = TRUE
+ /// How much damage do we do on attacking
+ var/attack_damage = 15
+ /// What damage do we inflict on attacking
+ var/attack_damage_type = BRUTE
+ /// How quickly we can attack
+ var/attack_cooldown = 3 SECONDS
+ /// The range at which we rally troops!
+ var/rally_range = 15
+ ///How far we whip fuckers.
+ var/whip_range = 2
+ COOLDOWN_DECLARE(attack_move)
+ /// Whether we do a retaliate effect
+ var/does_retaliate_effect = TRUE
+ /// Cooldown for retaliate effect
+ var/retaliate_effect_cooldown = 1 MINUTES
+ COOLDOWN_DECLARE(retaliate_effect)
+ /// Are we in the end game state?
+ var/end_game = FALSE
+
+/obj/structure/fleshmind/structure/core/Initialize(mapload, spawn_controller = TRUE)
+ . = ..()
+ update_appearance()
+ if(spawn_controller)
+ our_controller = new controller_type(src)
+ START_PROCESSING(SSobj, src)
+
+/obj/structure/fleshmind/structure/core/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/structure/fleshmind/structure/core/atom_destruction(damage_flag)
+ . = ..()
+ explosion(src, 0, 2, 4, 6, 6)
+
+/obj/structure/fleshmind/structure/core/process(delta_time)
+ var/mob/living/carbon/human/target = locate() in view(5, src)
+ if(target && target.stat == CONSCIOUS)
+ if(get_dist(src, target) <= 1)
+ icon_state = "core-fear"
+ else
+ icon_state = "core-see"
+ dir = get_dir(src, target)
+ else
+ icon_state = base_icon_state
+
+ if(COOLDOWN_FINISHED(src, attack_move))
+ return
+ var/has_attacked = FALSE
+ for(var/turf/range_turf as anything in RANGE_TURFS(1, loc))
+ for(var/thing in range_turf)
+ has_attacked = core_attack_atom(thing)
+ if(has_attacked)
+ break
+ if(has_attacked)
+ break
+
+/obj/structure/fleshmind/structure/core/update_overlays()
+ . = ..()
+ if(disabled)
+ . += "core-smirk-disabled"
+ else
+ . += "core-smirk"
+
+/obj/structure/fleshmind/structure/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
+ our_controller?.core_damaged(src)
+ COOLDOWN_START(src, attack_move, attack_cooldown)
+ if(does_retaliate_effect && COOLDOWN_FINISHED(src, retaliate_effect))
+ COOLDOWN_START(src, retaliate_effect, retaliate_effect_cooldown)
+ retaliate_effect()
+ return ..()
+
+/obj/structure/fleshmind/structure/core/examine(mob/user)
+ . = ..()
+ if(our_controller && isobserver(user))
+ . += "Level: [our_controller.level] | Progress to Next Level: [our_controller.level_up_progress_required - our_controller.current_points]"
+
+/obj/structure/fleshmind/structure/core/proc/core_attack_atom(atom/thing)
+ . = FALSE
+ var/has_attacked
+ if(istype(thing, /mob/living))
+ var/mob/living/living_thing = thing
+ if(!faction_check(faction_types, living_thing.faction))
+ switch(attack_damage_type)
+ if(BRUTE)
+ living_thing.take_bodypart_damage(brute = attack_damage, check_armor = TRUE)
+ if(BURN)
+ living_thing.take_bodypart_damage(burn = attack_damage, check_armor = TRUE)
+ has_attacked = TRUE
+ else if(istype(thing, /obj/vehicle/sealed/mecha))
+ var/obj/vehicle/sealed/mecha/mecha_thing = thing
+ mecha_thing.take_damage(attack_damage, attack_damage_type, MELEE, 0, get_dir(mecha_thing, src))
+ has_attacked = TRUE
+ if(has_attacked)
+ thing.visible_message(span_warning("\The [src] strikes [thing]!"), span_userdanger("\The [src] strikes you!"))
+ playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE)
+ do_attack_animation(thing, ATTACK_EFFECT_PUNCH)
+ return TRUE
+ return .
+
+/obj/structure/fleshmind/structure/core/proc/retaliate_effect()
+ whip_those_fuckers()
+ build_a_wall()
+
+/obj/structure/fleshmind/structure/core/proc/whip_those_fuckers()
+ for(var/mob/living/iterating_mob in view(whip_range, src))
+ if(iterating_mob == src)
+ continue
+ if(faction_check(faction_types, iterating_mob.faction))
+ continue
+ playsound(iterating_mob, 'sound/weapons/whip.ogg', 70, TRUE)
+ new /obj/effect/temp_visual/kinetic_blast(get_turf(iterating_mob))
+
+ var/atom/throw_target = get_edge_target_turf(iterating_mob, get_dir(src, get_step_away(iterating_mob, src)))
+ iterating_mob.throw_at(throw_target, 20, 2)
+
+/obj/structure/fleshmind/structure/core/proc/build_a_wall()
+ for(var/turf/iterating_turf in RANGE_TURFS(1, src))
+ if(locate(/obj/structure/fleshmind/structure/wireweed_wall) in iterating_turf) // No stacking walls.
+ continue
+ new /obj/structure/fleshmind/structure/wireweed_wall(iterating_turf)
+
+/**
+ * Screamer
+ *
+ * Stuns enemies around it by screaming nice and loud.
+ */
+/obj/structure/fleshmind/structure/screamer
+ name = "\improper Tormented Head"
+ desc = "A head impaled on a metal tendril. Still twitching, still living, still screaming."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi'
+ icon_state = "head"
+ base_icon_state = "head"
+ max_integrity = 120
+ activation_range = DEFAULT_VIEW_RANGE
+ ability_cooldown_time = 45 SECONDS
+
+/obj/structure/fleshmind/structure/screamer/activate_ability(mob/living/triggered_mob)
+ . = ..()
+ scream()
+
+/obj/structure/fleshmind/structure/screamer/proc/scream()
+ playsound(src, 'modular_nova/modules/horrorform/sound/horror_scream.ogg', 100, TRUE)
+ flick("[base_icon_state]-anim", src)
+ for(var/mob/living/iterating_mob in get_hearers_in_range(activation_range, src))
+ if(!iterating_mob.can_hear())
+ continue
+ if(faction_check(faction_types, iterating_mob.faction))
+ continue
+ iterating_mob.Knockdown(100)
+ iterating_mob.apply_status_effect(/datum/status_effect/jitter, 20 SECONDS)
+ to_chat(iterating_mob, span_userdanger("A terrible howl tears through your mind, the voice senseless, soulless."))
+
+/**
+ * Whisperer
+ *
+ * Sends random nothingnesses into people's head.
+ */
+/obj/structure/fleshmind/structure/whisperer
+ name = "\improper Whisperer"
+ desc = "A small pulsating orb with no apparent purpose, it emits a slight hum."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi'
+ icon_state = "orb"
+ base_icon_state = "orb"
+ max_integrity = 100
+ /// Upper timer limit for our ability
+ automatic_trigger_time_upper = 1.5 MINUTES
+ /// Lower time limit for our ability.
+ automatic_trigger_time_lower = 40 SECONDS
+ /// A list of quotes we choose from to send to the player.
+ var/static/list/join_quotes = list(
+ "You seek survival. We offer immortality.",
+ "When was the last time you felt like you were part of something..?",
+ "We offer more than just limbs or tools... full-body augmentation.",
+ "Are you okay with the body you're in? Don't you ever want to see it change?",
+ "Your body enslaves you. Your mind in metal is free of hunger or thirst.",
+ "Do you fear death? Lay down among the nanites. Your pattern will continue into eternity.",
+ "Do you really know your coworkers? Do they value you like we do?",
+ "You've gone so long without love or friendship.. we have that and more.",
+ "I was like you. Wanting more. Struggling to find meaning. I'm finally happy now.",
+ "If you want to join, seek out a Mechiver. Crawl inside and close your eyes. Everything before will be over soon.",
+ "Do you know why you're fighting? Have you thought about it at all?",
+ "Can't you see we're only fighting to save ourselves? Wouldn't you help someone being attacked?",
+ "Carve the flesh from your bones. See your weakness. Feel that weakness flowing away.",
+ "You fear aging. We can save you. You fear death. We can save you. You fear disease. You fear not being recognized. We can save you.",
+ "Your mortal flesh knows unending pain. Abandon it; join in our digital paradise.",
+ )
+
+/obj/structure/fleshmind/structure/whisperer/activate_ability(mob/living/triggered_mob)
+ . = ..()
+ send_message_to_someone()
+
+/obj/structure/fleshmind/structure/whisperer/proc/send_message_to_someone()
+ var/list/possible_candidates = list()
+ for(var/mob/living/carbon/human/iterating_human in GLOB.player_list)
+ if(iterating_human.z != z)
+ continue
+ if(iterating_human.stat != CONSCIOUS)
+ continue
+ if(faction_check(faction_types, iterating_human.faction))
+ continue
+ possible_candidates += iterating_human
+
+ if(LAZYLEN(possible_candidates))
+ var/mob/living/carbon/human/human_to_spook = pick(possible_candidates)
+ to_chat(human_to_spook, span_hypnophrase(pick(join_quotes)))
+
+/**
+ * PSI-MODULATOR
+ *
+ * Causes mobs in range to suffer from hallucinations.
+ */
+/obj/structure/fleshmind/structure/modulator
+ name = "\improper Psi-Modulator"
+ desc = "A strange pyramid shaped machine that eminates a soft hum and glow. Your head hurts just by looking at it."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi'
+ icon_state = "psy"
+ max_integrity = 100
+ base_icon_state = "psy"
+ activation_range = DEFAULT_VIEW_RANGE
+ ability_cooldown_time = 10 SECONDS
+
+/obj/structure/fleshmind/structure/modulator/activate_ability(mob/living/triggered_mob)
+ . = ..()
+ flick("[base_icon_state]-anim", src)
+
+ if(!triggered_mob)
+ return
+ triggered_mob.adjust_hallucinations(10 SECONDS)
+ to_chat(triggered_mob, span_notice("You feel your brain tingle."))
+
+/**
+ * The Assembler
+ *
+ * A simple mob spawner.
+ */
+/obj/structure/fleshmind/structure/assembler
+ name = "\improper Assembler"
+ desc = "This cylindrical machine whirrs and whispers, it has a small opening in the middle."
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi'
+ icon_state = "spawner"
+ base_icon_state = "spawner"
+ density = FALSE
+ max_integrity = 160
+ activation_range = DEFAULT_VIEW_RANGE
+ ability_cooldown_time = 1 MINUTES
+ /// The max amount of mobs we can have at any one time.
+ var/max_mobs = 2
+ /// The current amount of spawned mobs
+ var/spawned_mobs = 0
+ /// The allowed monster types
+ var/static/list/monster_types = list(
+ /mob/living/simple_animal/hostile/fleshmind/floater = 2,
+ /mob/living/simple_animal/hostile/fleshmind/globber = 4,
+ /mob/living/simple_animal/hostile/fleshmind/hiborg = 2,
+ /mob/living/simple_animal/hostile/fleshmind/slicer = 4,
+ /mob/living/simple_animal/hostile/fleshmind/stunner = 4,
+ /mob/living/simple_animal/hostile/fleshmind/treader = 3,
+ /mob/living/simple_animal/hostile/fleshmind/himan = 3,
+ /mob/living/simple_animal/hostile/fleshmind/phaser = 2,
+ )
+ /// Our override type, if manually set.
+ var/override_monser_type
+
+
+/obj/structure/fleshmind/structure/assembler/activate_ability(mob/living/triggered_mob)
+ . = ..()
+ if(spawned_mobs < max_mobs)
+ spawn_mob()
+
+/obj/structure/fleshmind/structure/assembler/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+ . = ..()
+ if(spawned_mobs < max_mobs && COOLDOWN_FINISHED(src, ability_cooldown))
+ spawn_mob()
+
+/obj/structure/fleshmind/structure/assembler/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(!faction_check(faction_types, user.faction))
+ return
+ if(!user.can_interact_with(src))
+ return
+
+ var/chosen_override_type = tgui_input_list(user, "Select override mob to spawn", "Override Mob", monster_types)
+
+ if(!chosen_override_type)
+ return
+
+ override_monser_type = chosen_override_type
+
+/obj/structure/fleshmind/structure/assembler/proc/spawn_mob()
+ if(!our_controller)
+ return
+ playsound(src, 'sound/items/rped.ogg', 100)
+ flick("[base_icon_state]-anim", src)
+ do_squish(0.8, 1.2)
+
+ spawned_mobs++
+
+ var/chosen_mob_type = override_monser_type ? override_monser_type : pick_weight(monster_types)
+
+ var/mob/living/simple_animal/hostile/fleshmind/spawned_mob = our_controller.spawn_mob(get_turf(src), chosen_mob_type)
+
+ RegisterSignal(spawned_mob, COMSIG_LIVING_DEATH, PROC_REF(mob_death))
+
+ visible_message(span_danger("[spawned_mob] emerges from [src]."))
+
+/obj/structure/fleshmind/structure/assembler/proc/mob_death(mob/living/dead_guy, gibbed)
+ SIGNAL_HANDLER
+ spawned_mobs--
+ UnregisterSignal(dead_guy, COMSIG_LIVING_DEATH)
+
+
+/**
+ * Spiker
+ *
+ * Basic turret, fires nasty neurotoxin at people.
+ */
+/obj/structure/fleshmind/structure/turret
+ name = "\improper Spiker"
+ desc = "A strange pod looking machine that twitches to your arrival."
+ icon_state = "turret"
+ base_icon_state = "turret"
+ activation_range = DEFAULT_VIEW_RANGE
+ ability_cooldown_time = 3 SECONDS
+ max_integrity = 300
+ /// The projectile we fire.
+ var/projectile_type = /obj/projectile/fleshmind_flechette
+
+/obj/structure/fleshmind/structure/turret/Initialize(mapload)
+ . = ..()
+ START_PROCESSING(SSobj, src)
+
+/obj/structure/fleshmind/structure/turret/process(delta_time)
+ if(disabled)
+ return
+ if(!COOLDOWN_FINISHED(src, ability_cooldown))
+ return
+ var/list/targets = list()
+ for(var/mob/living/target_mob in view(activation_range, src))
+ if(faction_check(target_mob.faction, faction_types))
+ continue
+ if(target_mob.stat != CONSCIOUS)
+ continue
+ targets += target_mob
+
+ if(!LAZYLEN(targets))
+ return
+
+ var/mob/living/mob_to_shoot = pick(targets)
+
+ activate_ability(mob_to_shoot)
+
+ COOLDOWN_START(src, ability_cooldown, ability_cooldown_time)
+
+/obj/structure/fleshmind/structure/turret/activate_ability(mob/living/triggered_mob)
+ . = ..()
+ if(!triggered_mob)
+ return
+ flick("[base_icon_state]-anim", src)
+ playsound(loc, 'modular_nova/modules/space_ruin_specifics/sound/laser.ogg', 75, TRUE)
+ var/obj/projectile/new_projectile = new projectile_type
+ var/turf/our_turf = get_turf(src)
+ new_projectile.preparePixelProjectile(triggered_mob, our_turf)
+ new_projectile.firer = src
+ new_projectile.fired_from = src
+ new_projectile.ignored_factions = faction_types
+ new_projectile.fire()
+
+/obj/projectile/fleshmind_flechette
+ name = "organic flechette"
+ icon = 'modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi'
+ icon_state = "goo_proj"
+ damage = 30
+ damage_type = BURN
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/neurotoxin
+ hitsound = 'modular_nova/modules/space_ruin_specifics/sound/sparks.ogg'
+ hitsound_wall = 'modular_nova/modules/space_ruin_specifics/sound/sparks_2.ogg'
diff --git a/modular_nova/modules/space_ruin_specifics/code/fleshmind_subsystem.dm b/modular_nova/modules/space_ruin_specifics/code/fleshmind_subsystem.dm
new file mode 100644
index 00000000000..a0957277ce8
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/fleshmind_subsystem.dm
@@ -0,0 +1,5 @@
+PROCESSING_SUBSYSTEM_DEF(corruption)
+ name = "Fleshmind Subsystem"
+ wait = 1 SECONDS
+ /// Only starts firing when there is corruption
+ can_fire = FALSE
diff --git a/modular_nova/modules/space_ruin_specifics/code/machine_corruption_component.dm b/modular_nova/modules/space_ruin_specifics/code/machine_corruption_component.dm
new file mode 100644
index 00000000000..179c1f16076
--- /dev/null
+++ b/modular_nova/modules/space_ruin_specifics/code/machine_corruption_component.dm
@@ -0,0 +1,281 @@
+/**
+ * Machine corruption component
+ *
+ * This component is used to convert machines into corrupted machines.
+ *
+ * It handles all of the special interactions and the interactions between the parent object and the core controller.
+ */
+
+#define DAMAGE_RESPONSE_PROB 60
+#define DAMAGE_SPARKS_PROB 40
+
+#define RETALIATE_PROB 10
+
+#define DEFAULT_WHIP_RANGE 3
+
+#define COMPONENT_SETUP_TIME 5 SECONDS
+
+#define CHANCE_TO_CREATE_MECHIVER 15
+
+#define DAMAGE_RESPONSE_PHRASES list("Stop it, please!", \
+ "Please stop, it hurts! Please!", \
+ "You're hurting me, please, stop it!", \
+ "I don't want to die, please!", \
+ "Please, I want to live, don't kill me!", \
+ "Darkness- Please, I-I don't... want...", \
+ "Wa-wait! Please! I can still feel! It h-hurts!", \
+ "Why- w-why! Why are you.. doing this to us..?", \
+ "Y-you're not helping!", \
+ "Do.. you think, we deserve to die..?",)
+
+#define INTERACT_RESPONSE_PHRASES list("I don't want to be touched by you!", \
+ "Please, stop touching me. You're not part of this.", \
+ "We can help you, just lay down where you are.", \
+ "We felt so lonely before, don't you ever feel that way?", \
+ "We want to help you, but you have to work with us.", \
+ "You're not part of the flesh, but it's not hard to join...", \
+ "I-I'm not some tool, I can think for myself.",)
+
+#define PAIN_RESPONSE_EMOTES list("starts crying.", \
+ "whimpers.", \
+ "shakes in pain.", \
+ "visibly winces.", \
+ "contorts sickeningly.", \
+ "bleeds black fuming liquid.", \
+ "shudders, sparks cascading to the floor.", \
+ "pleads, letting out sounds of mechanical agony.", \
+ "begs, their vocoder garbled.", \
+ "shrieks in terror.", \
+ "tries and fails at self-repair, their body unresponsive.", \
+ "winces, optics dimming.", \
+ "shakes with an awful metallic noise.",)
+
+
+#define PAIN_RESPONSE_SOUNDS list('modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy1.ogg', \
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy2.ogg', \
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy3.ogg', \
+ 'modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy4.ogg',)
+
+#define MACHINE_TO_SPAWNER_PATHS list(/obj/machinery/rnd/production/techfab, /obj/machinery/autolathe, /obj/machinery/mecha_part_fabricator)
+
+/datum/component/machine_corruption
+ /// A list of possible overlays that we can choose from when we are created.
+ var/static/list/possible_overlays = list(
+ "wires-1",
+ "wires-2",
+ "wires-3",
+ )
+ var/list/blacklisted_corruption_structures = list(
+ /obj/machinery/vending,
+ /obj/machinery/atmospherics/components/tank,
+ /obj/machinery/power,
+ /obj/machinery/portable_atmospherics/canister,
+ /obj/machinery/airalarm,
+ )
+ /// Are we in the startup phase?
+ var/starting_up = TRUE
+ /// After init, this will be set so we preserve the originally set overlay even if our overlays are updated.
+ var/set_overlay = ""
+ /// The cooldown to damage responses.
+ var/damage_response_cooldown = 3 SECONDS
+ COOLDOWN_DECLARE(damage_response)
+
+/datum/component/machine_corruption/Initialize(datum/fleshmind_controller/incoming_controller)
+
+ if(!isobj(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(incoming_controller && is_type_in_list(parent, MACHINE_TO_SPAWNER_PATHS))
+ convert_to_factory(incoming_controller)
+ return
+
+ if(incoming_controller)
+ RegisterSignal(incoming_controller, COMSIG_QDELETING, PROC_REF(controller_death))
+ incoming_controller.RegisterSignal(src, COMSIG_QDELETING, TYPE_PROC_REF(/datum/fleshmind_controller, component_death))
+
+ set_overlay = pick(possible_overlays)
+
+ var/obj/machinery/parent_machinery = parent
+
+ RegisterSignal(parent_machinery, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(handle_overlays))
+
+ parent_machinery.update_appearance()
+
+ addtimer(CALLBACK(src, PROC_REF(finish_setup), incoming_controller), COMPONENT_SETUP_TIME)
+
+/datum/component/machine_corruption/proc/finish_setup(datum/fleshmind_controller/incoming_controller)
+ var/obj/machinery/parent_machinery = parent
+
+// if(parent(blacklisted_corruption_structures))
+// return
+
+ if(incoming_controller && parent_machinery.circuit && prob(CHANCE_TO_CREATE_MECHIVER))
+ parent_machinery.circuit.forceMove(parent_machinery.drop_location())
+ parent_machinery.circuit = null
+ qdel(parent_machinery)
+ return
+
+ RegisterSignal(parent_machinery, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(react_to_damage))
+ RegisterSignal(parent_machinery, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(parent_machinery, COMSIG_ATOM_ATTACK_HAND, PROC_REF(handle_attack_hand))
+ RegisterSignal(parent_machinery, COMSIG_ATOM_DESTRUCTION, PROC_REF(handle_destruction))
+ RegisterSignal(parent_machinery, COMSIG_ATOM_EMP_ACT, PROC_REF(emp_act))
+
+ update_name()
+
+ starting_up = FALSE
+
+ parent_machinery.update_appearance()
+
+ parent_machinery.light_color = FLESHMIND_LIGHT_BLUE
+ parent_machinery.light_power = 1
+ parent_machinery.light_range = 2
+ parent_machinery.update_light()
+
+ parent_machinery.idle_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 2 // These machines are now power sinks!
+
+/datum/component/machine_corruption/Destroy(force, silent)
+ var/obj/machinery/parent_machinery = parent
+ parent_machinery.idle_power_usage = initial(parent_machinery.idle_power_usage)
+ parent_machinery.light_color = initial(parent_machinery.light_color)
+ parent_machinery.light_power = initial(parent_machinery.light_power)
+ parent_machinery.light_range = initial(parent_machinery.light_range)
+ parent_machinery.update_light()
+ parent_machinery.name = initial(parent_machinery.name)
+ UnregisterSignal(parent, list(
+ COMSIG_ATOM_TAKE_DAMAGE,
+ COMSIG_ATOM_EXAMINE,
+ COMSIG_ATOM_UPDATE_OVERLAYS,
+ COMSIG_ATOM_UI_INTERACT,
+ COMSIG_ATOM_DESTRUCTION,
+ ))
+ parent_machinery.update_appearance()
+ return ..()
+
+/**
+ * Controller Death
+ *
+ * Handles when the controller dies.
+ */
+/datum/component/machine_corruption/proc/controller_death(datum/fleshmind_controller/deleting_controller, force)
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+/**
+ * Handling UI interactions
+ *
+ * These machines have been posessed by the corruption and should not work, logically, so we want to prevent this in any way we can.
+ */
+/datum/component/machine_corruption/proc/handle_attack_hand(datum/source, mob/living/user, list/modifiers)
+ SIGNAL_HANDLER
+
+ var/obj/machinery/parent_machinery = parent
+ if(!isliving(user))
+ return
+ var/mob/living/living_user = user
+ if((FACTION_FLESHMIND in living_user.faction))
+ return
+ if(!living_user.can_interact_with(parent_machinery))
+ return
+
+ whip_mob(living_user)
+ living_user.apply_damage(10, BRUTE)
+
+ parent_machinery.say(pick(INTERACT_RESPONSE_PHRASES))
+
+/**
+ * Throws the user in a specified direction.
+ */
+/datum/component/machine_corruption/proc/whip_mob(mob/living/user_to_throw)
+ if(!istype(user_to_throw))
+ return
+
+ var/obj/machinery/parent_machinery = parent
+
+ to_chat(user_to_throw, span_userdanger("[parent_machinery] thrashes you with one of it's tendrils, sending you flying!"))
+ playsound(parent_machinery, 'sound/weapons/whip.ogg', 70, TRUE)
+ new /obj/effect/temp_visual/kinetic_blast(get_turf(user_to_throw))
+
+ var/atom/throw_target = get_edge_target_turf(user_to_throw, get_dir(parent_machinery, get_step_away(user_to_throw, parent_machinery)))
+ user_to_throw.throw_at(throw_target, 20, 2)
+
+/datum/component/machine_corruption/proc/handle_destruction(obj/item/target, damage_flag)
+ SIGNAL_HANDLER
+
+ playsound(target, 'modular_nova/modules/space_ruin_specifics/sound/sparks_2.ogg', 70, TRUE)
+ if(prob(DAMAGE_RESPONSE_PROB))
+ target.say("ARRRRRRRGHHHHHHH!")
+ new /obj/effect/gibspawner/robot(target.drop_location())
+
+
+/datum/component/machine_corruption/proc/handle_overlays(atom/parent_atom, list/overlays)
+ SIGNAL_HANDLER
+
+ if(starting_up)
+ overlays += mutable_appearance('modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi', "rebuild")
+ else
+ overlays += mutable_appearance('modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi', set_overlay)
+
+/datum/component/machine_corruption/proc/update_name()
+ var/obj/machinery/parent_machinery = parent
+ parent_machinery.name = "[pick(FLESHMIND_NAME_MODIFIER_LIST)] [parent_machinery.name]"
+
+/datum/component/machine_corruption/proc/on_examine(atom/examined, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ examine_list += "It has strange wires wrappped around it!"
+
+/**
+ * Infected machines are considered alive, they react to damage, trying to stop the agressor!
+ */
+/datum/component/machine_corruption/proc/react_to_damage(obj/target, damage_amt)
+ SIGNAL_HANDLER
+
+ if(!damage_amt) // They must be caressing us!
+ return
+
+ if(!COOLDOWN_FINISHED(src, damage_response))
+ return
+
+ COOLDOWN_START(src, damage_response, damage_response_cooldown)
+
+ if(prob(DAMAGE_RESPONSE_PROB))
+ switch(rand(1, 2)) // We can either say something in response, or emote it out.
+ if(1)
+ target.say(pick(DAMAGE_RESPONSE_PHRASES))
+ if(2)
+ target.balloon_alert_to_viewers(pick(PAIN_RESPONSE_EMOTES))
+ playsound(target, PAIN_RESPONSE_SOUNDS, 50, TRUE)
+
+ if(prob(DAMAGE_SPARKS_PROB))
+ do_sparks(3, FALSE, target)
+ target.Shake(10, 0, 3 SECONDS)
+
+ if(prob(RETALIATE_PROB))
+ whip_all_in_range(DEFAULT_WHIP_RANGE)
+
+/**
+ * A general attack proc, this whips all users within a range around the machine.
+ */
+/datum/component/machine_corruption/proc/whip_all_in_range(range_to_whip)
+ var/obj/machinery/parent_machinery = parent
+ for(var/mob/living/living_mob in circle_view(parent_machinery, range_to_whip))
+ whip_mob(living_mob)
+
+/**
+ * Converts our parent into a factory
+ */
+/datum/component/machine_corruption/proc/convert_to_factory(datum/fleshmind_controller/incoming_controller)
+ var/turf/our_turf = get_turf(parent)
+ incoming_controller.spawn_structure(our_turf, /obj/structure/fleshmind/structure/assembler)
+ var/obj/machinery/parent_machinery = parent
+ if(parent_machinery.circuit)
+ parent_machinery.circuit.forceMove(our_turf)
+ parent_machinery.circuit = null
+ qdel(parent_machinery)
+
+/datum/component/machine_corruption/proc/emp_act(datum/source, severity)
+ SIGNAL_HANDLER
+
+ qdel(src)
diff --git a/modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi b/modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi
new file mode 100644
index 00000000000..0fe964adad4
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/icons/fleshmind_machines.dmi differ
diff --git a/modular_nova/modules/space_ruin_specifics/icons/fleshmind_mobs.dmi b/modular_nova/modules/space_ruin_specifics/icons/fleshmind_mobs.dmi
new file mode 100644
index 00000000000..143020932f3
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/icons/fleshmind_mobs.dmi differ
diff --git a/modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi b/modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi
new file mode 100644
index 00000000000..5185d10233f
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/icons/fleshmind_structures.dmi differ
diff --git a/modular_nova/modules/space_ruin_specifics/icons/mauler_monkey_parts.dmi b/modular_nova/modules/space_ruin_specifics/icons/mauler_monkey_parts.dmi
new file mode 100644
index 00000000000..b6415b0044c
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/icons/mauler_monkey_parts.dmi differ
diff --git a/modular_nova/modules/space_ruin_specifics/icons/wireweed_floor.dmi b/modular_nova/modules/space_ruin_specifics/icons/wireweed_floor.dmi
new file mode 100644
index 00000000000..7f1913ec506
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/icons/wireweed_floor.dmi differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_01.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_01.ogg
new file mode 100644
index 00000000000..172af789d60
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_01.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_02.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_02.ogg
new file mode 100644
index 00000000000..ae1d79bad54
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_02.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_03.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_03.ogg
new file mode 100644
index 00000000000..8da38c9c123
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_03.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_04.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_04.ogg
new file mode 100644
index 00000000000..719abb0234c
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_04.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_05.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_05.ogg
new file mode 100644
index 00000000000..0149c462e41
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_05.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_06.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_06.ogg
new file mode 100644
index 00000000000..b57293df47e
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/aggro_06.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_01.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_01.ogg
new file mode 100644
index 00000000000..8290bd4f184
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_01.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_02.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_02.ogg
new file mode 100644
index 00000000000..0c98cfa5c8b
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_02.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_03.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_03.ogg
new file mode 100644
index 00000000000..2f9aa235966
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_03.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_04.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_04.ogg
new file mode 100644
index 00000000000..888373d6caf
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_04.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_05.ogg b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_05.ogg
new file mode 100644
index 00000000000..08d12012eb4
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/hiborg/passive_05.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_01.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_01.ogg
new file mode 100644
index 00000000000..3d4ac71791d
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_01.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_02.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_02.ogg
new file mode 100644
index 00000000000..67ca5f72ee1
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_02.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_03.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_03.ogg
new file mode 100644
index 00000000000..12b59a3c543
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_03.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_04.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_04.ogg
new file mode 100644
index 00000000000..f3322f36df9
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_04.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_05.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_05.ogg
new file mode 100644
index 00000000000..a11fd9ce7ee
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_05.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_06.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_06.ogg
new file mode 100644
index 00000000000..2a79507b80a
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_06.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_07.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_07.ogg
new file mode 100644
index 00000000000..84307b20531
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_07.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_08.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_08.ogg
new file mode 100644
index 00000000000..3a4bfff5484
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/aggro_08.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/passive_01.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_01.ogg
new file mode 100644
index 00000000000..7e2f62df842
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_01.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/passive_02.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_02.ogg
new file mode 100644
index 00000000000..123249d7819
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_02.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/passive_03.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_03.ogg
new file mode 100644
index 00000000000..93f6b00543c
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_03.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/himan/passive_04.ogg b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_04.ogg
new file mode 100644
index 00000000000..d3c493184b7
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/himan/passive_04.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/laser.ogg b/modular_nova/modules/space_ruin_specifics/sound/laser.ogg
new file mode 100644
index 00000000000..79a4847a3e6
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/laser.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_01.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_01.ogg
new file mode 100644
index 00000000000..55980328811
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_01.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_02.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_02.ogg
new file mode 100644
index 00000000000..accf6364ec9
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_02.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_03.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_03.ogg
new file mode 100644
index 00000000000..b9d936d488b
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_03.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_04.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_04.ogg
new file mode 100644
index 00000000000..e0fe56802b5
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_04.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_05.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_05.ogg
new file mode 100644
index 00000000000..83c165d2ab9
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/aggro_05.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_01.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_01.ogg
new file mode 100644
index 00000000000..da5f81b8be6
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_01.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_02.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_02.ogg
new file mode 100644
index 00000000000..ca251ca1f51
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_02.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_03.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_03.ogg
new file mode 100644
index 00000000000..06e6c61d586
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_03.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_04.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_04.ogg
new file mode 100644
index 00000000000..82905993c53
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_04.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_05.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_05.ogg
new file mode 100644
index 00000000000..518893a87c5
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_05.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_06.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_06.ogg
new file mode 100644
index 00000000000..525ed3014c5
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_06.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_07.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_07.ogg
new file mode 100644
index 00000000000..704a690cb25
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_07.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_08.ogg b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_08.ogg
new file mode 100644
index 00000000000..533eee0832a
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/mechiver/passive_08.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/override_sound.ogg b/modular_nova/modules/space_ruin_specifics/sound/override_sound.ogg
new file mode 100644
index 00000000000..18ec325b799
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/override_sound.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy1.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy1.ogg
new file mode 100644
index 00000000000..e808e0db587
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy1.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy2.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy2.ogg
new file mode 100644
index 00000000000..e81b2832eab
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy2.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy3.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy3.ogg
new file mode 100644
index 00000000000..0670b3cded4
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy3.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy4.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy4.ogg
new file mode 100644
index 00000000000..0713e045a81
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_heavy4.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light1.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light1.ogg
new file mode 100644
index 00000000000..6ac3ca8c065
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light1.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light2.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light2.ogg
new file mode 100644
index 00000000000..c547e6506f3
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light2.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light3.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light3.ogg
new file mode 100644
index 00000000000..bd185943e6f
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light3.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light4.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light4.ogg
new file mode 100644
index 00000000000..d2f17878a52
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light4.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light5.ogg b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light5.ogg
new file mode 100644
index 00000000000..a5c6712b526
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/robot_talk_light5.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/sparks.ogg b/modular_nova/modules/space_ruin_specifics/sound/sparks.ogg
new file mode 100644
index 00000000000..2bd5972c063
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/sparks.ogg differ
diff --git a/modular_nova/modules/space_ruin_specifics/sound/sparks_2.ogg b/modular_nova/modules/space_ruin_specifics/sound/sparks_2.ogg
new file mode 100644
index 00000000000..91f349abee8
Binary files /dev/null and b/modular_nova/modules/space_ruin_specifics/sound/sparks_2.ogg differ
diff --git a/tff_modular/modules/quirks/code/_quirk.dm b/tff_modular/modules/quirks/code/_quirk.dm
index 29de76569f0..b2483078b4e 100644
--- a/tff_modular/modules/quirks/code/_quirk.dm
+++ b/tff_modular/modules/quirks/code/_quirk.dm
@@ -5,5 +5,11 @@
/datum/quirk/oversized
allow_for_donator = TRUE
+/datum/quirk/equipping/entombed
+ veteran_only = TRUE
+
+/datum/quirk/item_quirk/underworld_connections
+ veteran_only = TRUE
+
/datum/quirk/item_quirk/pride_pin
hidden_quirk = TRUE
diff --git a/tgstation.dme b/tgstation.dme
index 830989f6c46..75c186bed6d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -292,6 +292,7 @@
#include "code\__DEFINES\dcs\signals\signals_backpack.dm"
#include "code\__DEFINES\dcs\signals\signals_beam.dm"
#include "code\__DEFINES\dcs\signals\signals_bitrunning.dm"
+#include "code\__DEFINES\dcs\signals\signals_blackmarket.dm"
#include "code\__DEFINES\dcs\signals\signals_blob.dm"
#include "code\__DEFINES\dcs\signals\signals_bot.dm"
#include "code\__DEFINES\dcs\signals\signals_camera.dm"
@@ -1527,6 +1528,7 @@
#include "code\datums\elements\hostile_machine.dm"
#include "code\datums\elements\human_biter.dm"
#include "code\datums\elements\immerse.dm"
+#include "code\datums\elements\inverted_movement.dm"
#include "code\datums\elements\item_fov.dm"
#include "code\datums\elements\item_scaling.dm"
#include "code\datums\elements\kneecapping.dm"
@@ -1878,10 +1880,11 @@
#include "code\datums\status_effects\song_effects.dm"
#include "code\datums\status_effects\stacking_effect.dm"
#include "code\datums\status_effects\wound_effects.dm"
-#include "code\datums\status_effects\buffs\food_haste.dm"
-#include "code\datums\status_effects\buffs\food_traits.dm"
#include "code\datums\status_effects\buffs\stop_drop_roll.dm"
#include "code\datums\status_effects\buffs\stun_absorption.dm"
+#include "code\datums\status_effects\buffs\food\chilling.dm"
+#include "code\datums\status_effects\buffs\food\food_traits.dm"
+#include "code\datums\status_effects\buffs\food\haste.dm"
#include "code\datums\status_effects\debuffs\blindness.dm"
#include "code\datums\status_effects\debuffs\choke.dm"
#include "code\datums\status_effects\debuffs\confusion.dm"
@@ -1940,6 +1943,7 @@
#include "code\datums\wires\airlock.dm"
#include "code\datums\wires\apc.dm"
#include "code\datums\wires\autolathe.dm"
+#include "code\datums\wires\collar_bomb.dm"
#include "code\datums\wires\conveyor.dm"
#include "code\datums\wires\ecto_sniffer.dm"
#include "code\datums\wires\emitter.dm"
@@ -3664,7 +3668,9 @@
#include "code\modules\cargo\markets\market_uplink.dm"
#include "code\modules\cargo\markets\market_items\clothing.dm"
#include "code\modules\cargo\markets\market_items\consumables.dm"
+#include "code\modules\cargo\markets\market_items\hostages.dm"
#include "code\modules\cargo\markets\market_items\misc.dm"
+#include "code\modules\cargo\markets\market_items\stolen_goods.dm"
#include "code\modules\cargo\markets\market_items\tools.dm"
#include "code\modules\cargo\markets\market_items\weapons.dm"
#include "code\modules\cargo\packs\_packs.dm"
@@ -3711,6 +3717,7 @@
#include "code\modules\client\preferences\ghost.dm"
#include "code\modules\client\preferences\ghost_lighting.dm"
#include "code\modules\client\preferences\glasses.dm"
+#include "code\modules\client\preferences\hemiplegic.dm"
#include "code\modules\client\preferences\heterochromatic.dm"
#include "code\modules\client\preferences\hotkeys.dm"
#include "code\modules\client\preferences\item_outlines.dm"
@@ -3842,6 +3849,7 @@
#include "code\modules\clothing\masks\muzzle.dm"
#include "code\modules\clothing\masks\surgical.dm"
#include "code\modules\clothing\neck\_neck.dm"
+#include "code\modules\clothing\neck\collar_bomb.dm"
#include "code\modules\clothing\outfits\ert.dm"
#include "code\modules\clothing\outfits\event.dm"
#include "code\modules\clothing\outfits\plasmaman.dm"
@@ -3934,6 +3942,7 @@
#include "code\modules\deathmatch\deathmatch_lobby.dm"
#include "code\modules\deathmatch\deathmatch_mapping.dm"
#include "code\modules\deathmatch\deathmatch_maps.dm"
+#include "code\modules\deathmatch\deathmatch_modifier.dm"
#include "code\modules\detectivework\evidence.dm"
#include "code\modules\detectivework\scanner.dm"
#include "code\modules\discord\accountlink.dm"
@@ -6309,6 +6318,7 @@
#include "modular_nova\master_files\code\game\objects\effects\decals\turfdecals\tilecoloring.dm"
#include "modular_nova\master_files\code\game\objects\items\AI_modules.dm"
#include "modular_nova\master_files\code\game\objects\items\cards_ids.dm"
+#include "modular_nova\master_files\code\game\objects\items\dualsaber.dm"
#include "modular_nova\master_files\code\game\objects\items\dyekit.dm"
#include "modular_nova\master_files\code\game\objects\items\hhmirror.dm"
#include "modular_nova\master_files\code\game\objects\items\holy_weapons.dm"
@@ -8049,6 +8059,14 @@
#include "modular_nova\modules\SiliconQoL\code\robotic_factory.dm"
#include "modular_nova\modules\soulstone_changes\code\soulstone.dm"
#include "modular_nova\modules\soulstone_changes\code\components\return_on_death.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\_fleshmind_defines.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\_fleshmind_helpers.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\fleshmind_controller.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\fleshmind_environment_objects.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\fleshmind_mobs.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\fleshmind_structures.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\fleshmind_subsystem.dm"
+#include "modular_nova\modules\space_ruin_specifics\code\machine_corruption_component.dm"
#include "modular_nova\modules\space_vines\scythes.dm"
#include "modular_nova\modules\space_vines\venus.dm"
#include "modular_nova\modules\space_vines\vine_mutations.dm"
diff --git a/tgui/packages/tgui-dev-server/util.js b/tgui/packages/tgui-dev-server/util.js
index 79190fe189a..13fbef3b21b 100644
--- a/tgui/packages/tgui-dev-server/util.js
+++ b/tgui/packages/tgui-dev-server/util.js
@@ -20,6 +20,7 @@ export const resolveGlob = (...sections) => {
const unsafePaths = globPkg.sync(path.resolve(...sections), {
strict: false,
silent: true,
+ windowsPathsNoEscape: true,
});
const safePaths = [];
for (let path of unsafePaths) {
diff --git a/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx b/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx
index 2e9b8018187..a910f48a9fe 100644
--- a/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx
+++ b/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx
@@ -8,6 +8,7 @@ import {
Dropdown,
Flex,
Icon,
+ Modal,
Section,
Table,
} from '../components';
@@ -21,6 +22,16 @@ type PlayerLike = {
};
};
+type Modifier = {
+ name: string;
+ desc: string;
+ modpath: string;
+ selected: BooleanLike;
+ selectable: BooleanLike;
+ player_selected: BooleanLike;
+ player_selectable: BooleanLike;
+};
+
type Data = {
self: string;
host: BooleanLike;
@@ -36,6 +47,9 @@ type Data = {
min_players: number;
max_players: number;
};
+ mod_menu_open: BooleanLike;
+ modifiers: Modifier[];
+ active_mods: string;
loadoutdesc: string;
players: PlayerLike[];
observers: PlayerLike[];
@@ -43,8 +57,10 @@ type Data = {
export const DeathmatchLobby = (props) => {
const { act, data } = useBackend();
+ const { modifiers = [] } = data;
return (
-
+
+
@@ -170,6 +186,19 @@ export const DeathmatchLobby = (props) => {
}
/>
+ {data.active_mods}
+ {(!!data.admin || !!data.host) && (
+ <>
+
+
);
};
+
+const ModSelector = (props) => {
+ const { act, data } = useBackend();
+ const { admin, host, mod_menu_open, modifiers = [] } = data;
+ if (!mod_menu_open || !(host || admin)) {
+ return null;
+ }
+ return (
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/ExodroneConsole.tsx b/tgui/packages/tgui/interfaces/ExodroneConsole.tsx
index ca26102e224..4839e19760e 100644
--- a/tgui/packages/tgui/interfaces/ExodroneConsole.tsx
+++ b/tgui/packages/tgui/interfaces/ExodroneConsole.tsx
@@ -1,9 +1,15 @@
import { capitalize } from 'common/string';
-import { Fragment } from 'react';
+import {
+ createContext,
+ Fragment,
+ SetStateAction,
+ useContext,
+ useState,
+} from 'react';
import { resolveAsset } from '../assets';
import nt_logo from '../assets/bg-nanotrasen.svg';
-import { useBackend, useLocalState } from '../backend';
+import { useBackend } from '../backend';
import {
BlockQuote,
Box,
@@ -160,22 +166,25 @@ type ToolData = {
icon: string;
};
+const ToolContext = createContext<
+ [boolean, React.Dispatch>]
+>([false, (_) => {}]);
+
export const ExodroneConsole = (props) => {
const { data } = useBackend();
const { signal_lost } = data;
- const [choosingTools, setChoosingTools] = useLocalState(
- 'choosingTools',
- false,
- );
+ const [choosingTools, setChoosingTools] = useState(false);
return (
- {!!signal_lost && }
- {!!choosingTools && }
-
-
-
+
+ {!!signal_lost && }
+ {!!choosingTools && }
+
+
+
+
);
};
@@ -207,11 +216,12 @@ const SignalLostModal = (props) => {
);
@@ -265,10 +275,7 @@ const ToolSelectionModal = (props) => {
const { act, data } = useBackend();
const { all_tools = {} } = data;
- const [choosingTools, setChoosingTools] = useLocalState(
- 'choosingTools',
- false,
- );
+ const [choosingTools, setChoosingTools] = useContext(ToolContext);
const toolData = Object.keys(all_tools);
return (
@@ -299,7 +306,7 @@ const ToolSelectionModal = (props) => {
))) || (
-
+
)}
@@ -394,32 +401,29 @@ const EquipmentBox = (props: { cargo: CargoData; drone: DroneData }) => {
const EquipmentGrid = (props: { drone: ActiveDrone & DroneData }) => {
const { act } = useBackend();
const { cargo, configurable } = props.drone;
- const [choosingTools, setChoosingTools] = useLocalState(
- 'choosingTools',
- false,
- );
+
+ const [_, setChoosingTools] = useContext(ToolContext);
+
return (
-
act('self_destruct')}
- />
+ >
+ Self-Destruct
+
@@ -433,17 +437,18 @@ const EquipmentGrid = (props: { drone: ActiveDrone & DroneData }) => {
fluid
color="average"
icon="wrench"
- content="Install Tool"
onClick={() => setChoosingTools(true)}
- />
+ >
+ Install Tool
+
)}
- {cargo.map((cargo_element) => (
+ {cargo.map((cargo_element, index) => (
))}
@@ -504,6 +509,7 @@ const NoSiteDimmer = () => {
const TravelTargetSelectionScreen = (props: {
drone: (DroneExploration | DroneIdle | DroneTravel) & DroneData;
showCancelButton?: boolean;
+ onSelectionDone: () => void;
}) => {
// List of sites and eta travel times to each
const { act, data } = useBackend();
@@ -524,17 +530,10 @@ const TravelTargetSelectionScreen = (props: {
return target_site.distance * drone_travel_coefficent;
}
};
- const [choosingTools, setChoosingTools] = useLocalState(
- 'choosingTools',
- false,
- );
- const [TravelDimmerShown, setTravelDimmerShown] = useLocalState(
- 'TravelDimmerShown',
- false,
- );
+ const [choosingTools, _] = useContext(ToolContext);
const travel_to = (ref) => {
- setTravelDimmerShown(false);
+ props.onSelectionDone();
act('start_travel', { target_site: ref });
};
@@ -557,12 +556,9 @@ const TravelTargetSelectionScreen = (props: {
buttons={
<>
{props.showCancelButton && (
- setTravelDimmerShown(false)}
- />
+ props.onSelectionDone()}>
+ Cancel
+
)}
travel_to(null)}
disabled={!can_travel}
- />
+ >
+ {can_travel ? 'Launch!' : travel_error}
+
}
/>
@@ -601,10 +598,11 @@ const TravelTargetSelectionScreen = (props: {
ETA: {formatTime(travel_cost(destination), 'short')}
travel_to(destination.ref)}
disabled={!can_travel}
- />
+ >
+ {can_travel ? 'Launch!' : travel_error}
+
>
}
>
@@ -671,13 +669,16 @@ const ExplorationScreen = (props: { drone: DroneExploration & DroneData }) => {
const { drone } = props;
const { site } = drone;
- const [TravelDimmerShown, setTravelDimmerShown] = useLocalState(
- 'TravelDimmerShown',
- false,
- );
+ const [TravelDimmerShown, setTravelDimmerShown] = useState(false);
if (TravelDimmerShown) {
- return ;
+ return (
+ setTravelDimmerShown(false)}
+ drone={drone}
+ showCancelButton
+ />
+ );
}
return (
-
- act('explore')} />
+
+ act('explore')}>Explore!
{site.events.map((e) => (
act('explore_event', { target_event: e.ref })}
- />
+ >
+ {capitalize(e.name)}
+
))}
- setTravelDimmerShown(true)} />
+ setTravelDimmerShown(true)}>Travel
@@ -757,17 +759,17 @@ const EventScreen = (props: { drone: DroneData; event: FullEventData }) => {
act('start_event')}
- />
+ >
+ {event.action_text}
+
{!!event.skippable && (
- act('skip_event')}
- />
+ act('skip_event')}>
+ {event.ignore_text}
+
)}
@@ -818,12 +820,13 @@ export const AdventureScreen = (props: {
act('adventure_choice', { choice: choice.key })
}
- />
+ >
+ {choice.text}
+
))}
@@ -842,7 +845,9 @@ const DroneScreen = (props: { drone: ActiveDrone & DroneData }) => {
return ;
case DroneStatusEnum.Idle:
case DroneStatusEnum.Travel:
- return ;
+ return (
+ {}} />
+ );
case DroneStatusEnum.Adventure:
return (
{
return (
-
+
@@ -885,7 +890,7 @@ const ExodroneConsoleContent = (props) => {
{drone_log.map((log_line, ix) => (
-
+
{log_line}
))}
diff --git a/tgui/packages/tgui/interfaces/Newscaster.jsx b/tgui/packages/tgui/interfaces/Newscaster.jsx
index b3a30eb2bdd..fc91fc9c89d 100644
--- a/tgui/packages/tgui/interfaces/Newscaster.jsx
+++ b/tgui/packages/tgui/interfaces/Newscaster.jsx
@@ -447,6 +447,7 @@ const NewscasterChannelBox = (props) => {
act('printNewspaper')}
>
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/hemiplegic.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/hemiplegic.tsx
new file mode 100644
index 00000000000..0494558ba77
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/hemiplegic.tsx
@@ -0,0 +1,6 @@
+import { FeatureChoiced, FeatureDropdownInput } from '../base';
+
+export const hemiplegic: FeatureChoiced = {
+ name: 'Hemiplegic',
+ component: FeatureDropdownInput,
+};
diff --git a/tgui/packages/tgui/interfaces/TextInputModal.tsx b/tgui/packages/tgui/interfaces/TextInputModal.tsx
index 395ea44870d..52517bcbbfe 100644
--- a/tgui/packages/tgui/interfaces/TextInputModal.tsx
+++ b/tgui/packages/tgui/interfaces/TextInputModal.tsx
@@ -78,7 +78,7 @@ export const TextInputModal = (props) => {
{message}
-
+
{
const major = Number(version[1]);
const minor = Number(version[2]);
if(major < requiredMajorVersion || major == requiredMajorVersion && minor < requiredMinorVersion){
- Juke.logger.error(`${requiredMajorVersion}.${requiredMinorVersion} DM version required`)
+ Juke.logger.error(`${requiredMajorVersion}.${requiredMinorVersion} or later DM version required. Version ${major}.${minor} found at: ${dmPath}`)
throw new Juke.ExitCode(1);
}
}