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) && ( + <> + + ); @@ -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 && ( - )} travel_to(null)} disabled={!can_travel} - /> + > + {can_travel ? 'Launch!' : travel_error} + } /> @@ -601,10 +598,11 @@ const TravelTargetSelectionScreen = (props: { ETA: {formatTime(travel_cost(destination), 'short')} } > @@ -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 (
{ - - {site.events.map((e) => ( ))} -
@@ -757,17 +759,17 @@ const EventScreen = (props: { drone: DroneData; event: FullEventData }) => { {!!event.skippable && ( - )} @@ -818,12 +820,13 @@ export const AdventureScreen = (props: { ))} @@ -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) => {