diff --git a/_maps/RandomRuins/BeachRuins/beach_treasure_cove.dmm b/_maps/RandomRuins/BeachRuins/beach_treasure_cove.dmm index becd88ae56fd..997f6bb56703 100644 --- a/_maps/RandomRuins/BeachRuins/beach_treasure_cove.dmm +++ b/_maps/RandomRuins/BeachRuins/beach_treasure_cove.dmm @@ -393,7 +393,7 @@ pixel_x = 9; pixel_y = -1 }, -/obj/item/gun/ballistic/automatic/assault/p16/minutemen{ +/obj/item/gun/ballistic/automatic/assault/cm82{ pixel_y = 7; pixel_x = -9 }, diff --git a/_maps/RandomRuins/SandRuins/whitesands_surface_camp_combination.dmm b/_maps/RandomRuins/SandRuins/whitesands_surface_camp_combination.dmm index 117a109576c8..485303fb66c3 100644 --- a/_maps/RandomRuins/SandRuins/whitesands_surface_camp_combination.dmm +++ b/_maps/RandomRuins/SandRuins/whitesands_surface_camp_combination.dmm @@ -1066,7 +1066,7 @@ /turf/open/floor/wood, /area/ruin) "Ok" = ( -/turf/open/acid/whitesands, +/turf/open/water/acid/whitesands, /area/overmap_encounter/planetoid/sand/explored) "Ov" = ( /obj/item/clothing/head/cowboy{ diff --git a/_maps/configs/inteq_talos.json b/_maps/configs/inteq_talos.json index 54f134964470..858255729809 100644 --- a/_maps/configs/inteq_talos.json +++ b/_maps/configs/inteq_talos.json @@ -40,9 +40,13 @@ "outfit": "/datum/outfit/job/inteq/security", "slots": 1 }, + "Corpsman": { + "outfit": "/datum/outfit/job/inteq/paramedic", + "slots": 1 + }, "Recruit": { "outfit": "/datum/outfit/job/inteq/assistant", - "slots": 4 + "slots": 4 } }, "enabled": true diff --git a/_maps/configs/pgf_crying_sun.json b/_maps/configs/pgf_crying_sun.json index d4eb67b1fb50..23114c8aa274 100644 --- a/_maps/configs/pgf_crying_sun.json +++ b/_maps/configs/pgf_crying_sun.json @@ -21,7 +21,7 @@ "officer": true, "slots": 1 }, - "Bridge Crew": { + "Helmsman": { "outfit": "/datum/outfit/job/gezena/assistant/bridge", "slots": 1 }, @@ -37,7 +37,7 @@ "outfit": "/datum/outfit/job/gezena/assistant", "slots": 2 }, - "Marine Sergeant": { + "Marine Lieutenant": { "outfit": "/datum/outfit/job/gezena/hos", "slots": 1 }, diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 2f531a149ffe..de0cba96be2f 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -1084,11 +1084,11 @@ /area/centcom/control) "apm" = ( /obj/structure/rack, -/obj/item/gun/ballistic/automatic/marksman/gal/inteq{ +/obj/item/gun/ballistic/automatic/marksman/f4/inteq{ pixel_x = 1; pixel_y = 4 }, -/obj/item/gun/ballistic/automatic/marksman/gal/inteq{ +/obj/item/gun/ballistic/automatic/marksman/f4/inteq{ pixel_x = -4; pixel_y = -2 }, diff --git a/_maps/outpost/indie_space.dmm b/_maps/outpost/indie_space.dmm index fee7b6d66bd4..86a2a8d102b1 100644 --- a/_maps/outpost/indie_space.dmm +++ b/_maps/outpost/indie_space.dmm @@ -108,10 +108,25 @@ /turf/closed/indestructible/reinforced, /area/outpost/crew/library) "aM" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/prison_contraband, -/turf/open/floor/plasteel/patterned/cargo_one, -/area/outpost/vacant_rooms) +/obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/structure/table, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{ + dir = 8 + }, +/obj/effect/spawner/lootdrop/plushie{ + pixel_x = 16; + pixel_y = 4 + }, +/obj/machinery/computer/cryopod/directional/north, +/obj/item/folder{ + pixel_x = -5; + pixel_y = 2 + }, +/obj/structure/cable/yellow{ + icon_state = "2-4" + }, +/turf/open/floor/plasteel, +/area/outpost/crew/cryo) "aP" = ( /obj/structure/railing{ dir = 8 @@ -916,6 +931,7 @@ /obj/effect/turf_decal/corner/opaque/neutral{ dir = 1 }, +/obj/machinery/newscaster/directional/north, /turf/open/floor/plasteel, /area/outpost/hallway/port) "gq" = ( @@ -1463,8 +1479,16 @@ /area/outpost/maintenance/fore) "kh" = ( /obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{ + dir = 1 + }, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel, -/area/outpost/vacant_rooms) +/area/outpost/crew/cryo) "km" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/effect/decal/cleanable/confetti, @@ -1695,18 +1719,8 @@ /turf/open/floor/plating, /area/outpost/maintenance/fore) "lG" = ( -/obj/machinery/door/airlock/public, -/obj/effect/turf_decal/industrial/warning, -/obj/effect/turf_decal/industrial/warning{ - dir = 1 - }, -/obj/machinery/door/firedoor/border_only, -/obj/machinery/door/firedoor/border_only{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, -/turf/open/floor/plasteel/tech, -/area/outpost/vacant_rooms) +/turf/closed/indestructible/reinforced, +/area/outpost/crew/cryo) "lH" = ( /obj/structure/cable/yellow{ icon_state = "1-2" @@ -1722,6 +1736,15 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, /area/outpost/crew/library) +"lJ" = ( +/obj/effect/turf_decal/corner/opaque/grey/full, +/obj/effect/turf_decal/corner/opaque/neutral{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer2, +/turf/open/floor/plasteel, +/area/outpost/hallway/port) "lM" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/steeldecal/steel_decals9, @@ -2013,11 +2036,17 @@ /area/outpost/cargo) "nz" = ( /obj/effect/turf_decal/corner/opaque/neutral/diagonal, -/obj/structure/chair{ - dir = 1 +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{ + dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel, -/area/outpost/vacant_rooms) +/area/outpost/crew/cryo) "nK" = ( /obj/machinery/door/firedoor, /obj/effect/turf_decal/industrial/warning{ @@ -2473,6 +2502,11 @@ dir = 4 }, /obj/effect/decal/cleanable/dirt/dust, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/structure/cable/yellow{ + icon_state = "1-8" + }, /turf/open/floor/plasteel, /area/outpost/hallway/port) "qT" = ( @@ -2552,7 +2586,6 @@ /obj/effect/turf_decal/corner/opaque/neutral{ dir = 1 }, -/obj/structure/extinguisher_cabinet/directional/north, /obj/effect/turf_decal/floordetail/tiled, /turf/open/floor/plasteel, /area/outpost/hallway/port) @@ -3399,10 +3432,18 @@ /turf/open/floor/plating/asteroid, /area/outpost/external) "vd" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, /obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/structure/chair{ + dir = 8 + }, +/obj/structure/extinguisher_cabinet/directional/east{ + pixel_y = -7 + }, +/obj/machinery/firealarm/directional/east{ + pixel_y = 6 + }, /turf/open/floor/plasteel, -/area/outpost/vacant_rooms) +/area/outpost/crew/cryo) "vp" = ( /obj/effect/turf_decal/corner/opaque/grey/full, /turf/open/floor/plasteel, @@ -3587,6 +3628,25 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel/patterned, /area/outpost/cargo) +"ww" = ( +/obj/machinery/door/airlock/glass{ + name = "Cryogenics" + }, +/obj/effect/turf_decal/industrial/warning, +/obj/effect/turf_decal/industrial/warning{ + dir = 1 + }, +/obj/machinery/door/firedoor/border_only, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/turf/open/floor/plasteel/tech, +/area/outpost/crew/cryo) "wA" = ( /obj/effect/decal/cleanable/food/tomato_smudge, /turf/open/floor/wood/mahogany, @@ -3669,6 +3729,16 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/plasteel, /area/outpost/storage) +"wY" = ( +/obj/machinery/cryopod, +/obj/effect/turf_decal/corner_techfloor_grid{ + dir = 5 + }, +/obj/structure/sign/poster/random{ + pixel_y = 30 + }, +/turf/open/floor/plasteel/tech/techmaint, +/area/outpost/crew/cryo) "xc" = ( /obj/effect/turf_decal/corner/opaque/red{ dir = 4 @@ -3738,10 +3808,12 @@ /turf/open/floor/plasteel/tech/techmaint, /area/outpost/maintenance/central) "xI" = ( -/obj/machinery/camera/autoname, -/obj/effect/turf_decal/corner/opaque/neutral/diagonal, -/turf/open/floor/plasteel, -/area/outpost/vacant_rooms) +/obj/machinery/cryopod, +/obj/effect/turf_decal/corner_techfloor_grid{ + dir = 5 + }, +/turf/open/floor/plasteel/tech/techmaint, +/area/outpost/crew/cryo) "xJ" = ( /obj/effect/turf_decal/corner/opaque/grey/full, /obj/structure/disposalpipe/segment{ @@ -4779,7 +4851,6 @@ dir = 4 }, /obj/effect/decal/cleanable/dirt/dust, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, /obj/effect/turf_decal/corner/opaque/black{ dir = 4 }, @@ -5361,15 +5432,20 @@ /turf/open/floor/plating, /area/outpost/maintenance/fore) "HB" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/glowstick, -/obj/effect/spawner/lootdrop/glowstick, -/obj/effect/spawner/lootdrop/glowstick, -/obj/effect/spawner/lootdrop/glowstick, -/obj/effect/spawner/lootdrop/glowstick, -/obj/machinery/firealarm/directional/east, -/turf/open/floor/plasteel/patterned/cargo_one, -/area/outpost/vacant_rooms) +/obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/structure/table, +/obj/item/paper_bin{ + pixel_y = 6; + pixel_x = 6 + }, +/obj/item/pen, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable/yellow{ + icon_state = "0-8" + }, +/obj/effect/turf_decal/steeldecal/steel_decals_central6, +/turf/open/floor/plasteel, +/area/outpost/crew/cryo) "HD" = ( /turf/closed/indestructible/rock, /area/outpost/external) @@ -5628,11 +5704,13 @@ /turf/open/floor/plating, /area/outpost/maintenance/fore) "IS" = ( -/obj/machinery/light/dim/directional/east, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4, /obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/machinery/light/dim/directional/east, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ + dir = 8 + }, /turf/open/floor/plasteel, -/area/outpost/vacant_rooms) +/area/outpost/crew/cryo) "IT" = ( /obj/machinery/camera/autoname, /obj/effect/turf_decal/corner/opaque/red{ @@ -6036,10 +6114,12 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ dir = 4 }, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4, /obj/effect/turf_decal/corner/opaque/neutral{ dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ + dir = 4 + }, /turf/open/floor/plasteel, /area/outpost/hallway/port) "Lh" = ( @@ -6140,10 +6220,10 @@ /area/outpost/maintenance/fore) "LS" = ( /obj/effect/turf_decal/corner/opaque/grey/full, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, /obj/effect/turf_decal/corner/opaque/neutral{ dir = 1 }, +/obj/structure/extinguisher_cabinet/directional/north, /turf/open/floor/plasteel, /area/outpost/hallway/port) "LU" = ( @@ -6332,6 +6412,15 @@ }, /turf/open/floor/plasteel, /area/outpost/hallway/central) +"MQ" = ( +/obj/machinery/cryopod{ + dir = 1 + }, +/obj/effect/turf_decal/corner_techfloor_grid{ + dir = 10 + }, +/turf/open/floor/plasteel/tech/techmaint, +/area/outpost/crew/cryo) "Nc" = ( /turf/closed/indestructible/rock, /area/outpost/hallway/central) @@ -6615,6 +6704,18 @@ }, /turf/open/floor/plasteel, /area/outpost/hallway/central) +"Pt" = ( +/obj/machinery/cryopod{ + dir = 1 + }, +/obj/effect/turf_decal/corner_techfloor_grid{ + dir = 10 + }, +/obj/machinery/camera/autoname{ + dir = 4 + }, +/turf/open/floor/plasteel/tech/techmaint, +/area/outpost/crew/cryo) "Pw" = ( /obj/structure/rack, /obj/effect/spawner/lootdrop/maintenance/four, @@ -6626,6 +6727,10 @@ /obj/structure/extinguisher_cabinet/directional/east, /turf/open/floor/plating, /area/outpost/maintenance/fore) +"PA" = ( +/obj/effect/spawner/structure/window/reinforced/indestructable, +/turf/open/floor/plating, +/area/outpost/crew/cryo) "PD" = ( /obj/structure/chair/sofa/brown/right/directional/west, /turf/open/floor/carpet/royalblack, @@ -6679,6 +6784,26 @@ }, /turf/open/floor/wood, /area/outpost/crew/bar) +"PQ" = ( +/obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey/corner{ + dir = 1 + }, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey/corner{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ + dir = 6 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ + dir = 10 + }, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plasteel, +/area/outpost/crew/cryo) "PS" = ( /obj/structure/falsewall/reinforced, /obj/structure/cable/yellow{ @@ -6714,7 +6839,6 @@ /area/outpost/security) "Qa" = ( /obj/effect/turf_decal/corner/opaque/grey/full, -/obj/machinery/newscaster/directional/north, /obj/effect/turf_decal/corner/opaque/neutral{ dir = 1 }, @@ -7301,10 +7425,14 @@ /turf/open/floor/plasteel, /area/outpost/security) "Tg" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/donut, -/turf/open/floor/plasteel/patterned/cargo_one, -/area/outpost/vacant_rooms) +/obj/effect/turf_decal/corner/opaque/neutral/diagonal, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{ + dir = 1 + }, +/obj/effect/turf_decal/spline/fancy/opaque/lightgrey, +/obj/machinery/light/dim/directional/west, +/turf/open/floor/plasteel, +/area/outpost/crew/cryo) "Th" = ( /turf/closed/indestructible/reinforced, /area/outpost/cargo/office) @@ -7829,6 +7957,11 @@ /obj/effect/turf_decal/corner/opaque/neutral{ dir = 1 }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, /turf/open/floor/plasteel, /area/outpost/hallway/port) "Wz" = ( @@ -16959,11 +17092,11 @@ LL Uq Aw LL -wL -wL -wL -wL -wL +lG +lG +lG +lG +lG BM Ky yj @@ -17082,11 +17215,11 @@ LL LL LL LL -wL -aM +lG +wY Tg -aM -wL +Pt +PA rm Kl hW @@ -17205,11 +17338,11 @@ HD HD HD HD -wL +lG xI kh -ev -wL +MQ +PA Qa LY wu @@ -17328,14 +17461,14 @@ HD HD HD HD -wL +lG aM -kh +PQ nz -wL -Fi +ww +Wv qR -wu +lJ iV Pw AW @@ -17451,7 +17584,7 @@ HD HD HD HD -wL +lG HB IS vd @@ -17825,7 +17958,7 @@ yN XA di wL -Wv +Fi vp kb XD diff --git a/_maps/shuttles/independent/independent_shetland.dmm b/_maps/shuttles/independent/independent_shetland.dmm index f147aeb82276..459bfebcd48f 100644 --- a/_maps/shuttles/independent/independent_shetland.dmm +++ b/_maps/shuttles/independent/independent_shetland.dmm @@ -377,6 +377,9 @@ }, /obj/machinery/atmospherics/pipe/simple/cyan/visible/layer5, /obj/item/radio/intercom/directional/east, +/obj/structure/cable/yellow{ + icon_state = "2-8" + }, /turf/open/floor/plasteel/dark, /area/ship/engineering/engine) "dl" = ( @@ -930,9 +933,6 @@ /obj/machinery/atmospherics/pipe/simple/cyan/visible/layer5{ dir = 5 }, -/obj/structure/cable/yellow{ - icon_state = "4-10" - }, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ dir = 4 }, @@ -945,6 +945,12 @@ /obj/machinery/atmospherics/pipe/simple/dark/visible{ dir = 10 }, +/obj/structure/cable/yellow{ + icon_state = "1-4" + }, +/obj/structure/cable/yellow{ + icon_state = "4-8" + }, /turf/open/floor/plasteel/dark, /area/ship/engineering/engine) "ib" = ( @@ -1336,6 +1342,9 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ dir = 9 }, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, /turf/open/floor/plating, /area/ship/engineering/engine) "lu" = ( @@ -2144,6 +2153,9 @@ /obj/structure/cable{ icon_state = "1-6" }, +/obj/structure/cable/yellow{ + icon_state = "4-8" + }, /turf/open/floor/plasteel/dark, /area/ship/engineering/engine) "sb" = ( @@ -2686,9 +2698,6 @@ /turf/open/floor/plasteel/dark, /area/ship/engineering/engine) "wW" = ( -/obj/structure/cable/yellow{ - icon_state = "1-6" - }, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ dir = 6 }, @@ -2698,6 +2707,12 @@ /obj/machinery/atmospherics/pipe/simple/dark/visible{ dir = 4 }, +/obj/structure/cable/yellow{ + icon_state = "1-5" + }, +/obj/structure/cable/yellow{ + icon_state = "2-4" + }, /turf/open/floor/plating, /area/ship/engineering/engine) "xd" = ( @@ -3115,12 +3130,6 @@ /obj/effect/turf_decal/number/zero{ dir = 8 }, -/obj/structure/cable/yellow{ - icon_state = "5-9" - }, -/obj/structure/cable/yellow{ - icon_state = "5-10" - }, /obj/machinery/atmospherics/pipe/layer_manifold/visible{ dir = 4 }, @@ -5524,12 +5533,12 @@ /turf/open/floor/plasteel, /area/ship/hallway/fore) "VV" = ( -/obj/structure/cable/yellow{ - icon_state = "2-5" - }, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ dir = 1 }, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, /turf/open/floor/plasteel/dark, /area/ship/engineering/engine) "VZ" = ( @@ -5766,6 +5775,9 @@ /obj/structure/cable{ icon_state = "1-2" }, +/obj/structure/cable/yellow{ + icon_state = "4-10" + }, /turf/open/floor/plasteel/dark, /area/ship/engineering/engine) "XY" = ( diff --git a/_maps/shuttles/inteq/inteq_colossus.dmm b/_maps/shuttles/inteq/inteq_colossus.dmm index 5925b9c0acdd..920d16cfc1c7 100644 --- a/_maps/shuttles/inteq/inteq_colossus.dmm +++ b/_maps/shuttles/inteq/inteq_colossus.dmm @@ -3423,13 +3423,13 @@ /obj/effect/turf_decal/trimline/opaque/yellow/line{ dir = 8 }, -/obj/item/ammo_box/magazine/gal{ +/obj/item/ammo_box/magazine/f4_308{ pixel_x = -5 }, -/obj/item/ammo_box/magazine/gal{ +/obj/item/ammo_box/magazine/f4_308{ pixel_x = 5 }, -/obj/item/gun/ballistic/automatic/marksman/gal/inteq{ +/obj/item/gun/ballistic/automatic/marksman/f4/inteq{ pixel_x = -8; pixel_y = 3 }, diff --git a/_maps/shuttles/inteq/inteq_talos.dmm b/_maps/shuttles/inteq/inteq_talos.dmm index 491853b5266c..ddad109a06fb 100644 --- a/_maps/shuttles/inteq/inteq_talos.dmm +++ b/_maps/shuttles/inteq/inteq_talos.dmm @@ -874,7 +874,6 @@ icon_state = "0-2" }, /obj/effect/decal/cleanable/dirt, -/obj/structure/extinguisher_cabinet/directional/north, /obj/structure/extinguisher_cabinet/directional/east, /turf/open/floor/plasteel/grimy, /area/ship/crew) @@ -4630,11 +4629,13 @@ }, /obj/structure/crate_shelf, /obj/structure/closet/crate/medical, -/obj/item/storage/firstaid/regular{ - pixel_x = 5 +/obj/item/storage/firstaid/brute{ + pixel_x = -8; + pixel_y = 0 }, -/obj/item/storage/firstaid/medical{ - pixel_x = -5 +/obj/item/storage/firstaid/fire{ + pixel_x = 9; + pixel_y = 0 }, /obj/item/storage/box/bodybags, /turf/open/floor/plasteel/patterned/cargo_one, diff --git a/_maps/shuttles/pgf/pgf_crying_sun.dmm b/_maps/shuttles/pgf/pgf_crying_sun.dmm index e016ef725525..c0584df1e945 100644 --- a/_maps/shuttles/pgf/pgf_crying_sun.dmm +++ b/_maps/shuttles/pgf/pgf_crying_sun.dmm @@ -1,4 +1,18 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"ae" = ( +/obj/structure/cable/blue{ + icon_state = "0-4" + }, +/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/door/poddoor{ + dir = 4; + id = "lib_engine_blast" + }, +/obj/machinery/power/shuttle/engine/fire{ + dir = 4 + }, +/turf/open/floor/plating, +/area/ship/engineering/engines/port) "ai" = ( /obj/structure/railing, /obj/structure/cable/yellow{ @@ -129,6 +143,13 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, /turf/open/floor/plasteel/white, /area/ship/hallway/port) +"bi" = ( +/obj/machinery/computer/crew{ + dir = 8 + }, +/obj/structure/catwalk/over/plated_catwalk/dark, +/turf/open/floor/plasteel/telecomms_floor, +/area/ship/bridge) "bl" = ( /obj/effect/turf_decal/industrial/traffic, /obj/machinery/power/apc/auto_name/directional/north, @@ -215,10 +236,6 @@ /obj/item/storage/box/flashes{ pixel_y = -1 }, -/obj/item/screwdriver{ - pixel_x = -8; - pixel_y = -5 - }, /obj/item/screwdriver{ pixel_x = -5; pixel_y = -5 @@ -235,10 +252,6 @@ pixel_x = 4; pixel_y = -5 }, -/obj/item/screwdriver{ - pixel_x = 7; - pixel_y = -5 - }, /obj/structure/closet/secure_closet/wall/directional/west{ icon_state = "sec_wall"; name = "equipment locker" @@ -406,9 +419,6 @@ dir = 6 }, /obj/structure/catwalk/over/plated_catwalk/dark, -/obj/structure/cable{ - icon_state = "1-8" - }, /obj/structure/cable{ icon_state = "1-2" }, @@ -416,13 +426,28 @@ /area/ship/engineering/engines/starboard) "du" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 6 + dir = 6; + mode = 1 }, /obj/structure/cable{ icon_state = "0-8" }, /turf/open/floor/engine/hull, /area/ship/external/dark) +"dv" = ( +/obj/structure/cable/blue{ + icon_state = "0-4" + }, +/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/door/poddoor{ + dir = 4; + id = "lib_engine_blast" + }, +/obj/machinery/power/shuttle/engine/fire{ + dir = 4 + }, +/turf/open/floor/plating, +/area/ship/engineering/engines/starboard) "dz" = ( /obj/effect/turf_decal/corner_steel_grid{ dir = 10 @@ -464,10 +489,10 @@ /turf/open/floor/plasteel/telecomms_floor, /area/ship/hallway/port) "dK" = ( -/obj/machinery/computer/cargo{ - dir = 8 +/obj/structure/chair/comfy/shuttle{ + dir = 4 }, -/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/effect/turf_decal/floordetail/tiled, /obj/structure/cable{ icon_state = "1-2" }, @@ -523,9 +548,6 @@ dir = 8; name = "engine fuel pump" }, -/obj/structure/cable{ - icon_state = "2-4" - }, /obj/machinery/camera/autoname{ dir = 2; network = list("GEC") @@ -1010,12 +1032,27 @@ /obj/machinery/light/directional/north, /turf/open/floor/plating, /area/ship/engineering) +"hX" = ( +/obj/structure/window/reinforced/fulltile/shuttle, +/obj/structure/grille, +/obj/machinery/door/firedoor/border_only{ + dir = 8 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, +/obj/machinery/door/poddoor/shutters{ + id = "lib_bridge_shut" + }, +/turf/open/floor/plating, +/area/ship/bridge) "if" = ( /obj/structure/cable{ icon_state = "0-8" }, /obj/machinery/porta_turret/ship/weak{ - dir = 4 + dir = 4; + mode = 1 }, /obj/structure/catwalk/over/plated_catwalk/dark, /turf/open/floor/plating/airless, @@ -1166,12 +1203,12 @@ /area/ship/hangar/starboard) "jm" = ( /obj/machinery/holopad/emergency, -/obj/effect/turf_decal/spline/fancy/opaque/lime{ - dir = 1 - }, /obj/structure/cable{ icon_state = "4-8" }, +/obj/effect/turf_decal/spline/fancy/opaque/lime{ + dir = 1 + }, /turf/open/floor/plasteel/dark, /area/ship/bridge) "jt" = ( @@ -1253,7 +1290,7 @@ }, /obj/machinery/button/door{ id = "lib_armory_1"; - name = "Sergeant Access"; + name = "Lieutenant Access"; dir = 4; pixel_x = -20; pixel_y = -5; @@ -1421,7 +1458,7 @@ /obj/structure/table/reinforced, /obj/effect/turf_decal/corner/opaque/lime/mono, /obj/machinery/camera/autoname{ - dir = 1 + dir = 10 }, /obj/machinery/light/directional/south, /turf/open/floor/plasteel/mono/dark, @@ -1457,14 +1494,13 @@ pixel_x = 4; pixel_y = 4 }, +/obj/machinery/light/directional/north, /obj/item/reagent_containers/food/drinks/bottle/whiskey{ pixel_x = -10; pixel_y = 3; - name = "'Anastheasia'"; + name = "'Anesthesia'"; desc = "A bottle of Git's with the word 'Anastheasia' written over it in marker." }, -/obj/machinery/light/directional/north, -/obj/item/clothing/neck/stethoscope, /turf/open/floor/mineral/titanium, /area/ship/medical) "mL" = ( @@ -1473,7 +1509,7 @@ }, /obj/structure/closet/crate{ icon_state = "wooden"; - name = "myning geer"; + name = "mining gear"; desc = "A rectangular steel crate with 'myning geer' spelled out in crayon on top." }, /obj/item/storage/bag/ore{ @@ -1608,9 +1644,12 @@ /turf/open/floor/plasteel/white, /area/ship/hallway/port) "no" = ( -/obj/structure/bed, -/obj/item/bedsheet/cosmos, /obj/structure/curtain/cloth/grey, +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/bed, +/obj/item/bedsheet/brown, /turf/open/floor/plasteel/dark, /area/ship/crew/dorm) "nv" = ( @@ -1665,6 +1704,26 @@ }, /turf/open/floor/plasteel/telecomms_floor, /area/ship/crew/cryo) +"om" = ( +/obj/effect/turf_decal/corner/opaque/lime/mono, +/obj/structure/table/reinforced, +/obj/item/clipboard{ + pixel_x = -9; + pixel_y = -1 + }, +/obj/item/pen{ + pixel_x = -10; + pixel_y = 1 + }, +/obj/item/gps{ + pixel_x = 8; + pixel_y = -2 + }, +/obj/item/binoculars{ + pixel_y = 9 + }, +/turf/open/floor/plasteel/mono/dark, +/area/ship/bridge) "ou" = ( /obj/structure/railing{ dir = 8 @@ -1784,22 +1843,22 @@ /obj/structure/railing{ dir = 4 }, -/obj/structure/table, /obj/item/toy/plush/knight{ - pixel_y = 10; + pixel_y = 11; pixel_x = -10 }, /obj/item/toy/plush/hornet{ - pixel_y = 8; - pixel_x = -3 + pixel_y = 10; + pixel_x = -2 }, /obj/item/toy/plush/mora{ - pixel_y = 4; - pixel_x = 5 + pixel_y = 6; + pixel_x = 6 }, /obj/effect/turf_decal/corner_steel_grid{ dir = 10 }, +/obj/structure/dresser, /turf/open/floor/plasteel/dark, /area/ship/crew/dorm) "oX" = ( @@ -1848,6 +1907,22 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, /turf/open/floor/plasteel/mono/dark, /area/ship/hallway/central) +"pK" = ( +/obj/structure/cable/blue{ + icon_state = "0-8" + }, +/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/obj/structure/window/plasma/reinforced{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/shuttle/fire_heater{ + dir = 4 + }, +/turf/open/floor/plating, +/area/ship/engineering/engines/starboard) "pW" = ( /obj/machinery/computer/helm/viewscreen/directional/west, /obj/effect/turf_decal/siding/wood{ @@ -1883,15 +1958,8 @@ /turf/open/floor/plasteel/sepia, /area/ship/crew/dorm/dormthree) "qj" = ( -/obj/structure/table/reinforced, -/obj/item/desk_flag/gezena{ - pixel_x = 9; - pixel_y = 1 - }, -/obj/machinery/recharger{ - pixel_y = 1; - pixel_x = -6 - }, +/obj/structure/chair/comfy/shuttle, +/obj/effect/turf_decal/floordetail/tiled, /turf/open/floor/plasteel/telecomms_floor, /area/ship/bridge) "qm" = ( @@ -1904,7 +1972,6 @@ /turf/open/floor/plasteel/dark, /area/ship/crew/canteen) "qp" = ( -/obj/machinery/atmospherics/pipe/simple/orange/visible, /obj/structure/cable{ icon_state = "1-10" }, @@ -1915,6 +1982,9 @@ dir = 1 }, /obj/item/paper/guides/jobs/engi/combustion_thruster, +/obj/machinery/atmospherics/pipe/manifold/orange/visible{ + dir = 4 + }, /turf/open/floor/plasteel/tech, /area/ship/engineering/engines/starboard) "qy" = ( @@ -2104,11 +2174,6 @@ /turf/closed/wall/mineral/plastitanium, /area/ship/engineering/engines/starboard) "sF" = ( -/obj/machinery/door/airlock/security/glass{ - req_one_access_txt = "1"; - req_access = list(1); - name = "Checkpoint" - }, /obj/structure/cable{ icon_state = "1-2" }, @@ -2117,6 +2182,10 @@ dir = 1 }, /obj/machinery/door/firedoor/border_only, +/obj/machinery/door/airlock/security/glass{ + req_one_access_txt = "1"; + name = "Checkpoint" + }, /turf/open/floor/plasteel/dark, /area/ship/hallway/port) "sJ" = ( @@ -2241,7 +2310,6 @@ pixel_y = 3 }, /obj/effect/turf_decal/corner/opaque/lime/mono, -/obj/machinery/light/directional/south, /obj/machinery/light_switch{ dir = 4; pixel_x = -19; @@ -2260,26 +2328,31 @@ /turf/closed/wall/mineral/titanium/exterior, /area/ship/crew/cryo) "tQ" = ( -/obj/structure/chair/comfy/shuttle, /obj/effect/turf_decal/corner_steel_grid{ dir = 9 }, +/obj/structure/chair/comfy/shuttle, +/obj/effect/turf_decal/floordetail/tiled, /turf/open/floor/plasteel/telecomms_floor, /area/ship/bridge) "tU" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 5 + dir = 5; + mode = 1 }, /obj/structure/cable{ icon_state = "0-2" }, /turf/open/floor/engine/hull, /area/ship/external/dark) -"uh" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 4 +"tY" = ( +/obj/machinery/modular_computer/console/preset/command{ + dir = 1 }, -/obj/effect/turf_decal/floordetail/tiled, +/obj/structure/catwalk/over/plated_catwalk/dark, +/turf/open/floor/plasteel/telecomms_floor, +/area/ship/bridge) +"uh" = ( /obj/structure/cable{ icon_state = "4-8" }, @@ -2585,12 +2658,12 @@ /turf/open/floor/plasteel/dark, /area/ship/crew/canteen) "wz" = ( -/obj/effect/turf_decal/corner_steel_grid{ - dir = 9 - }, /obj/structure/cable{ icon_state = "4-8" }, +/obj/effect/turf_decal/corner_steel_grid{ + dir = 9 + }, /turf/open/floor/plasteel/telecomms_floor, /area/ship/bridge) "wK" = ( @@ -2812,6 +2885,19 @@ /obj/machinery/airalarm/directional/east, /turf/open/floor/plasteel/tech, /area/ship/engineering/engines/port) +"yy" = ( +/obj/structure/table/reinforced, +/obj/item/desk_flag/gezena{ + pixel_x = 9; + pixel_y = 1 + }, +/obj/machinery/recharger{ + pixel_y = 1; + pixel_x = -6 + }, +/obj/effect/turf_decal/floordetail/tiled, +/turf/open/floor/plasteel/telecomms_floor, +/area/ship/bridge) "yF" = ( /obj/effect/turf_decal/industrial/traffic{ dir = 1 @@ -2932,9 +3018,6 @@ icon_state = "1-4" }, /obj/structure/catwalk/over/plated_catwalk, -/obj/structure/table/reinforced{ - color = "#c1b6a5" - }, /obj/machinery/cell_charger, /obj/item/stock_parts/cell/high, /obj/structure/cable, @@ -2945,6 +3028,7 @@ dir = 5 }, /obj/machinery/airalarm/directional/west, +/obj/structure/table/reinforced, /turf/open/floor/plating, /area/ship/engineering) "zL" = ( @@ -2970,7 +3054,7 @@ }, /obj/machinery/door/airlock/security{ req_access = list(3); - name = "Sergeant's Quarters" + name = "Lieutenant's Quarters" }, /turf/open/floor/plasteel/tech, /area/ship/security) @@ -3092,6 +3176,22 @@ /obj/machinery/vending/toyliberationstation, /turf/open/floor/plasteel/tech, /area/ship/security) +"AM" = ( +/obj/structure/cable/blue{ + icon_state = "0-8" + }, +/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/obj/structure/window/plasma/reinforced{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/shuttle/fire_heater{ + dir = 4 + }, +/turf/open/floor/plating, +/area/ship/engineering/engines/port) "AN" = ( /obj/structure/window/reinforced{ dir = 1 @@ -3353,7 +3453,8 @@ /area/ship/crew/canteen) "CF" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 10 + dir = 10; + mode = 1 }, /obj/structure/cable{ icon_state = "0-4" @@ -3583,10 +3684,7 @@ /turf/closed/wall/mineral/plastitanium/nodiagonal, /area/ship/external/dark) "El" = ( -/obj/structure/railing{ - dir = 8 - }, -/obj/machinery/modular_computer/console/preset/command{ +/obj/machinery/computer/security{ dir = 1 }, /obj/structure/catwalk/over/plated_catwalk/dark, @@ -3635,7 +3733,8 @@ /area/ship/hallway/central) "Eu" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 5 + dir = 5; + mode = 1 }, /obj/structure/cable, /turf/open/floor/engine/hull, @@ -3898,7 +3997,8 @@ /area/ship/hallway/central) "FJ" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 5 + dir = 5; + mode = 1 }, /obj/structure/cable{ icon_state = "0-4" @@ -3918,7 +4018,8 @@ /area/ship/engineering/engines/starboard) "FO" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 8 + dir = 8; + mode = 1 }, /obj/structure/cable{ icon_state = "0-4" @@ -3946,10 +4047,10 @@ /turf/open/floor/plasteel/mono/dark, /area/ship/hallway/central) "Gb" = ( -/obj/machinery/computer/crew{ - dir = 8 +/obj/structure/chair/comfy/shuttle{ + dir = 4 }, -/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/effect/turf_decal/floordetail/tiled, /obj/structure/cable{ icon_state = "1-8" }, @@ -4104,8 +4205,8 @@ "GW" = ( /obj/structure/rack, /obj/item/stock_parts/cell/gun/pgf{ - pixel_y = 12; - pixel_x = -3 + pixel_x = -6; + pixel_y = -2 }, /obj/item/stock_parts/cell/gun/pgf{ pixel_y = 10; @@ -4128,6 +4229,13 @@ pixel_x = 3 }, /obj/effect/turf_decal/corner/opaque/neutral/full, +/obj/item/stock_parts/cell/gun/pgf{ + pixel_x = -12 + }, +/obj/item/stock_parts/cell/gun/pgf{ + pixel_x = -8; + pixel_y = -2 + }, /turf/open/floor/vault, /area/ship/security/armory) "Ha" = ( @@ -4146,7 +4254,8 @@ /area/ship/security/armory) "Hb" = ( /obj/machinery/porta_turret/ship/weak{ - dir = 4 + dir = 4; + mode = 1 }, /obj/structure/cable{ icon_state = "0-2" @@ -4300,10 +4409,6 @@ /area/ship/crew/canteen) "Ig" = ( /obj/machinery/computer/helm/viewscreen/directional/north, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/obj/effect/turf_decal/floordetail/tiled, /turf/open/floor/plasteel/telecomms_floor, /area/ship/bridge) "Ir" = ( @@ -4466,6 +4571,20 @@ }, /turf/open/floor/engine/hull, /area/ship/external/dark) +"KH" = ( +/obj/structure/window/reinforced/fulltile/shuttle, +/obj/structure/grille, +/obj/machinery/door/poddoor/shutters{ + id = "lib_bridge_shut" + }, +/obj/machinery/door/firedoor/border_only{ + dir = 8 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, +/turf/open/floor/plating, +/area/ship/bridge) "KM" = ( /turf/closed/wall/mineral/titanium/nodiagonal, /area/ship/hallway/port) @@ -4516,9 +4635,6 @@ dir = 8; name = "engine fuel pump" }, -/obj/structure/cable{ - icon_state = "1-4" - }, /obj/machinery/camera/autoname{ dir = 1; network = list("GEC") @@ -4677,7 +4793,7 @@ "Mw" = ( /obj/structure/bed, /obj/item/bedsheet/hos{ - name = "Sergeant's Bedsheet"; + name = "Lieutenant's Bedsheet"; desc = "It is decorated with a shield emblem." }, /obj/effect/turf_decal/borderfloorblack, @@ -4713,9 +4829,6 @@ pixel_x = -9; pixel_y = 3 }, -/obj/item/clothing/head/gezena/medic/flap{ - pixel_x = 12 - }, /obj/item/clothing/head/gezena/medic{ pixel_y = 4; pixel_x = 7 @@ -4743,6 +4856,11 @@ pixel_x = -13; pixel_y = -19 }, +/obj/item/clothing/neck/stethoscope, +/obj/item/clothing/head/gezena/flap/medic{ + pixel_y = 1; + pixel_x = 12 + }, /turf/open/floor/mineral/titanium, /area/ship/medical) "MF" = ( @@ -4801,6 +4919,9 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ dir = 8 }, +/obj/structure/railing/corner{ + dir = 4 + }, /turf/open/floor/plasteel/tech, /area/ship/bridge) "MZ" = ( @@ -4877,7 +4998,7 @@ dir = 8 }, /obj/structure/closet/crate{ - name = "Mishun Acomplshed" + name = "Mission Accomplished" }, /obj/item/poster/mission_accomplished_7, /obj/item/poster/mission_accomplished_6, @@ -4917,8 +5038,13 @@ /area/ship/external/dark) "NF" = ( /obj/structure/closet/crate/radiation, -/obj/effect/turf_decal/borderfloorblack{ - dir = 4 +/obj/item/clothing/suit/radiation, +/obj/item/clothing/suit/radiation, +/obj/item/clothing/head/radiation{ + pixel_x = -7 + }, +/obj/item/clothing/head/radiation{ + pixel_x = -7 }, /turf/open/floor/plasteel/dark, /area/ship/construction) @@ -5119,6 +5245,9 @@ /turf/open/floor/vault, /area/ship/security/armory) "Pb" = ( +/obj/structure/railing{ + dir = 4 + }, /turf/open/floor/plasteel/stairs{ icon = 'icons/obj/stairs.dmi'; dir = 2 @@ -5145,7 +5274,6 @@ /turf/open/floor/plating, /area/ship/engineering) "Ph" = ( -/obj/machinery/atmospherics/pipe/simple/orange/visible, /obj/structure/cable{ icon_state = "2-9" }, @@ -5155,6 +5283,9 @@ /obj/machinery/atmospherics/components/trinary/mixer/layer2{ dir = 1 }, +/obj/machinery/atmospherics/pipe/manifold/orange/visible{ + dir = 4 + }, /turf/open/floor/plasteel/tech, /area/ship/engineering/engines/port) "Pi" = ( @@ -5360,6 +5491,13 @@ /obj/effect/turf_decal/corner/opaque/neutral/full, /turf/open/floor/vault, /area/ship/security/armory) +"Qi" = ( +/obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/computer/cargo{ + dir = 8 + }, +/turf/open/floor/plasteel/telecomms_floor, +/area/ship/bridge) "Qq" = ( /obj/structure/rack, /obj/item/kitchen/knife/combat/survival{ @@ -5867,6 +6005,7 @@ /obj/item/gun/energy/kalix/pistol{ pixel_y = -16 }, +/obj/item/screwdriver, /turf/open/floor/plasteel/sepia, /area/ship/crew/dorm/dormthree) "UH" = ( @@ -5915,13 +6054,6 @@ pixel_x = -10; pixel_y = -7 }, -/obj/item/clothing/under/gezena/marine{ - pixel_x = -10; - pixel_y = -7 - }, -/obj/item/clothing/gloves/gezena/marine{ - pixel_y = 11 - }, /obj/item/clothing/gloves/gezena/marine{ pixel_y = 11 }, @@ -5931,22 +6063,6 @@ /obj/item/clothing/gloves/gezena/marine{ pixel_y = 11 }, -/obj/item/clothing/head/gezena/marine/flap{ - pixel_x = -10 - }, -/obj/item/clothing/head/gezena/marine/flap{ - pixel_x = -10 - }, -/obj/item/clothing/head/gezena/marine/flap{ - pixel_x = -10 - }, -/obj/item/clothing/head/gezena/marine/flap{ - pixel_x = -10 - }, -/obj/item/clothing/head/gezena/marine{ - pixel_y = 10; - pixel_x = -10 - }, /obj/item/clothing/head/gezena/marine{ pixel_y = 10; pixel_x = -10 @@ -5975,13 +6091,6 @@ /obj/item/storage/backpack/duffelbag/sec{ pixel_y = -13 }, -/obj/item/storage/backpack/duffelbag/sec{ - pixel_y = -13 - }, -/obj/item/storage/backpack/satchel/sec{ - pixel_x = -8; - pixel_y = -17 - }, /obj/item/storage/backpack/satchel/sec{ pixel_x = -8; pixel_y = -17 @@ -6006,11 +6115,19 @@ pixel_x = 3; pixel_y = 0 }, -/obj/item/clothing/shoes/combat/gezena{ - pixel_x = 3; - pixel_y = 0 - }, /obj/effect/turf_decal/corner/opaque/neutral/full, +/obj/item/clothing/head/gezena/flap/marine{ + pixel_y = 1; + pixel_x = -10 + }, +/obj/item/clothing/head/gezena/flap/marine{ + pixel_y = 1; + pixel_x = -10 + }, +/obj/item/clothing/head/gezena/flap/marine{ + pixel_y = 1; + pixel_x = -10 + }, /turf/open/floor/vault, /area/ship/security/armory) "Vp" = ( @@ -6059,11 +6176,11 @@ /turf/open/floor/plasteel/mono, /area/ship/hangar/starboard) "VM" = ( -/obj/machinery/power/terminal{ - dir = 8 - }, -/obj/structure/cable, /obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "engine fuel pump" + }, /turf/open/floor/plating, /area/ship/engineering/engines/starboard) "VY" = ( @@ -6075,7 +6192,7 @@ anchored = 1; can_be_unanchored = 1; icon_state = "warden"; - name = "sergeant's locker"; + name = "Lieutenant's locker"; req_access_txt = "3"; req_access = list(3) }, @@ -6106,10 +6223,6 @@ pixel_y = 1; pixel_x = -11 }, -/obj/item/clothing/head/gezena/marine/lead/flap{ - pixel_y = 9; - pixel_x = -11 - }, /obj/item/clothing/suit/armor/gezena/marinecoat{ pixel_y = 8 }, @@ -6117,14 +6230,6 @@ pixel_y = 11; pixel_x = 5 }, -/obj/item/radio/headset/pgf/captain{ - pixel_x = -4; - pixel_y = 9 - }, -/obj/item/kitchen/knife/combat/survival{ - pixel_y = 0; - pixel_x = 1 - }, /obj/item/storage/belt/military/gezena{ pixel_y = 1 }, @@ -6132,6 +6237,10 @@ pixel_x = -8; pixel_y = -17 }, +/obj/item/radio/headset/pgf/captain{ + pixel_x = -2; + pixel_y = 9 + }, /obj/item/storage/backpack/duffelbag/sec{ pixel_y = -13 }, @@ -6140,7 +6249,14 @@ pixel_x = -3 }, /obj/item/clothing/head/helmet/gezena{ - pixel_y = -11 + pixel_x = -11 + }, +/obj/item/clothing/suit/armor/gezena/marine{ + pixel_y = 8 + }, +/obj/item/clothing/head/gezena/flap/marine/lead{ + pixel_y = 10; + pixel_x = -10 }, /turf/open/floor/plasteel/tech, /area/ship/crew/dorm/dormtwo) @@ -6171,6 +6287,16 @@ }, /turf/open/floor/plasteel/sepia, /area/ship/crew/dorm) +"Wg" = ( +/obj/effect/turf_decal/spline/fancy/opaque/lime{ + dir = 1 + }, +/obj/effect/turf_decal/spline/fancy/opaque/lime, +/obj/structure/railing{ + dir = 1 + }, +/turf/open/floor/plasteel/tech, +/area/ship/bridge) "Wk" = ( /obj/machinery/power/smes/shuttle/precharged{ dir = 4 @@ -6229,9 +6355,6 @@ dir = 5 }, /obj/structure/catwalk/over/plated_catwalk/dark, -/obj/structure/cable{ - icon_state = "2-8" - }, /obj/structure/cable{ icon_state = "1-2" }, @@ -6384,13 +6507,11 @@ /turf/open/floor/plasteel/tech, /area/ship/construction) "Yd" = ( -/obj/machinery/power/terminal{ - dir = 8 - }, -/obj/structure/cable{ - icon_state = "0-2" - }, /obj/structure/catwalk/over/plated_catwalk/dark, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "engine fuel pump" + }, /turf/open/floor/plating, /area/ship/engineering/engines/port) "Yo" = ( @@ -6707,14 +6828,14 @@ xz xz lc hJ -hJ +ae is lc xz xz TV FN -fN +dv fN TV xz @@ -6735,14 +6856,14 @@ xz lc lc CB -CB +AM UM lc FO LD TV bB -Wk +pK Wk TV TV @@ -7914,8 +8035,8 @@ bb wz tQ El -Ms -MF +Wg +om MF eO Ly @@ -7941,10 +8062,10 @@ MF Ig uh qj -Ms -CZ -xz -Mk +tY +hX +MF +MF Bc KS Sp @@ -7968,10 +8089,10 @@ Hb vr dK Gb -Ms +yy +KH CZ xz -xz Mk iK sQ @@ -7993,13 +8114,13 @@ xz xz xz cR -Rx -bD -bD +Sd +Qi +bi +Ms CZ xz xz -xz Mk Er Mk @@ -8021,10 +8142,10 @@ xz xz xz xz -xz -xz -xz -xz +Rx +bD +bD +CZ xz xz xz diff --git a/_maps/shuttles/subshuttles/pgf_nail.dmm b/_maps/shuttles/subshuttles/pgf_nail.dmm index 9a5224dc4573..02255b90a5c0 100644 --- a/_maps/shuttles/subshuttles/pgf_nail.dmm +++ b/_maps/shuttles/subshuttles/pgf_nail.dmm @@ -391,7 +391,8 @@ icon_state = "0-4" }, /obj/machinery/porta_turret/ship/weak{ - dir = 8 + dir = 8; + mode = 1 }, /turf/open/floor/engine/hull/reinforced/interior, /area/ship/external/dark) diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm index c55604dcebdd..a37fda280ec0 100644 --- a/code/__DEFINES/achievements.dm +++ b/code/__DEFINES/achievements.dm @@ -24,6 +24,7 @@ #define MEDAL_SNAIL "KKKiiilll mmmeee" #define MEDAL_LOOKOUTSIR "Look Out, Sir!" #define MEDAL_GOTTEM "GOTTEM" +#define MEDAL_SPRINGLOCK "The Man Inside the Modsuit" //Skill medal hub IDs #define MEDAL_LEGENDARY_MINER "Legendary Miner" diff --git a/code/__DEFINES/actions.dm b/code/__DEFINES/actions.dm new file mode 100644 index 000000000000..ca2068106994 --- /dev/null +++ b/code/__DEFINES/actions.dm @@ -0,0 +1,20 @@ +///Action button checks if hands are unusable +#define AB_CHECK_HANDS_BLOCKED (1<<0) +///Action button checks if user is immobile +#define AB_CHECK_IMMOBILE (1<<1) +///Action button checks if user is resting +#define AB_CHECK_LYING (1<<2) +///Action button checks if user is conscious +#define AB_CHECK_CONSCIOUS (1<<3) + +///Action button triggered with right click +#define TRIGGER_SECONDARY_ACTION (1<<0) + +// Defines for formatting cooldown actions for the stat panel. +/// The stat panel the action is displayed in. +#define PANEL_DISPLAY_PANEL "panel" +/// The status shown in the stat panel. +/// Can be stuff like "ready", "on cooldown", "active", "charges", "charge cost", etc. +#define PANEL_DISPLAY_STATUS "status" +/// The name shown in the stat panel. +#define PANEL_DISPLAY_NAME "name" diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index ec5286ca34a4..22e69cd06d9f 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -33,6 +33,10 @@ /// Amount of air to take a from a tile #define BREATH_PERCENTAGE (BREATH_VOLUME/CELL_VOLUME) +/// This is the divisor which handles how much of the temperature difference between the current body temperature and 310.15K (optimal temperature) humans auto-regenerate each tick. The higher the number, the slower the recovery. This is applied each tick, so long as the mob is alive. +#define BODYTEMP_AUTORECOVERY_DIVISOR 28 +/// The natural temperature for a body +#define BODYTEMP_NORMAL 310.15 //EXCITED GROUPS /// number of FULL air controller ticks before an excited group breaks down (averages gas contents across turfs) diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 69886107d61c..6fa77815ff2b 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -168,3 +168,14 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(/obj/item/gun))) //We will round to this value in damage calculations. #define DAMAGE_PRECISION 0.1 + +/// Alternate attack defines. Return these at the end of procs like afterattack_secondary. +/// Calls the normal attack proc. For example, if returned in afterattack_secondary, will call afterattack. +/// Will continue the chain depending on the return value of the non-alternate proc, like with normal attacks. +#define SECONDARY_ATTACK_CALL_NORMAL 1 + +/// Cancels the attack chain entirely. +#define SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN 2 + +/// Proceed with the attack chain, but don't call the normal methods. +#define SECONDARY_ATTACK_CONTINUE_CHAIN 3 diff --git a/code/__DEFINES/dcs/signals/signals.dm b/code/__DEFINES/dcs/signals/signals.dm index 5552a60890d8..ecb2f4666dae 100644 --- a/code/__DEFINES/dcs/signals/signals.dm +++ b/code/__DEFINES/dcs/signals/signals.dm @@ -59,6 +59,14 @@ #define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE "atom_init_success" ///from base of atom/attackby(): (/obj/item, /mob/living, params) #define COMSIG_PARENT_ATTACKBY "atom_attackby" +/// From base of [/obj/item/proc/pre_attack_secondary()]: (atom/target, mob/user, params) +#define COMSIG_ITEM_PRE_ATTACK_SECONDARY "item_pre_attack_secondary" + #define COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN (1<<0) + #define COMPONENT_SECONDARY_CONTINUE_ATTACK_CHAIN (1<<1) + #define COMPONENT_SECONDARY_CALL_NORMAL_ATTACK_CHAIN (1<<2) +#define COMSIG_PARENT_ATTACKBY_SECONDARY "atom_attackby_secondary" +/// From base of [/atom/proc/attack_hand_secondary]: (mob/user, list/modifiers) - Called when the atom receives a secondary unarmed attack. +#define COMSIG_ATOM_ATTACK_HAND_SECONDARY "atom_attack_hand_secondary" ///Return this in response if you don't want afterattack to be called #define COMPONENT_NO_AFTERATTACK (1<<0) ///from base of atom/attack_hulk(): (/mob/living/carbon/human) @@ -158,6 +166,7 @@ ///from internal loop in atom/movable/proc/CanReach(): (list/next) #define COMSIG_ATOM_CANREACH "atom_can_reach" #define COMPONENT_BLOCK_REACH 1 + #define COMPONENT_ALLOW_REACH (1<<0) ///for when an atom has been created through processing (atom/original_atom, list/chosen_processing_option) #define COMSIG_ATOM_CREATEDBY_PROCESSING "atom_createdby_processing" @@ -337,6 +346,37 @@ #define COMSIG_MOVABLE_LIGHT_OVERLAY_TOGGLE_ON "movable_light_overlay_toggle_on" ///called when the movable's glide size is updated: (new_glide_size) #define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" +/// from base of atom/movable/Process_Spacemove(): (movement_dir, continuous_move) +#define COMSIG_MOVABLE_SPACEMOVE "spacemove" + #define COMSIG_MOVABLE_STOP_SPACEMOVE (1<<0) + ///from datum/component/drift/apply_initial_visuals(): () +#define COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT "movable_drift_visual_attempt" + #define DRIFT_VISUAL_FAILED (1<<0) + ///from datum/component/drift/allow_final_movement(): () +#define COMSIG_MOVABLE_DRIFT_BLOCK_INPUT "movable_drift_block_input" + #define DRIFT_ALLOW_INPUT (1<<0) + +///signal sent out by an atom when it checks if it can be pulled, for additional checks +#define COMSIG_ATOM_CAN_BE_PULLED "movable_can_be_pulled" + #define COMSIG_ATOM_CANT_PULL (1 << 0) +///signal sent out by an atom when it is no longer being pulled by something else +#define COMSIG_ATOM_NO_LONGER_PULLED "movable_no_longer_pulled" +///signal sent out by an atom when it is no longer pulling something : (atom/pulling) +#define COMSIG_ATOM_NO_LONGER_PULLING "movable_no_longer_pulling" +///called on /living, when pull is attempted, but before it completes, from base of [/mob/living/start_pulling]: (atom/movable/thing, force) +#define COMSIG_LIVING_TRY_PULL "living_try_pull" + #define COMSIG_LIVING_CANCEL_PULL (1 << 0) +/// Called from /mob/living/update_pull_movespeed +#define COMSIG_LIVING_UPDATING_PULL_MOVESPEED "living_updating_pull_movespeed" +/// Called from /mob/living/PushAM -- Called when this mob is about to push a movable, but before it moves +/// (aotm/movable/being_pushed) +#define COMSIG_LIVING_PUSHING_MOVABLE "living_pushing_movable" +///from base of [/atom/proc/interact]: (mob/user) +#define COMSIG_ATOM_UI_INTERACT "atom_ui_interact" +///called on /living when attempting to pick up an item, from base of /mob/living/put_in_hand_check(): (obj/item/I) +#define COMSIG_LIVING_TRY_PUT_IN_HAND "living_try_put_in_hand" + /// Can't pick up + #define COMPONENT_LIVING_CANT_PUT_IN_HAND (1<<0) // /mob signals @@ -358,6 +398,9 @@ #define COMSIG_MOB_ALTCLICKON "mob_altclickon" #define COMSIG_MOB_CANCEL_CLICKON (1<<0) +///From base of mob/living/MobBump() (mob/living) +#define COMSIG_LIVING_MOB_BUMP "living_mob_bump" + ///from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj #define COMSIG_MOB_ALLOWED "mob_allowed" ///from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources) @@ -549,13 +592,6 @@ // /obj/mecha signals #define COMSIG_MECHA_ACTION_ACTIVATE "mecha_action_activate" //sent from mecha action buttons to the mecha they're linked to -// /mob/living/carbon/human signals -#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity) -#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity) -#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) -#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted) -#define COMSIG_JOB_RECEIVED "job_received" //Whenever EquipRanked is called, called after job is set - // /datum/species signals #define COMSIG_SPECIES_GAIN "species_gain" //from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species) #define COMSIG_SPECIES_LOSS "species_loss" //from datum/species/on_species_loss(): (datum/species/lost_species) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm new file mode 100644 index 000000000000..2428eddf1346 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -0,0 +1,82 @@ +///Called from /mob/living/carbon/help_shake_act, before any hugs have ocurred. (mob/living/helper) +#define COMSIG_CARBON_PRE_HELP_ACT "carbon_pre_help" + /// Stops the rest of help act (hugging, etc) from occuring + #define COMPONENT_BLOCK_HELP_ACT (1<<0) + +///Called from /mob/living/carbon/help_shake_act on the person being helped, after any hugs have ocurred. (mob/living/helper) +#define COMSIG_CARBON_HELP_ACT "carbon_help" +///Called from /mob/living/carbon/help_shake_act on the helper, after any hugs have ocurred. (mob/living/helped) +#define COMSIG_CARBON_HELPED "carbon_helped_someone" + +///Before a carbon mob is shoved, sent to the turf we're trying to shove onto (mob/living/carbon/shover, mob/living/carbon/target) +#define COMSIG_CARBON_DISARM_PRESHOVE "carbon_disarm_preshove" + #define COMSIG_CARBON_ACT_SOLID (1<<0) //Tells disarm code to act as if the mob was shoved into something solid, even we we're not +///When a carbon mob is disarmed, this is sent to the turf we're trying to shove onto (mob/living/carbon/shover, mob/living/carbon/target, shove_blocked) +#define COMSIG_CARBON_DISARM_COLLIDE "carbon_disarm_collision" + #define COMSIG_CARBON_SHOVE_HANDLED (1<<0) + +// /mob/living/carbon physiology signals +#define COMSIG_CARBON_GAIN_WOUND "carbon_gain_wound" //from /datum/wound/proc/apply_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L) +#define COMSIG_CARBON_LOSE_WOUND "carbon_lose_wound" //from /datum/wound/proc/remove_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L) +///from base of /obj/item/bodypart/proc/attach_limb(): (new_limb, special) allows you to fail limb attachment +#define COMSIG_CARBON_ATTACH_LIMB "carbon_attach_limb" +#define COMSIG_CARBON_REMOVE_LIMB "carbon_remove_limb" //from base of /obj/item/bodypart/proc/drop_limb(lost_limb, dismembered) +#define COMSIG_BODYPART_GAUZED "bodypart_gauzed" // from /obj/item/bodypart/proc/apply_gauze(/obj/item/stack/gauze) +#define COMSIG_BODYPART_GAUZE_DESTROYED "bodypart_degauzed" // from [/obj/item/bodypart/proc/seep_gauze] when it runs out of absorption + +///Called when someone attempts to cuff a carbon +#define COMSIG_CARBON_CUFF_ATTEMPTED "carbon_attempt_cuff" +///Called when a carbon mutates (source = dna, mutation = mutation added) +#define COMSIG_CARBON_GAIN_MUTATION "carbon_gain_mutation" +///Called when a carbon loses a mutation (source = dna, mutation = mutation lose) +#define COMSIG_CARBON_LOSE_MUTATION "carbon_lose_mutation" +///Called when a carbon becomes addicted (source = what addiction datum, addicted_mind = mind of the addicted carbon) +#define COMSIG_CARBON_GAIN_ADDICTION "carbon_gain_addiction" +///Called when a carbon is no longer addicted (source = what addiction datum was lost, addicted_mind = mind of the freed carbon) +#define COMSIG_CARBON_LOSE_ADDICTION "carbon_lose_addiction" +///Called when a carbon gets a brain trauma (source = carbon, trauma = what trauma was added) - this is before on_gain() +#define COMSIG_CARBON_GAIN_TRAUMA "carbon_gain_trauma" +///Called when a carbon loses a brain trauma (source = carbon, trauma = what trauma was removed) +#define COMSIG_CARBON_LOSE_TRAUMA "carbon_lose_trauma" +///Called when a carbon updates their health (source = carbon) +#define COMSIG_CARBON_HEALTH_UPDATE "carbon_health_update" +///Called when a carbon updates their sanity (source = carbon) +#define COMSIG_CARBON_SANITY_UPDATE "carbon_sanity_update" +///Called when a carbon breathes, before the breath has actually occured +#define COMSIG_CARBON_PRE_BREATHE "carbon_pre_breathe" +///Called when a carbon updates their mood +#define COMSIG_CARBON_MOOD_UPDATE "carbon_mood_update" + +// /mob/living/carbon/human signals + +///Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted) +#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" +///Whenever EquipRanked is called, called after job is set +#define COMSIG_JOB_RECEIVED "job_received" +///from /mob/living/carbon/human/proc/set_coretemperature(): (oldvalue, newvalue) +#define COMSIG_HUMAN_CORETEMP_CHANGE "human_coretemp_change" +///from /datum/species/handle_fire. Called when the human is set on fire and burning clothes and stuff +#define COMSIG_HUMAN_BURNING "human_burning" +///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers) +#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack" +///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers) +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" +//from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" +//from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration) +#define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields" + #define SHIELD_BLOCK (1<<0) + +// Mob transformation signals +///Called when a human turns into a monkey, from /mob/living/carbon/proc/finish_monkeyize() +#define COMSIG_HUMAN_MONKEYIZE "human_monkeyize" +///Called when a monkey turns into a human, from /mob/living/carbon/proc/finish_humanize(species) +#define COMSIG_MONKEY_HUMANIZE "monkey_humanize" + +///From mob/living/carbon/human/suicide() +#define COMSIG_HUMAN_SUICIDE_ACT "human_suicide_act" + +/// A mob has just equipped an item. Called on [/mob] from base of [/obj/item/equipped()]: (/obj/item/equipped_item, slot) +#define COMSIG_MOB_EQUIPPED_ITEM "mob_equipped_item" +/// A mob has just unequipped an item. +#define COMSIG_MOB_UNEQUIPPED_ITEM "mob_unequipped_item" diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm new file mode 100644 index 000000000000..e5c27a902a65 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mod.dm @@ -0,0 +1,25 @@ +//MODsuit signals +/// Called when a module is selected to be the active one from on_select(obj/item/mod/module/module) +#define COMSIG_MOD_MODULE_SELECTED "mod_module_selected" +/// Called when a MOD activation is called from toggle_activate(mob/user) +#define COMSIG_MOD_ACTIVATE "mod_activate" + /// Cancels the suit's activation + #define MOD_CANCEL_ACTIVATE (1 << 0) +/// Called when a MOD is having modules removed from crowbar_act(mob/user, obj/crowbar) +#define COMSIG_MOD_MODULE_REMOVAL "mod_module_removal" + /// Cancels the removal of modules + #define MOD_CANCEL_REMOVAL (1 << 0) +/// Called when a module attempts to activate, however it does. At the end of checks so you can add some yourself, or work on trigger behavior (mob/user) +#define COMSIG_MODULE_TRIGGERED "mod_module_triggered" + // Cancels activation, with no message. include feedback on your cancel. + #define MOD_ABORT_USE (1<<0) +/// Called when a module activates, after all checks have passed and cooldown started. +#define COMSIG_MODULE_ACTIVATED "mod_module_activated" +/// Called when a module deactivates, after all checks have passed. +#define COMSIG_MODULE_DEACTIVATED "mod_module_deactivated" +/// Called when a module is used, after all checks have passed and cooldown started. +#define COMSIG_MODULE_USED "mod_module_used" +/// Called when the MODsuit wearer is set. +#define COMSIG_MOD_WEARER_SET "mod_wearer_set" +/// Called when the MODsuit wearer is unset. +#define COMSIG_MOD_WEARER_UNSET "mod_wearer_unset" diff --git a/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm b/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm index 536efc724f92..1a562b84a4a1 100644 --- a/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm +++ b/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm @@ -13,7 +13,8 @@ ///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) #define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" ///from base of obj/item/hit_reaction(): (list/args) -#define COMSIG_ITEM_HIT_REACT "item_hit_react" +#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args) + #define COMPONENT_HIT_REACTION_BLOCK (1<<0) #define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) #define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob) diff --git a/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm b/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm index 137925811720..136b73ffb3d2 100644 --- a/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm @@ -10,3 +10,5 @@ #define COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH "obj_default_unfasten_wrench" ///from base of /turf/proc/levelupdate(). (intact) true to hide and false to unhide #define COMSIG_OBJ_HIDE "obj_hide" +/// from base of [/atom/proc/obj_destruction]: (damage_flag) +#define COMSIG_OBJ_DESTRUCTION "obj_destruction" diff --git a/code/__DEFINES/dcs/signals/signals_storage.dm b/code/__DEFINES/dcs/signals/signals_storage.dm new file mode 100644 index 000000000000..456ac3c0781a --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_storage.dm @@ -0,0 +1,4 @@ +/// Sent when /datum/storage/dump_content_at(): (obj/item/storage_source, mob/user) +#define COMSIG_STORAGE_DUMP_CONTENT "storage_dump_contents" + /// Return to stop the standard dump behavior. + #define STORAGE_DUMP_HANDLED (1<<0) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index c27a78ffd2de..00093c8ecf72 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -46,6 +46,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define SHOW_BEHIND_LARGE_ICONS_1 (1<<12) /// Should we use the initial icon for display? Mostly used by overlay only objects #define HTML_USE_INITAL_ICON_1 (1<<20) +// Whether or not this atom is storing contents for a disassociated storage object +#define HAS_DISASSOCIATED_STORAGE_1 (1<<24) // Update flags for [/atom/proc/update_appearance] /// Update the atom's name diff --git a/code/__DEFINES/generators.dm b/code/__DEFINES/generators.dm new file mode 100644 index 000000000000..e9d373c9a65d --- /dev/null +++ b/code/__DEFINES/generators.dm @@ -0,0 +1,15 @@ +//generator types +#define GEN_NUM "num" +#define GEN_VECTOR "vector" +#define GEN_BOX "box" +#define GEN_COLOR "color" +#define GEN_CIRCLE "circle" +#define GEN_SPHERE "sphere" +#define GEN_SQUARE "square" +#define GEN_CUBE "cube" + +///particle editor var modifiers +#define P_DATA_GENERATOR "generator" +#define P_DATA_ICON_ADD "icon_add" +#define P_DATA_ICON_REMOVE "icon_remove" +#define P_DATA_ICON_WEIGHT "icon_edit" diff --git a/code/__DEFINES/guns.dm b/code/__DEFINES/guns.dm index 17cbab5683d8..01cfa1e5bd5c 100644 --- a/code/__DEFINES/guns.dm +++ b/code/__DEFINES/guns.dm @@ -24,6 +24,9 @@ ///Gun has a bolt, it locks back when empty. It can be released to chamber a round if a magazine is in. /// Example: Pistols with a slide lock, some SMGs #define BOLT_TYPE_LOCKING 4 +///Gun has an HK-style locking charging handle, so you can slap it. Only use this for flavor, otherwise modern-style automatics should use BOLT_TYPE_LOCKING. +/// Example: everything made by lanchester +#define BOLT_TYPE_CLIP 5 //Sawn off nerfs ///accuracy penalty of sawn off guns #define SAWN_OFF_ACC_PENALTY 25 @@ -61,6 +64,7 @@ #define MANUFACTURER_BRAZIL "a green flag with a blue circle and a yellow diamond around it" #define MANUFACTURER_INTEQ "an orange crest with the letters 'IRMG'" #define MANUFACTURER_MINUTEMAN "the Lanchester City Firearms Plant logo" +#define MANUFACTURER_MINUTEMAN_LASER "the Clover Photonics logo" #define MANUFACTURER_DONKCO "the Donk! Co. logo" #define MANUFACTURER_PGF "the Etherbor Industries emblem" #define MANUFACTURER_IMPORT "Lanchester Import Co." diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index 64aa6aa52627..6cad7078b6ec 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -46,6 +46,7 @@ #define HIDEFACIALHAIR (1<<9) #define HIDENECK (1<<10) #define HIDEHORNS (1<<11) // Used for hiding Sarathi horns. +#define HIDESNOUT (1<<11) //bitflags for clothing coverage - also used for limbs #define HEAD (1<<0) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 241136c297b9..9659ac17c64b 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -8,6 +8,8 @@ #define isweakref(D) (istype(D, /datum/weakref)) +#define isgenerator(A) (istype(A, /generator)) + //Turfs //#define isturf(A) (istype(A, /turf)) This is actually a byond built-in. Added here for completeness sake. @@ -166,6 +168,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isitem(A) (istype(A, /obj/item)) +#define isstack(A) (istype(A, /obj/item/stack)) + #define isgrenade(A) (istype(A, /obj/item/grenade)) #define islandmine(A) (istype(A, /obj/item/mine)) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index d021558901f4..bd9b0f0063bb 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -1,5 +1,6 @@ //Defines for atom layers and planes //KEEP THESE IN A NICE ACSCENDING ORDER, PLEASE +#define LOWEST_EVER_PLANE -100 #define CLICKCATCHER_PLANE -99 @@ -21,6 +22,20 @@ #define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals #define BLACKNESS_PLANE_RENDER_TARGET "BLACKNESS_PLANE" +#define ABOVE_GAME_PLANE 1 + +//-------------------- Rendering --------------------- +#define RENDER_PLANE_GAME 100 +#define RENDER_PLANE_NON_GAME 101 +#define RENDER_PLANE_MASTER 102 + +// Lummox I swear to god I will find you +// NOTE! You can only ever have planes greater then -10000, if you add too many with large offsets you will brick multiz +// Same can be said for large multiz maps. Tread carefully mappers +#define HIGHEST_EVER_PLANE RENDER_PLANE_MASTER +/// The range unique planes can be in +#define PLANE_RANGE (HIGHEST_EVER_PLANE - LOWEST_EVER_PLANE) + #define SPACE_LAYER 1.8 //#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define #define MID_TURF_LAYER 2.02 diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index 719f06f2a812..a442ddb464b8 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -290,3 +290,11 @@ /// Like SPT_PROB_RATE but easier to use, simply put `if(SPT_PROB(10, 5))` #define SPT_PROB(prob_per_second_percent, seconds_per_tick) (prob(100*SPT_PROB_RATE((prob_per_second_percent)/100, (seconds_per_tick)))) + +/// Converts a probability/second chance to probability/delta_time chance +/// For example, if you want an event to happen with a 10% per second chance, but your proc only runs every 5 seconds, do `if(prob(100*DT_PROB_RATE(0.1, 5)))` +#define DT_PROB_RATE(prob_per_second, delta_time) (1 - (1 - (prob_per_second)) ** (delta_time)) + +/// Like DT_PROB_RATE but easier to use, simply put `if(DT_PROB(10, 5))` +#define DT_PROB(prob_per_second_percent, delta_time) (prob(100*DT_PROB_RATE((prob_per_second_percent)/100, (delta_time)))) +// ) diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm new file mode 100644 index 000000000000..29a450eceb91 --- /dev/null +++ b/code/__DEFINES/mod.dm @@ -0,0 +1,40 @@ +/// Default value for the max_complexity var on MODsuits +#define DEFAULT_MAX_COMPLEXITY 15 + +/// Default cell drain per process on MODsuits +#define DEFAULT_CHARGE_DRAIN 0.09 + +/// Default time for a part to seal +#define MOD_ACTIVATION_STEP_TIME (2 SECONDS) + +/// Passive module, just acts when put in naturally. +#define MODULE_PASSIVE 0 +/// Usable module, does something when you press a button. +#define MODULE_USABLE 1 +/// Toggle module, you turn it on/off and it does stuff. +#define MODULE_TOGGLE 2 +/// Actively usable module, you may only have one selected at a time. +#define MODULE_ACTIVE 3 + +//Defines used by the theme for clothing flags and similar +#define CONTROL_LAYER "control_layer" +#define HELMET_FLAGS "helmet_flags" +#define CHESTPLATE_FLAGS "chestplate_flags" +#define GAUNTLETS_FLAGS "gauntlets_flags" +#define BOOTS_FLAGS "boots_flags" + +#define UNSEALED_LAYER "unsealed_layer" +#define UNSEALED_CLOTHING "unsealed_clothing" +#define SEALED_CLOTHING "sealed_clothing" +#define UNSEALED_INVISIBILITY "unsealed_invisibility" +#define SEALED_INVISIBILITY "sealed_invisibility" +#define UNSEALED_COVER "unsealed_cover" +#define SEALED_COVER "sealed_cover" +#define CAN_OVERSLOT "can_overslot" + +//Defines used to override MOD clothing's icon and worn icon files in the skin. +#define MOD_ICON_OVERRIDE "mod_icon_override" +#define MOD_WORN_ICON_OVERRIDE "mod_worn_icon_override" + +/// Global list of all /datum/mod_theme +GLOBAL_LIST_INIT(mod_themes, setup_mod_themes()) diff --git a/code/__DEFINES/particles.dm b/code/__DEFINES/particles.dm new file mode 100644 index 000000000000..5657566a63bb --- /dev/null +++ b/code/__DEFINES/particles.dm @@ -0,0 +1,5 @@ +// /obj/effect/abstract/particle_holder/var/particle_flags +// Flags that effect how a particle holder displays something + +/// If we're inside something inside a mob, display off that mob too +#define PARTICLE_ATTACH_MOB (1<<0) diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 1bd23038c600..285e7ce5ff80 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -174,4 +174,36 @@ #define SOUND_AREA_ICEMOON SOUND_ENVIRONMENT_CAVE #define SOUND_AREA_WOODFLOOR SOUND_ENVIRONMENT_CITY +/// List of all of our sound keys. +#define SFX_BODYFALL "bodyfall" +#define SFX_BULLET_MISS "bullet_miss" +#define SFX_CAN_OPEN "can_open" +#define SFX_CLOWN_STEP "clown_step" +#define SFX_DESECRATION "desecration" +#define SFX_EXPLOSION "explosion" +#define SFX_EXPLOSION_CREAKING "explosion_creaking" +#define SFX_HISS "hiss" +#define SFX_HONKBOT_E "honkbot_e" +#define SFX_HULL_CREAKING "hull_creaking" +#define SFX_HYPERTORUS_CALM "hypertorus_calm" +#define SFX_HYPERTORUS_MELTING "hypertorus_melting" +#define SFX_IM_HERE "im_here" +#define SFX_LAW "law" +#define SFX_PAGE_TURN "page_turn" +#define SFX_PUNCH "punch" +#define SFX_REVOLVER_SPIN "revolver_spin" +#define SFX_RICOCHET "ricochet" +#define SFX_RUSTLE "rustle" +#define SFX_SHATTER "shatter" +#define SFX_SM_CALM "sm_calm" +#define SFX_SM_DELAM "sm_delam" +#define SFX_SPARKS "sparks" +#define SFX_SUIT_STEP "suit_step" +#define SFX_SWING_HIT "swing_hit" +#define SFX_TERMINAL_TYPE "terminal_type" +#define SFX_WARPSPEED "warpspeed" +#define SFX_CRUNCHY_BUSH_WHACK "crunchy_bush_whack" +#define SFX_TREE_CHOP "tree_chop" +#define SFX_ROCK_TAP "rock_tap" + #define SOUND_EMPTY_MAG 'sound/weapons/empty.ogg' diff --git a/code/__DEFINES/species_clothing_defines.dm b/code/__DEFINES/species_clothing_defines.dm index 517579192634..937adf6b7cdd 100644 --- a/code/__DEFINES/species_clothing_defines.dm +++ b/code/__DEFINES/species_clothing_defines.dm @@ -22,6 +22,7 @@ #define VOX_SUIT_PATH 'icons/mob/species/vox/onmob_suit_vox.dmi' #define VOX_EARS_PATH 'icons/mob/species/vox/onmob_ears_vox.dmi' #define VOX_MASK_PATH 'icons/mob/species/vox/onmob_mask_vox.dmi' +#define VOX_NECK_PATH 'icons/mob/species/vox/onmob_neck_vox.dmi' #define VOX_HEAD_PATH 'icons/mob/species/vox/onmob_head_vox.dmi' #define VOX_GLOVES_PATH 'icons/mob/species/vox/onmob_hands_vox.dmi' #define VOX_GLASSES_PATH 'icons/mob/species/vox/onmob_eyes_vox.dmi' diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index d1fbf26616d5..68913e3925fd 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -99,6 +99,14 @@ #define STATUS_EFFECT_METAB_FROZEN /datum/status_effect/metab_frozen // Affected cannot process chems +//Incapacitated status effect flags +/// If the incapacitated status effect will ignore a mob in restraints (handcuffs) +#define IGNORE_RESTRAINTS (1<<0) +/// If the incapacitated status effect will ignore a mob in stasis (stasis beds) +#define IGNORE_STASIS (1<<1) +/// If the incapacitated status effect will ignore a mob being agressively grabbed +#define IGNORE_GRAB (1<<2) + ///////////// // NEUTRAL // ///////////// diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 7167d7faeca2..70066746cd09 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -180,7 +180,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_VIRUSIMMUNE "virus_immunity" #define TRAIT_PIERCEIMMUNE "pierce_immunity" #define TRAIT_NODISMEMBER "dismember_immunity" +#define TRAIT_LAVA_IMMUNE "lava_immunity" +#define TRAIT_SNOWSTORM_IMMUNE "snow_immunity" +#define TRAIT_ASHSTORM_IMMUNE "ash_immunity" +#define TRAIT_SANDSTORM_IMMUNE "sand_immunity" #define TRAIT_NOFIRE "nonflammable" +/// Prevents plasmamen from self-igniting if only their helmet is missing +#define TRAIT_NOSELFIGNITION_HEAD_ONLY "no_selfignition_head_only" #define TRAIT_NOGUNS "no_guns" #define TRAIT_NOHUNGER "no_hunger" #define TRAIT_NOMETABOLISM "no_metabolism" @@ -216,6 +222,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NOMOBSWAP "no-mob-swap" #define TRAIT_XRAY_VISION "xray_vision" #define TRAIT_THERMAL_VISION "thermal_vision" +/// Like antimagic, but doesn't block the user from casting +#define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock" /// We have some form of forced gravity acting on us #define TRAIT_FORCED_GRAVITY "forced_gravity" #define TRAIT_ABDUCTOR_TRAINING "abductor-training" @@ -250,6 +258,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_GAMERGOD "gamer-god" //double arcade prizes #define TRAIT_GIANT "giant" #define TRAIT_DWARF "dwarf" +#define TRAIT_FASTMED "fast_med_use" #define TRAIT_SILENT_FOOTSTEPS "silent_footsteps" //makes your footsteps completely silent #define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good.... /// The holder of this trait has antennae or whatever that hurt a ton when noogied @@ -281,6 +290,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_AREA_SENSITIVE "area-sensitive" ///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags] +///every object that is currently the active storage of some client mob has this trait +#define TRAIT_ACTIVE_STORAGE "active_storage" + #define TRAIT_KEEP_TOGETHER "keep-together" // item traits @@ -364,6 +376,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define LYING_DOWN_TRAIT "lying-down" /// Trait associated to lacking electrical power. #define POWER_LACK_TRAIT "power-lack" +/// Trait applied by MODsuits. +#define MOD_TRAIT "mod" // unique trait sources, still defines #define CLONING_POD_TRAIT "cloning-pod" @@ -437,6 +451,31 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define BEAUTY_ELEMENT_TRAIT "beauty_element" #define MOOD_COMPONENT_TRAIT "mood_component" +// mobility flag traits +// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) +// BUT FOR NOW, THESE ARE HOOKED TO DO update_mobility() VIA COMSIG IN living_mobility.dm +// SO IF YOU ADD MORE, BESURE TO UPDATE IT THERE. + +/// Disallow movement +#define TRAIT_MOBILITY_NOMOVE "mobility_nomove" +/// Disallow pickup +#define TRAIT_MOBILITY_NOPICKUP "mobility_nopickup" +/// Disallow item use +#define TRAIT_MOBILITY_NOUSE "mobility_nouse" +///Disallow resting/unresting +#define TRAIT_MOBILITY_NOREST "mobility_norest" + +#define TRAIT_FORCED_STANDING "forcedstanding" + +///Movement type traits for movables. See elements/movetype_handler.dm +#define TRAIT_MOVE_GROUND "move_ground" +#define TRAIT_MOVE_FLYING "move_flying" +#define TRAIT_MOVE_VENTCRAWLING "move_ventcrawling" +#define TRAIT_MOVE_FLOATING "move_floating" +#define TRAIT_MOVE_PHASING "move_phasing" +/// Disables the floating animation. See above. +#define TRAIT_NO_FLOATING_ANIM "no-floating-animation" + /// Trait granted by [mob/living/silicon/ai] /// Applied when the ai anchors itself #define AI_ANCHOR_TRAIT "ai_anchor" diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 5d9522b18ee4..602473b6086d 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -92,6 +92,7 @@ #define VV_HK_AUTO_RENAME "auto_rename" #define VV_HK_RADIATE "radiate" #define VV_HK_EDIT_FILTERS "edit_filters" +#define VV_HK_EDIT_PARTICLES "edit_particles" // /obj #define VV_HK_OSAY "osay" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index f603e85292ad..435b83e29797 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -109,13 +109,25 @@ return "[output][and_text][input[index]]" -//Checks for specific types in a list -/proc/is_type_in_list(atom/A, list/L) - if(!LAZYLEN(L) || !A) +/** + * Checks for specific types in a list. + * + * If using zebra mode the list should be an assoc list with truthy/falsey values. + * The check short circuits so earlier entries in the input list will take priority. + * Ergo, subtypes should come before parent types. + * Notice that this is the opposite priority of [/proc/typecacheof]. + * + * Arguments: + * - [type_to_check][/datum]: An instance to check. + * - [list_to_check][/list]: A list of typepaths to check the type_to_check against. + * - zebra: Whether to use the value of the matching type in the list instead of just returning true when a match is found. + */ +/proc/is_type_in_list(datum/type_to_check, list/list_to_check, zebra = FALSE) + if(!LAZYLEN(list_to_check) || !type_to_check) return FALSE - for(var/type in L) - if(istype(A, type)) - return TRUE + for(var/type in list_to_check) + if(istype(type_to_check, type)) + return !zebra || list_to_check[type] // Subtypes must come first in zebra lists. return FALSE //Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches') diff --git a/code/__HELPERS/_planes.dm b/code/__HELPERS/_planes.dm new file mode 100644 index 000000000000..d8306c356d4c --- /dev/null +++ b/code/__HELPERS/_planes.dm @@ -0,0 +1,80 @@ +// This file contains helper macros for plane operations +// See the planes section of Visuals.md for more detail, but essentially +// When we render multiz, we do it by placing all atoms on lower levels on well, lower planes +// This is done with stacks of plane masters (things we use to apply effects to planes) +// These macros exist to facilitate working with this system, and other associated small bits + +/// Takes an atom to change the plane of, a new plane value, and something that can be used as a reference to a z level as input +/// Modifies the new value to match the plane we actually want. Note, if you pass in an already offset plane the offsets will add up +/// Use PLANE_TO_TRUE() to avoid this +#define SET_PLANE(thing, new_value, z_reference) (thing.plane = MUTATE_PLANE(new_value, z_reference)) + +/// Takes a plane and a z reference, and offsets the plane by the mutation +/// The SSmapping.max_plane_offset bit here is technically redundant, but saves a bit of work in the base case +/// And the base case is important to me. Non multiz shouldn't get hit too bad by this code +#define MUTATE_PLANE(new_value, z_reference) ((SSmapping.max_plane_offset) ? GET_NEW_PLANE(new_value, GET_TURF_PLANE_OFFSET(z_reference)) : (new_value)) + +/// Takes a z reference that we are unsure of, sanity checks it +/// Returns either its offset, or 0 if it's not a valid ref +#define GET_TURF_PLANE_OFFSET(z_reference) ((SSmapping.max_plane_offset && isatom(z_reference)) ? GET_Z_PLANE_OFFSET(z_reference.z) : 0) +/// Essentially just an unsafe version of GET_TURF_PLANE_OFFSET() +/// Takes a z value we returns its offset with a list lookup +/// Will runtime during parts of init. Be careful :) +#define GET_Z_PLANE_OFFSET(z) (SSmapping.z_level_to_plane_offset[z]) + +/// Takes a plane to offset, and the multiplier to use, and well, does the offsetting +/// Respects a blacklist we use to remove redundant plane masters, such as hud objects +#define GET_NEW_PLANE(new_value, multiplier) (SSmapping.plane_offset_blacklist?["[new_value]"] ? new_value : (new_value) - (PLANE_RANGE * (multiplier))) + +// Now for the more niche things + +/// Takes an object, new plane, and multipler, and offsets the plane +/// This is for cases where you have a multipler precalculated, and just want to use it +/// Often an optimization, sometimes a necessity +#define SET_PLANE_W_SCALAR(thing, new_value, multiplier) (thing.plane = GET_NEW_PLANE(new_value, multiplier)) + + +/// Implicit plane set. We take the turf from the object we're changing the plane of, and use ITS z as a spokesperson for our plane value +#define SET_PLANE_IMPLICIT(thing, new_value) SET_PLANE_EXPLICIT(thing, new_value, thing) + +// This is an unrolled and optimized version of SET_PLANE, for use anywhere where you are unsure of a source's "turfness" +// The plane is cached to allow for fancy stuff to be eval'd once, rather then often +#define SET_PLANE_EXPLICIT(thing, new_value, source) \ + do {\ + if(SSmapping.max_plane_offset) {\ + var/_cached_plane = new_value;\ + var/turf/_our_turf = get_turf(source);\ + if(_our_turf){\ + thing.plane = GET_NEW_PLANE(_cached_plane, GET_Z_PLANE_OFFSET(_our_turf.z));\ + }\ + }\ + else {\ + thing.plane = new_value;\ + }\ + }\ + while (FALSE) + +// Now for macros that exist to get info from SSmapping +// Mostly about details of planes, or z levels + +/// Takes a z level, gets the lowest plane offset in its "stack" +#define GET_LOWEST_STACK_OFFSET(z) ((SSmapping.max_plane_offset) ? SSmapping.z_level_to_lowest_plane_offset[z] : 0) +/// Takes a plane, returns the canonical, unoffset plane it represents +#define PLANE_TO_TRUE(plane) ((SSmapping.plane_offset_to_true) ? SSmapping.plane_offset_to_true["[plane]"] : plane) +/// Takes a plane, returns the offset it uses +#define PLANE_TO_OFFSET(plane) ((SSmapping.plane_to_offset) ? SSmapping.plane_to_offset["[plane]"] : plane) +/// Takes a plane, returns TRUE if it is of critical priority, FALSE otherwise +#define PLANE_IS_CRITICAL(plane) ((SSmapping.plane_to_offset) ? !!SSmapping.critical_planes["[plane]"] : FALSE) +/// Takes a true plane, returns the offset planes that would canonically represent it +#define TRUE_PLANE_TO_OFFSETS(plane) ((SSmapping.true_to_offset_planes) ? SSmapping.true_to_offset_planes["[plane]"] : list(plane)) +/// Takes a render target and an offset, returns a canonical render target string for it +#define OFFSET_RENDER_TARGET(render_target, offset) (_OFFSET_RENDER_TARGET(render_target, SSmapping.render_offset_blacklist?["[render_target]"] ? 0 : offset)) +/// Helper macro for the above +/// Honestly just exists to make the pattern of render target strings more readable +#define _OFFSET_RENDER_TARGET(render_target, offset) ("[(render_target)] #[(offset)]") + +// Known issues: +// Potentially too much client load? Hard to tell due to not having a potato pc to hand. +// This is solvable with lowspec preferences, which would not be hard to implement +// Player popups will now render their effects, like overlay lights. this is fixable, but I've not gotten to it +// I think overlay lights can render on the wrong z layer. s fucked diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm new file mode 100644 index 000000000000..dbb42122ff45 --- /dev/null +++ b/code/__HELPERS/atoms.dm @@ -0,0 +1,9 @@ +///Returns the src and all recursive contents as a list. +/atom/proc/get_all_contents(ignore_flag_1) + . = list(src) + var/i = 0 + while(i < length(.)) + var/atom/checked_atom = .[++i] + if(checked_atom.flags_1 & ignore_flag_1) + continue + . += checked_atom.contents diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index e7af7f31884d..34cacd872d42 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -596,3 +596,31 @@ block( \ continue C.energy_fail(rand(duration_min,duration_max)) + +///Returns a list of turfs around a center based on RANGE_TURFS() +/proc/circle_range_turfs(center = usr, radius = 3) + + var/turf/center_turf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius + 0.5) + + for(var/turf/checked_turf as anything in RANGE_TURFS(radius, center_turf)) + var/dx = checked_turf.x - center_turf.x + var/dy = checked_turf.y - center_turf.y + if(dx * dx + dy * dy <= rsq) + turfs += checked_turf + return turfs + +///Returns a list of turfs around a center based on view() +/proc/circle_view_turfs(center=usr,radius=3) //Is there even a diffrence between this proc and circle_range_turfs()? + + var/turf/center_turf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius + 0.5) + + for(var/turf/checked_turf in view(radius, center_turf)) + var/dx = checked_turf.x - center_turf.x + var/dy = checked_turf.y - center_turf.y + if(dx * dx + dy * dy <= rsq) + turfs += checked_turf + return turfs diff --git a/code/__HELPERS/generators.dm b/code/__HELPERS/generators.dm new file mode 100644 index 000000000000..d50df7deba19 --- /dev/null +++ b/code/__HELPERS/generators.dm @@ -0,0 +1,11 @@ +/** + * returns the arguments given to a generator and manually extracts them from the internal byond object + * returns: + * * flat list of strings for args given to the generator. + * * Note: this means things like "list(1,2,3)" will need to be processed + */ +/proc/return_generator_args(generator/target) + var/string_repr = "[target]" //the name of the generator is the string representation of it's _binobj, which also contains it's args + string_repr = copytext(string_repr, 11, length(string_repr)) // strips extraneous data + string_repr = replacetext(string_repr, "\"", "") // removes the " around the type + return splittext(string_repr, ", ") diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm new file mode 100644 index 000000000000..983ecc800274 --- /dev/null +++ b/code/__HELPERS/maths.dm @@ -0,0 +1,13 @@ +///Calculate the angle between two movables and the west|east coordinate +/proc/get_angle(atom/movable/start, atom/movable/end)//For beams. + if(!start || !end) + return 0 + var/dy =(32 * end.y + end.pixel_y) - (32 * start.y + start.pixel_y) + var/dx =(32 * end.x + end.pixel_x) - (32 * start.x + start.pixel_x) + if(!dy) + return (dx >= 0) ? 90 : 270 + . = arctan(dx/dy) + if(dy < 0) + . += 180 + else if(dx < 0) + . += 360 diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 6a3028443dcc..ae5a1c1ce929 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -257,7 +257,19 @@ GLOBAL_LIST_EMPTY(species_list) return ..() /** - * Timed action involving one mob user. A target can also be specified, but it is optional. + * Used to get the amount of change between two body temperatures + * + * When passed the difference between two temperatures returns the amount of change to temperature to apply. + * The change rate should be kept at a low value tween 0.16 and 0.02 for optimal results. + * vars: + * * temp_diff (required) The differance between two temperatures + * * change_rate (optional)(Default: 0.06) The rate of range multiplyer + */ +/proc/get_temp_change_amount(temp_diff, change_rate = 0.06) + if(temp_diff < 0) + return -(BODYTEMP_AUTORECOVERY_DIVISOR / 2) * log(1 - (temp_diff * change_rate)) + +/* Timed action involving one mob user. A target can also be specified, but it is optional. * * Checks that `user` does not move, change hands, get stunned, etc. for the * given `delay`. Returns `TRUE` on success or `FALSE` on failure. diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 185c6c595b80..36764c6bae9f 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -295,7 +295,7 @@ Turf and target are separate in case you want to teleport some distance from a t return "[pick("!","@","#","$","%","^","&")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" //Returns a list of all items of interest with their name -/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, specify_dead_role = TRUE, only_realname = FALSE) +/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, specify_dead_role = TRUE) var/list/mobs = sortmobs() var/list/namecounts = list() var/list/pois = list() @@ -305,11 +305,7 @@ Turf and target are separate in case you want to teleport some distance from a t continue if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins continue - var/name = "" - if(only_realname) - name = avoid_assoc_duplicate_keys(M.real_name, namecounts) - else - name = avoid_assoc_duplicate_keys(M.name, namecounts) + M.get_realname_string() + var/name = avoid_assoc_duplicate_keys(M.name, namecounts) + M.get_realname_string() if(M.stat == DEAD && specify_dead_role) if(isobserver(M)) @@ -325,7 +321,6 @@ Turf and target are separate in case you want to teleport some distance from a t pois[avoid_assoc_duplicate_keys(A.name, namecounts)] = A return pois - //Orders mobs by type then by name /proc/sortmobs() var/list/moblist = list() diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm index fb2bac175034..6cd9aa8a0849 100644 --- a/code/_onclick/hud/action_button.dm +++ b/code/_onclick/hud/action_button.dm @@ -56,10 +56,16 @@ if(id && usr.client) //try to (un)remember position usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = locked ? moved : null return TRUE + var/trigger_flags + if(LAZYACCESS(modifiers, ALT_CLICK)) + if(locked) + to_chat(usr, "Action button \"[name]\" is locked, unlock it first.") + return TRUE + trigger_flags |= TRIGGER_SECONDARY_ACTION if(usr.next_click > world.time) return usr.next_click = world.time + 1 - linked_action.Trigger() + linked_action.Trigger(trigger_flags) return TRUE //Hide/Show Action Buttons ... Button diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 2b43444c31ae..bdbe8d1ac376 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -587,6 +587,29 @@ Recharging stations are available in robotics, the dormitory bathrooms, and the desc = "Your blood's electric charge is becoming dangerously high, find an outlet for your energy. Use Grab Intent on an APC to channel your energy into it." icon_state = "ethereal_overcharge" +//MODsuit unique +/atom/movable/screen/alert/nocore + name = "Missing Core" + desc = "Unit has no core. No modules available until a core is reinstalled. Robotics may provide assistance." + icon_state = "no_cell" + +/atom/movable/screen/alert/emptycell/plasma + name = "Out of Power" + desc = "Unit's plasma core has no charge remaining. No modules available until plasma core is recharged. \ + Unit can be refilled through plasma fuel." + +/atom/movable/screen/alert/emptycell/plasma/update_desc() + . = ..() + desc = initial(desc) + +/atom/movable/screen/alert/lowcell/plasma + name = "Low Charge" + desc = "Unit's plasma core is running low. Unit can be refilled through plasma fuel." + +/atom/movable/screen/alert/lowcell/plasma/update_desc() + . = ..() + desc = initial(desc) + //Need to cover all use cases - emag, illegal upgrade module, malf AI hack, traitor cyborg /atom/movable/screen/alert/hacked name = "Hacked" diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 03720e4d641f..a8f905e5fe25 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -42,6 +42,28 @@ SUBSYSTEM_DEF(mapping) /// Translation of virtual level ID to a virtual level reference var/list/virtual_z_translation = list() + /// List of z level (as number) -> plane offset of that z level + /// Used to maintain the plane cube + var/list/z_level_to_plane_offset = list() + /// List of z level (as number) -> The lowest plane offset in that z stack + var/list/z_level_to_lowest_plane_offset = list() + // This pair allows for easy conversion between an offset plane, and its true representation + // Both are in the form "input plane" -> output plane(s) + /// Assoc list of string plane values to their true, non offset representation + var/list/plane_offset_to_true + /// Assoc list of true string plane values to a list of all potential offset planess + var/list/true_to_offset_planes + /// Assoc list of string plane to the plane's offset value + var/list/plane_to_offset + /// List of planes that do not allow for offsetting + var/list/plane_offset_blacklist + /// List of render targets that do not allow for offsetting + var/list/render_offset_blacklist + /// List of plane masters that are of critical priority + var/list/critical_planes + /// The largest plane offset we've generated so far + var/max_plane_offset = 0 + /datum/controller/subsystem/mapping/Initialize(timeofday) if(initialized) return diff --git a/code/datums/action.dm b/code/datums/action.dm index cdca8729984f..e8d0ea303888 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -1,8 +1,3 @@ -#define AB_CHECK_HANDS_BLOCKED (1<<0) -#define AB_CHECK_IMMOBILE (1<<1) -#define AB_CHECK_LYING (1<<2) -#define AB_CHECK_CONSCIOUS (1<<3) - /datum/action var/name = "Generic Action" var/desc = null @@ -91,7 +86,7 @@ button.locked = FALSE button.id = null -/datum/action/proc/Trigger() +/datum/action/proc/Trigger(trigger_flags) if(!IsAvailable()) return FALSE if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER) diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm new file mode 100644 index 000000000000..3451a75538ad --- /dev/null +++ b/code/datums/components/jetpack.dm @@ -0,0 +1,149 @@ +// Welcome to the jetpack component +// Apply this to something when you want it to be "like a jetpack" +// So propulsion through space on move, that sort of thing +/datum/component/jetpack + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + var/datum/callback/check_on_move + var/datum/callback/get_mover + /// If we should stabilize ourselves when not drifting + var/stabilize = FALSE + /// The signal we listen for as an activation + var/activation_signal + /// The signal we listen for as a de-activation + var/deactivation_signal + /// The return flag our parent expects for a failed activation + var/return_flag + var/datum/effect_system/trail_follow/trail + /// The typepath to instansiate our trail as, when we need it + var/effect_type + +/** + * Arguments: + * * stabilize - If we should drift when we finish moving, or sit stable in space] + * * activation_signal - Signal we activate on + * * deactivation_signal - Signal we deactivate on + * * return_flag - Flag to return if activation fails + * * get_mover - Callback we use to get the "moving" thing, for trail purposes, alongside signal registration + * * check_on_move - Callback we call each time we attempt a move, we expect it to retun true if the move is ok, false otherwise. It expects an arg, TRUE if fuel should be consumed, FALSE othewise + * * effect_type - Type of trail_follow to spawn + */ +/datum/component/jetpack/Initialize(stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/get_mover, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type) + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + if(!activation_signal) // Can't activate? go away + return COMPONENT_INCOMPATIBLE + + RegisterSignal(parent, activation_signal, PROC_REF(activate)) + if(deactivation_signal) + RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate)) + + src.check_on_move = check_on_move + src.get_mover = get_mover + src.stabilize = stabilize + src.return_flag = return_flag + src.activation_signal = activation_signal + src.deactivation_signal = deactivation_signal + src.effect_type = effect_type + +/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/get_mover, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type) + UnregisterSignal(parent, src.activation_signal) + if(src.deactivation_signal) + UnregisterSignal(parent, src.deactivation_signal) + RegisterSignal(parent, activation_signal, PROC_REF(activate)) + if(deactivation_signal) + RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate)) + + src.check_on_move = check_on_move + src.get_mover = get_mover + src.stabilize = stabilize + src.activation_signal = activation_signal + src.deactivation_signal = deactivation_signal + src.effect_type = effect_type + + if(trail && effect_type != trail.type) + QDEL_NULL(trail) + setup_trail() + +/datum/component/jetpack/Destroy() + QDEL_NULL(trail) + QDEL_NULL(check_on_move) + return ..() + +/datum/component/jetpack/proc/setup_trail() + var/mob/moving = get_mover.Invoke() + if(!moving || trail) + return + trail = new effect_type + trail.auto_process = FALSE + trail.set_up(moving) + +/datum/component/jetpack/proc/activate(datum/source) + SIGNAL_HANDLER + var/mob/moving = get_mover.Invoke() + if(!thrust(moving)) + return return_flag + trail.start() + RegisterSignal(moving, COMSIG_MOVABLE_MOVED, PROC_REF(move_react)) + RegisterSignal(moving, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react)) + RegisterSignal(moving, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(spacemove_react)) + RegisterSignal(moving, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT, PROC_REF(block_starting_visuals)) + RegisterSignal(moving, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(ignore_ending_block)) + +/datum/component/jetpack/proc/deactivate(datum/source) + SIGNAL_HANDLER + QDEL_NULL(trail) + var/mob/moving = get_mover.Invoke() + if(moving) + UnregisterSignal(moving, COMSIG_MOVABLE_MOVED) + UnregisterSignal(moving, COMSIG_MOVABLE_PRE_MOVE) + UnregisterSignal(moving, COMSIG_MOVABLE_SPACEMOVE) + UnregisterSignal(moving, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) + UnregisterSignal(moving, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) + +/datum/component/jetpack/proc/move_react(mob/user) + SIGNAL_HANDLER + if(!user || !user.client)//Don't allow jet self using + return + if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet + return + if(!(user.movement_type & FLOATING) || user.buckled)//You don't want use jet in gravity or while buckled. + return + if(user.pulledby)//You don't must use jet if someone pull you + return + if(user.throwing)//You don't must use jet if you thrown + return + if(length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes. + thrust() + +/datum/component/jetpack/proc/pre_move_react(mob/user) + SIGNAL_HANDLER + trail.oldposition = get_turf(user) + +/datum/component/jetpack/proc/spacemove_react(mob/user, movement_dir, continuous_move) + SIGNAL_HANDLER + if(!continuous_move && movement_dir) + return COMSIG_MOVABLE_STOP_SPACEMOVE + // Check if we have the fuel to stop this. Do NOT cosume any fuel, just check + // This is done because things other then us can use our fuel + if(stabilize && check_on_move.Invoke(FALSE)) + return COMSIG_MOVABLE_STOP_SPACEMOVE + +/// Returns true if the thrust went well, false otherwise +/datum/component/jetpack/proc/thrust() + if(!check_on_move.Invoke(TRUE)) + return FALSE + if(!trail) + setup_trail() + trail.generate_effect() + return TRUE + +/// Basically, tell the drift component not to do its starting visuals, because they look dumb for us +/datum/component/jetpack/proc/block_starting_visuals(datum/source) + SIGNAL_HANDLER + return DRIFT_VISUAL_FAILED + +/// If we're on, don't let the drift component block movements at the end since we can speed +/datum/component/jetpack/proc/ignore_ending_block(datum/source) + SIGNAL_HANDLER + return DRIFT_ALLOW_INPUT diff --git a/code/datums/components/shielded.dm b/code/datums/components/shielded.dm new file mode 100644 index 000000000000..81cb0c2b4d40 --- /dev/null +++ b/code/datums/components/shielded.dm @@ -0,0 +1,186 @@ +/** + * The shielded component causes the parent item to nullify a certain number of attacks against the wearer, see: shielded vests. + */ + +/datum/component/shielded + /// The person currently wearing us + var/mob/living/wearer + /// How many charges we can have max, and how many we start with + var/max_charges + /// How many charges we currently have + var/current_charges + /// How long we have to avoid being hit to replenish charges. If set to 0, we never recharge lost charges + var/recharge_start_delay = 20 SECONDS + /// Once we go unhit long enough to recharge, we replenish charges this often. The floor is effectively 1 second, AKA how often SSdcs processes + var/charge_increment_delay = 1 SECONDS + /// How many charges we recover on each charge increment + var/charge_recovery = 1 + /// What .dmi we're pulling the shield icon from + var/shield_icon_file = 'icons/effects/effects.dmi' + /// What icon is used when someone has a functional shield up + var/shield_icon = "shield-old" + /// Do we still shield if we're being held in-hand? If FALSE, it needs to be equipped to a slot to work + var/shield_inhand = FALSE + /// Should the shield lose charges equal to the damage dealt by a hit? + var/lose_multiple_charges = FALSE + /// The cooldown tracking when we were last hit + COOLDOWN_DECLARE(recently_hit_cd) + /// The cooldown tracking when we last replenished a charge + COOLDOWN_DECLARE(charge_add_cd) + /// A callback for the sparks/message that play when a charge is used, see [/datum/component/shielded/proc/default_run_hit_callback] + var/datum/callback/on_hit_effects + +/datum/component/shielded/Initialize(max_charges = 3, recharge_start_delay = 20 SECONDS, charge_increment_delay = 1 SECONDS, charge_recovery = 1, lose_multiple_charges = FALSE, starting_charges = null, shield_icon_file = 'icons/effects/effects.dmi', shield_icon = "shield-old", shield_inhand = FALSE, run_hit_callback) + if(!isitem(parent) || max_charges <= 0) + return COMPONENT_INCOMPATIBLE + + src.max_charges = max_charges + src.recharge_start_delay = recharge_start_delay + src.charge_increment_delay = charge_increment_delay + src.charge_recovery = charge_recovery + src.lose_multiple_charges = lose_multiple_charges + src.shield_icon_file = shield_icon_file + src.shield_icon = shield_icon + src.shield_inhand = shield_inhand + src.on_hit_effects = run_hit_callback || CALLBACK(src, PROC_REF(default_run_hit_callback)) + if(isnull(starting_charges)) + current_charges = max_charges + else + current_charges = starting_charges + if(recharge_start_delay) + START_PROCESSING(SSdcs, src) + +/datum/component/shielded/Destroy(force, silent) + if(wearer) + shield_icon = "broken" + UnregisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS) + wearer.update_appearance(UPDATE_ICON) + wearer = null + QDEL_NULL(on_hit_effects) + return ..() + +/datum/component/shielded/RegisterWithParent() + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped)) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(lost_wearer)) + RegisterSignal(parent, COMSIG_ITEM_HIT_REACT, PROC_REF(on_hit_react)) + RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(check_recharge_rune)) + var/atom/shield = parent + if(ismob(shield.loc)) + var/mob/holder = shield.loc + if(holder.is_holding(parent) && !shield_inhand) + return + set_wearer(holder) + +/datum/component/shielded/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ITEM_HIT_REACT, COMSIG_PARENT_ATTACKBY)) + var/atom/shield = parent + if(shield.loc == wearer) + lost_wearer(src, wearer) + +// Handle recharging, if we want to +/datum/component/shielded/process(delta_time) + if(current_charges >= max_charges) + STOP_PROCESSING(SSdcs, src) + return + + if(!COOLDOWN_FINISHED(src, recently_hit_cd)) + return + if(!COOLDOWN_FINISHED(src, charge_add_cd)) + return + + var/obj/item/item_parent = parent + COOLDOWN_START(src, charge_add_cd, charge_increment_delay) + adjust_charge(charge_recovery) // set the number of charges to current + recovery per increment, clamped from zero to max_charges + playsound(item_parent, 'sound/magic/charge.ogg', 50, TRUE) + if(current_charges == max_charges) + playsound(item_parent, 'sound/machines/ding.ogg', 50, TRUE) + +/datum/component/shielded/proc/adjust_charge(change) + current_charges = clamp(current_charges + change, 0, max_charges) + if(wearer) + wearer.update_appearance(UPDATE_ICON) + +/// Check if we've been equipped to a valid slot to shield +/datum/component/shielded/proc/on_equipped(datum/source, mob/user, slot) + SIGNAL_HANDLER + + if(slot == ITEM_SLOT_HANDS && !shield_inhand) + lost_wearer(source, user) + return + set_wearer(source, user) + +/// Either we've been dropped or our wearer has been QDEL'd. Either way, they're no longer our problem +/datum/component/shielded/proc/lost_wearer(datum/source, mob/user) + SIGNAL_HANDLER + + if(wearer) + UnregisterSignal(wearer, list(COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_PARENT_QDELETING)) + wearer.update_appearance(UPDATE_ICON) + wearer = null + +/datum/component/shielded/proc/set_wearer(mob/user) + wearer = user + RegisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) + RegisterSignal(wearer, COMSIG_PARENT_QDELETING, PROC_REF(lost_wearer)) + if(current_charges) + wearer.update_appearance(UPDATE_ICON) + +/// Used to draw the shield overlay on the wearer +/datum/component/shielded/proc/on_update_overlays(atom/parent_atom, list/overlays) + SIGNAL_HANDLER + + overlays += mutable_appearance(shield_icon_file, (current_charges > 0 ? shield_icon : "broken"), ABOVE_MOB_LAYER) + +/** + * This proc fires when we're hit, and is responsible for checking if we're charged, then deducting one + returning that we're blocking if so. + * It then runs the callback in [/datum/component/shielded/var/on_hit_effects] which handles the messages/sparks (so the visuals) + */ +/datum/component/shielded/proc/on_hit_react(datum/source, mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type) + SIGNAL_HANDLER + + COOLDOWN_START(src, recently_hit_cd, recharge_start_delay) + + if(current_charges <= 0) + return + . = COMPONENT_HIT_REACTION_BLOCK + + var/charge_loss = 1 // how many charges do we lose + + if(lose_multiple_charges) // if the shield has health like damage we'll lose charges equal to the damage of the hit + charge_loss = damage + + adjust_charge(-charge_loss) + + INVOKE_ASYNC(src, PROC_REF(actually_run_hit_callback), owner, attack_text, current_charges) + + if(!recharge_start_delay) // if recharge_start_delay is 0, we don't recharge + if(!current_charges) // obviously if someone ever adds a manual way to replenish charges, change this + qdel(src) + return + + START_PROCESSING(SSdcs, src) // if we DO recharge, start processing so we can do that + +/// The wrapper to invoke the on_hit callback, so we don't have to worry about blocking in the signal handler +/datum/component/shielded/proc/actually_run_hit_callback(mob/living/owner, attack_text, current_charges) + on_hit_effects.Invoke(owner, attack_text, current_charges) + +/// Default on_hit proc, since cult robes are stupid and have different descriptions/sparks +/datum/component/shielded/proc/default_run_hit_callback(mob/living/owner, attack_text, current_charges) + do_sparks(2, TRUE, owner) + owner.visible_message(span_danger("Щит [owner] отражает [attack_text]!")) + if(current_charges <= 0) + owner.visible_message(span_warning("Щит [owner] перегружается!")) + +/datum/component/shielded/proc/check_recharge_rune(datum/source, obj/item/wizard_armour_charge/recharge_rune, mob/living/user) + /*SIGNAL_HANDLER + + if(!istype(recharge_rune)) + return + . = COMPONENT_NO_AFTERATTACK + if(!istype(parent, /obj/item/clothing/suit/space/hardsuit/shielded/wizard)) + to_chat(user, span_warning("Руна может быть использована только на броне боевого мага!")) + return + + current_charges += recharge_rune.restored_charges + to_chat(user, span_notice("Заряжаю [parent]. Теперь она сможет поглотить [current_charges] ударов.")) + qdel(recharge_rune)*/ diff --git a/code/datums/components/sizzle.dm b/code/datums/components/sizzle.dm index 88fadb377ceb..ecb45e448d84 100644 --- a/code/datums/components/sizzle.dm +++ b/code/datums/components/sizzle.dm @@ -3,9 +3,11 @@ var/sizzlealpha = 0 dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS -/datum/component/sizzle/Initialize() +/datum/component/sizzle/Initialize(_alpha) if(!isatom(parent)) return COMPONENT_INCOMPATIBLE + if(_alpha) + sizzlealpha = _alpha setup_sizzle() /datum/component/sizzle/InheritComponent(datum/component/C, i_am_original) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 89831dafab72..765e14e5db64 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -424,7 +424,7 @@ /datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M) var/atom/A = parent var/atom/dump_destination = dest_object.get_dumping_location() - if(A.Adjacent(M) && dump_destination && M.Adjacent(dump_destination)) + if(M.CanReach(A) && dump_destination && M.CanReach(dump_destination)) if(locked) to_chat(M, "[parent] seems to be [locked_flavor]!") return FALSE @@ -433,6 +433,12 @@ return TRUE return FALSE +/datum/component/storage/proc/get_dumping_location(atom/dest_object) + var/datum/component/storage/storage = dest_object.GetComponent(/datum/component/storage) + if(storage) + return storage.real_location() + return dest_object.get_dumping_location() + //This proc is called when you want to place an item into the storage item. /datum/component/storage/proc/attackby(datum/source, obj/item/I, mob/M, params) SIGNAL_HANDLER diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index 68c74921b8b7..0d3d520c2b54 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -270,11 +270,10 @@ if(ishuman(target)) var/mob/living/carbon/human/T = target - var/suit_slot = T.get_item_by_slot(ITEM_SLOT_OCLOTHING) if(isnull(T.wear_suit) && isnull(T.w_uniform)) // who honestly puts all of their effort into tackling a naked guy? defense_mod += 2 - if(suit_slot && (istype(suit_slot,/obj/item/clothing/suit/space/hardsuit))) + if(T.mob_negates_gravity()) defense_mod += 1 if(T.is_shove_knockdown_blocked()) // riot armor and such defense_mod += 5 diff --git a/code/datums/elements/empprotection.dm b/code/datums/elements/empprotection.dm new file mode 100644 index 000000000000..8d5d798c3cb8 --- /dev/null +++ b/code/datums/elements/empprotection.dm @@ -0,0 +1,20 @@ +/datum/element/empprotection + element_flags = ELEMENT_DETACH | ELEMENT_BESPOKE + id_arg_index = 2 + var/flags = NONE + +/datum/element/empprotection/Attach(datum/target, _flags) + . = ..() + if(. == ELEMENT_INCOMPATIBLE || !isatom(target)) + return ELEMENT_INCOMPATIBLE + flags = _flags + RegisterSignal(target, COMSIG_ATOM_EMP_ACT, PROC_REF(getEmpFlags)) + +/datum/element/empprotection/Detach(atom/target) + UnregisterSignal(target, COMSIG_ATOM_EMP_ACT) + return ..() + +/datum/element/empprotection/proc/getEmpFlags(datum/source, severity) + SIGNAL_HANDLER + + return flags diff --git a/code/datums/looping_sounds/weather.dm b/code/datums/looping_sounds/weather.dm index 4398e7d5b1b2..0e26b4592eb0 100644 --- a/code/datums/looping_sounds/weather.dm +++ b/code/datums/looping_sounds/weather.dm @@ -74,3 +74,20 @@ /datum/looping_sound/weather/rain/indoors volume = 30 + +/datum/looping_sound/weather/rain/storm + mid_sounds = list( + 'sound/ambience/storm_outdoors.ogg' = 1 + ) + mid_length = 20.03 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind. + start_sound = 'sound/ambience/acidrain_start.ogg' + start_length = null + end_sound = null + volume = 50 + +/datum/looping_sound/weather/rain/storm/indoors + volume = 30 + mid_sounds = list( + 'sound/ambience/storm_indoors.ogg' = 1 + ) + mid_length = 20.03 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind. diff --git a/code/datums/mapgen/planetary/SandGenerator.dm b/code/datums/mapgen/planetary/SandGenerator.dm index 1431ab53c6b9..e285f5b1ac6c 100644 --- a/code/datums/mapgen/planetary/SandGenerator.dm +++ b/code/datums/mapgen/planetary/SandGenerator.dm @@ -173,10 +173,10 @@ ) /datum/biome/sand/acid //plains - open_turf_types = list(/turf/open/floor/plating/asteroid/whitesands/lit = 10, /turf/open/acid/whitesands = 1) + open_turf_types = list(/turf/open/floor/plating/asteroid/whitesands/lit = 10, /turf/open/water/acid/whitesands = 1) /datum/biome/sand/acid/total - open_turf_types = list(/turf/open/acid/whitesands = 1) + open_turf_types = list(/turf/open/water/acid/whitesands = 1) flora_spawn_chance = 0 feature_spawn_chance = 0 mob_spawn_chance = 0 @@ -228,4 +228,4 @@ open_turf_types = list(/turf/open/floor/plating/asteroid/whitesands/dried = 7, /turf/open/lava = 1) /datum/biome/cave/sand/volcanic/acidic - open_turf_types = list(/turf/open/floor/plating/asteroid/whitesands/dried = 8, /turf/open/acid/whitesands = 1) + open_turf_types = list(/turf/open/floor/plating/asteroid/whitesands/dried = 8, /turf/open/water/acid/whitesands = 1) diff --git a/code/datums/weather/weather_types/rain.dm b/code/datums/weather/weather_types/rain.dm index fbbb0269ed2a..591a569b19ec 100644 --- a/code/datums/weather/weather_types/rain.dm +++ b/code/datums/weather/weather_types/rain.dm @@ -47,3 +47,17 @@ desc = "Storm with rain and lightning." weather_message = "The clouds blacken and the sky starts to flash as thunder strikes down!" thunder_chance = 10 + +/datum/weather/rain/heavy/storm_intense + name = "storm" + desc = "Storm with rain and lightning." + weather_overlay = "storm_very" + thunder_chance = 20 + weather_color = "#a3daf7" + weather_duration_lower = 420690 + weather_duration_upper = 420690 + + sound_active_outside = /datum/looping_sound/weather/rain/storm/indoors + sound_active_inside = /datum/looping_sound/weather/rain/storm + sound_weak_outside = /datum/looping_sound/weather/rain/storm/indoors + sound_weak_inside = /datum/looping_sound/weather/rain/storm diff --git a/code/datums/wires/mod.dm b/code/datums/wires/mod.dm new file mode 100644 index 000000000000..b5805557eafa --- /dev/null +++ b/code/datums/wires/mod.dm @@ -0,0 +1,57 @@ +/datum/wires/mod + holder_type = /obj/item/mod/control + proper_name = "MOD control unit" + +/datum/wires/mod/New(atom/holder) + wires = list(WIRE_HACK, WIRE_DISABLE, WIRE_SHOCK, WIRE_INTERFACE) + add_duds(2) + ..() + +/datum/wires/mod/interactable(mob/user) + if(!..()) + return FALSE + var/obj/item/mod/control/mod = holder + return mod.open + +/datum/wires/mod/get_status() + var/obj/item/mod/control/mod = holder + var/list/status = list() + status += "The orange light is [mod.seconds_electrified ? "on" : "off"]." + status += "The red light is [mod.malfunctioning ? "off" : "blinking"]." + status += "The green light is [mod.locked ? "on" : "off"]." + status += "The yellow light is [mod.interface_break ? "off" : "on"]." + return status + +/datum/wires/mod/on_pulse(wire) + var/obj/item/mod/control/mod = holder + switch(wire) + if(WIRE_HACK) + mod.locked = !mod.locked + if(WIRE_DISABLE) + mod.malfunctioning = TRUE + if(WIRE_SHOCK) + mod.seconds_electrified = MACHINE_DEFAULT_ELECTRIFY_TIME + if(WIRE_INTERFACE) + mod.interface_break = !mod.interface_break + +/datum/wires/mod/on_cut(wire, mend) + var/obj/item/mod/control/mod = holder + switch(wire) + if(WIRE_HACK) + if(!mend) + mod.req_access = list() + if(WIRE_DISABLE) + mod.malfunctioning = !mend + if(WIRE_SHOCK) + if(mend) + mod.seconds_electrified = MACHINE_NOT_ELECTRIFIED + else + mod.seconds_electrified = MACHINE_ELECTRIFIED_PERMANENT + if(WIRE_INTERFACE) + mod.interface_break = !mend + +/datum/wires/mod/ui_act(action, params) + var/obj/item/mod/control/mod = holder + if(!issilicon(usr) && mod.seconds_electrified && mod.shock(usr)) + return FALSE + return ..() diff --git a/code/game/MapData/shuttles/pgf_crying_sun.dm b/code/game/MapData/shuttles/pgf_crying_sun.dm index 6df40aa0efdd..2851518e669a 100644 --- a/code/game/MapData/shuttles/pgf_crying_sun.dm +++ b/code/game/MapData/shuttles/pgf_crying_sun.dm @@ -2,7 +2,7 @@ name = "The UCWLWM" desc = "It's looks old and worn out." icon_state = "book3" - author = "Welds-the-Steel" + author = "Senior Engineer Wihlz-Saai" title = "The Universal Colossal Warship Linear Weapon Mount" dat = {"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 80db6272f2fd..f059ae475640 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -44,6 +44,14 @@ */ var/list/atom_colours + /// Lazylist of all images (hopefully attached to us) to update when we change z levels + /// You will need to manage adding/removing from this yourself, but I'll do the updating for you + var/list/image/update_on_z + + /// Lazylist of all overlays attached to us to update when we change z levels + /// You will need to manage adding/removing from this yourself, but I'll do the updating for you + /// Oh and note, if order of addition is important this WILL break that. so mind yourself + var/list/image/update_overlays_on_z /// a very temporary list of overlays to remove var/list/remove_overlays @@ -977,7 +985,7 @@ return TRUE ///Get the best place to dump the items contained in the source storage item? -/atom/proc/get_dumping_location(obj/item/storage/source,mob/user) +/atom/proc/get_dumping_location() return null /** diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index beb7cef2718f..79326ab9e2ad 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -772,6 +772,44 @@ . = movement_type movement_type = newval +/** + * Called when a movable changes z-levels. + * + * Arguments: + * * old_turf - The previous turf they were on before. + * * new_turf - The turf they have now entered. + * * same_z_layer - If their old and new z levels are on the same level of plane offsets or not + * * notify_contents - Whether or not to notify the movable's contents that their z-level has changed. NOTE, IF YOU SET THIS, YOU NEED TO MANUALLY SET PLANE OF THE CONTENTS LATER + */ +/atom/movable/proc/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents = TRUE) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_turf, new_turf, same_z_layer) + + // If our turfs are on different z "layers", recalc our planes + if(!same_z_layer && !QDELETED(src)) + SET_PLANE(src, PLANE_TO_TRUE(src.plane), new_turf) + // a TON of overlays use planes, and thus require offsets + // so we do this. sucks to suck + update_appearance() + + if(update_on_z) + // I so much wish this could be somewhere else. alas, no. + for(var/image/update in update_on_z) + SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf) + if(update_overlays_on_z) + // This EVEN more so + cut_overlay(update_overlays_on_z) + // This even more so + for(var/mutable_appearance/update in update_overlays_on_z) + SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf) + add_overlay(update_overlays_on_z) + + if(!notify_contents) + return + + for (var/atom/movable/content as anything in src) // Notify contents of Z-transition. + content.on_changed_z_level(old_turf, new_turf, same_z_layer) + /** * Called whenever an object moves and by mobs when they attempt to move themselves through space * And when an object or action applies a force on src, see [newtonian_move][/atom/movable/proc/newtonian_move] @@ -784,6 +822,9 @@ * * movement_dir - 0 when stopping or any dir when trying to move */ /atom/movable/proc/Process_Spacemove(movement_dir = 0) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir) & COMSIG_MOVABLE_STOP_SPACEMOVE) + return TRUE + if(has_gravity(src)) return 1 diff --git a/code/game/machinery/quantum_pad.dm b/code/game/machinery/quantum_pad.dm index 7d0455ab82f0..a3fc7082f1be 100644 --- a/code/game/machinery/quantum_pad.dm +++ b/code/game/machinery/quantum_pad.dm @@ -14,6 +14,7 @@ var/teleporting = FALSE //if it's in the process of teleporting var/power_efficiency = 1 var/obj/machinery/quantumpad/linked_pad + var/restrain_vlevel = TRUE //mapping var/static/list/mapped_quantum_pads = list() @@ -177,7 +178,7 @@ else if(!isobserver(ROI)) continue - do_teleport(ROI, get_turf(target_pad),null,null,null,null,null,TRUE, channel = TELEPORT_CHANNEL_QUANTUM) + do_teleport(ROI, get_turf(target_pad),null,null,null,null,null,TRUE, channel = TELEPORT_CHANNEL_QUANTUM, restrain_vlevel = restrain_vlevel) CHECK_TICK /obj/machinery/quantumpad/proc/initMappedLink() diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 107c5656c034..f6be0b0a036e 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -16,6 +16,7 @@ var/obj/item/clothing/suit/space/suit = null var/obj/item/clothing/head/helmet/space/helmet = null var/obj/item/clothing/mask/mask = null + var/obj/item/mod/control/mod = null var/obj/item/storage = null // if you add more storage slots, update cook() to clear their radiation too. @@ -27,6 +28,8 @@ var/mask_type = null /// What type of additional item the unit starts with when spawned. var/storage_type = null + /// What type of MOD the unit starts with when spawned. + var/mod_type = null state_open = FALSE /// If the SSU's doors are locked closed. Can be toggled manually via the UI, but is also locked automatically when the UV decontamination sequence is running. @@ -194,6 +197,8 @@ helmet = new helmet_type(src) if(mask_type) mask = new mask_type(src) + if(mod_type) + mod = new mod_type(src) if(storage_type) storage = new storage_type(src) update_appearance() @@ -215,6 +220,7 @@ QDEL_NULL(suit) QDEL_NULL(helmet) QDEL_NULL(mask) + QDEL_NULL(mod) QDEL_NULL(storage) return ..() @@ -225,19 +231,19 @@ . += "[base_icon_state]_panel" if(state_open) . += "[base_icon_state]_open" - if(suit) + if(suit || mod) . += "[base_icon_state]_suit" if(helmet) . += "[base_icon_state]_helm" if(storage) . += "[base_icon_state]_storage" - if(uv && uv_super) - . += "[base_icon_state]_super" if(!(machine_stat & BROKEN || machine_stat & NOPOWER)) if(state_open) . += "[base_icon_state]_lights_open" else if(uv) + if(uv_super) + . += "[base_icon_state]_super" . += "[base_icon_state]_lights_red" else . += "[base_icon_state]_lights_closed" @@ -247,6 +253,8 @@ . += "[base_icon_state]_uvstrong" else . += "[base_icon_state]_uv" + else if(locked) + . += "[base_icon_state]_locked" else . += "[base_icon_state]_ready" @@ -263,6 +271,7 @@ helmet = null suit = null mask = null + mod = null storage = null occupant = null @@ -289,6 +298,7 @@ "suit" = create_silhouette_of(/obj/item/clothing/suit/space/eva), "helmet" = create_silhouette_of(/obj/item/clothing/head/helmet/space/eva), "mask" = create_silhouette_of(/obj/item/clothing/mask/breath), + "mod" = create_silhouette_of(/obj/item/mod/control), "storage" = create_silhouette_of(/obj/item/tank/internals/oxygen), ) @@ -340,8 +350,10 @@ close_machine() if ("disinfect") if (occupant && safeties) + say("Alert: safeties triggered, occupant detected!") return else if (!helmet && !mask && !suit && !storage && !occupant) + to_chat(user, span_notice("There's nothing inside [src] to disinfect!")) return else if (occupant) @@ -351,6 +363,7 @@ if ("lock", "unlock") if (!state_open) locked = !locked + update_icon() else var/obj/item/item_to_dispense = vars[choice] if (item_to_dispense) @@ -397,7 +410,7 @@ if(!is_operational) to_chat(user, span_warning("The unit is not operational!")) return - if(occupant || helmet || suit || storage) + if(occupant || helmet || suit || mod || storage) to_chat(user, span_warning("It's too cluttered inside to fit in!")) return @@ -407,7 +420,7 @@ target.visible_message(span_warning("[user] starts shoving [target] into [src]!"), span_userdanger("[user] starts shoving you into [src]!")) if(do_after(user, 30, target)) - if(occupant || helmet || suit || storage) + if(occupant || helmet || suit || mod || storage) return if(target == user) user.visible_message(span_warning("[user] slips into [src] and closes the door behind [user.p_them()]!"), span_notice("You slip into [src]'s cramped space and shut its door.")) @@ -452,6 +465,8 @@ qdel(suit) // Delete everything but the occupant. mask = null qdel(mask) + mod = null + qdel(mod) storage = null qdel(storage) // The wires get damaged too. @@ -479,6 +494,9 @@ if(mask) things_to_clear += mask things_to_clear += mask.GetAllContents() + if(mod) + things_to_clear += mod + things_to_clear += mod.GetAllContents() if(storage) things_to_clear += storage things_to_clear += storage.GetAllContents() @@ -565,6 +583,13 @@ if(!user.transferItemToLoc(I, src)) return mask = I + else if(istype(I, /obj/item/mod/control)) + if(mod) + to_chat(user, span_warning("The unit already contains a MOD!")) + return + if(!user.transferItemToLoc(I, src)) + return + mod = I else if(storage) to_chat(user, span_warning("The auxiliary storage compartment is full!")) @@ -631,6 +656,9 @@ else if(istype(AM, /obj/item/clothing/mask) && !mask) AM.forceMove(src) mask = AM + else if(istype(AM, /obj/item/mod/control) && !storage) + AM.forceMove(src) + mod = AM else if(istype(AM, /obj/item) && !storage) AM.forceMove(src) storage = AM diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm index cf89811d5f09..67d47ede3277 100644 --- a/code/game/mecha/combat/durand.dm +++ b/code/game/mecha/combat/durand.dm @@ -12,6 +12,7 @@ force = 40 wreckage = /obj/structure/mecha_wreckage/durand var/obj/durand_shield/shield + var/shield_passive_drain = 300 /obj/mecha/combat/durand/clip @@ -46,7 +47,7 @@ /obj/mecha/combat/durand/process() . = ..() - if(defense_mode && !use_power(100)) + if(defense_mode && !use_power(max(0, shield_passive_drain - (scanmod.rating * 10)))) defense_action.Activate(forced_state = TRUE) /obj/mecha/combat/durand/domove(direction) @@ -226,7 +227,7 @@ the shield is disabled by means other than the action button (like running out o return . = ..() flick("shield_impact", src) - if(!chassis.use_power((max_integrity - obj_integrity) * 100)) + if(!chassis.use_power(max(1, (max_integrity - obj_integrity + 15) * (10 - chassis.capacitor.rating)))) chassis.cell?.charge = 0 chassis.defense_action.Activate(forced_state = TRUE) obj_integrity = 10000 diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 5774716fca71..5fe5d9350c14 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -14,7 +14,7 @@ wreckage = /obj/structure/mecha_wreckage/gygax internal_damage_threshold = 35 max_equip = 3 - step_energy_drain = 3 + base_step_energy_drain = 8 /obj/mecha/combat/gygax/mechturn(direction) . = ..() diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 95b60eba68d8..f1c48e8cc307 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -4,7 +4,7 @@ icon_state = "phazon" step_in = 2 dir_in = 2 //Facing South. - step_energy_drain = 3 + base_step_energy_drain = 8 max_integrity = 200 deflect_chance = 30 armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 50, "fire" = 100, "acid" = 100) diff --git a/code/game/mecha/combat/reticence.dm b/code/game/mecha/combat/reticence.dm index 0d11859352d8..b63487ca17a5 100644 --- a/code/game/mecha/combat/reticence.dm +++ b/code/game/mecha/combat/reticence.dm @@ -14,7 +14,7 @@ add_req_access = 0 internal_damage_threshold = 25 max_equip = 2 - step_energy_drain = 3 + base_step_energy_drain = 8 color = "#87878715" stepsound = null turnsound = null diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 8e233a6410de..b903564c88d6 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -18,8 +18,8 @@ var/mob/living/carbon/occupant = null var/step_in = 10 //make a step in step_in/10 sec. var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - var/normal_step_energy_drain = 10 //How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain. - var/step_energy_drain = 10 + var/base_step_energy_drain = 15 //The base amount of energy the mech should consume each time it moves. This variable is a backup for when leg actuators affect the energy drain. + var/step_energy_drain // How much energy the mech actually consumes when moving after modifiers (Eg, stock parts, leg actuators) var/melee_energy_drain = 15 var/overload_step_energy_drain_min = 100 max_integrity = 300 //max_integrity is base health @@ -147,6 +147,7 @@ diag_hud_set_mechcell() diag_hud_set_mechstat() become_hearing_sensitive(ROUNDSTART_TRAIT) + update_part_values() /obj/mecha/update_icon_state() if(silicon_pilot && silicon_icon_state) @@ -225,11 +226,9 @@ /obj/mecha/proc/update_part_values() ///Updates the values given by scanning module and capacitor tier, called when a part is removed or inserted. if(scanmod) - normal_step_energy_drain = 20 - (5 * scanmod.rating) //10 is normal, so on lowest part its worse, on second its ok and on higher its real good up to 0 on best - step_energy_drain = normal_step_energy_drain + step_energy_drain = max(1, base_step_energy_drain - (5 * scanmod.rating)) //10 is normal, so on lowest part its worse, on second its ok and on higher its real good up to 0 on best else - normal_step_energy_drain = 500 - step_energy_drain = normal_step_energy_drain + step_energy_drain = 500 if(capacitor) armor = armor.modifyRating(energy = (capacitor.rating * 5)) //Each level of capacitor protects the mech against emp by 5% else //because we can still be hit without a cap, even if we can't move diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index baeac5bd8f31..6d860558a652 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -179,7 +179,7 @@ else chassis.leg_overload_mode = 0 chassis.step_in = initial(chassis.step_in) - chassis.step_energy_drain = chassis.normal_step_energy_drain + chassis.update_part_values() chassis.occupant_message("You disable leg actuators overload.") UpdateButtonIcon() diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index 27e139c84ab6..4874ab142647 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -8,7 +8,7 @@ wreckage = /obj/structure/mecha_wreckage/odysseus internal_damage_threshold = 35 deflect_chance = 15 - step_energy_drain = 6 + base_step_energy_drain = 11 /obj/mecha/medical/odysseus/moved_inside(mob/living/carbon/human/H) . = ..() diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 57ffd6992808..9f360011df86 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -107,7 +107,7 @@ light_range = 7 light_power = 1 wreckage = /obj/structure/mecha_wreckage/ripley/deathripley - step_energy_drain = 0 + base_step_energy_drain = 0 enclosed = TRUE enter_delay = 40 silicon_icon_state = null @@ -171,7 +171,7 @@ name = "\improper CLIP APLU Mk-IV \"Rogue\"" icon_state = "clipripley" base_icon_state = "clipripley" - step_energy_drain = 15 //overdriven servos are less efficient + base_step_energy_drain = 20 //overdriven servos are less efficient wreckage = /obj/structure/mecha_wreckage/ripley/clip enclosed = TRUE enter_delay = 20 //slower than a mk. I, faster than the armored Ripleys diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 3dd327dda08c..5de5510bec7b 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -68,6 +68,8 @@ random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5") dryname = "dried tracks" drydesc = "Some old bloody tracks left by wheels. Machines are evil, perhaps." + ///Absorb the /squirt subtype when it exists on the turf + var/absorb_squirts = TRUE /obj/effect/decal/cleanable/blood/tracks icon_state = "tracks" @@ -278,3 +280,132 @@ if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY)) return 1 return 0 + +/obj/effect/decal/cleanable/blood/hitsplatter + name = "blood splatter" + pass_flags = PASSTABLE | PASSGRILLE + icon_state = "hitsplatter1" + random_icon_states = list("hitsplatter1", "hitsplatter2", "hitsplatter3") + /// The turf we just came from, so we can back up when we hit a wall + var/turf/prev_loc + /// The cached info about the blood + var/list/blood_dna_info + /// Skip making the final blood splatter when we're done, like if we're not in a turf + var/skip = FALSE + /// How many tiles/items/people we can paint red + var/splatter_strength = 3 + /// Insurance so that we don't keep moving once we hit a stoppoint + var/hit_endpoint = FALSE +// ///Absorb the /squirt subtype when it exists on the turf +// var/absorb_squirts = TRUE + +/obj/effect/decal/cleanable/blood/hitsplatter/Initialize(mapload, splatter_strength) + . = ..() + prev_loc = loc //Just so we are sure prev_loc exists + if(splatter_strength) + src.splatter_strength = splatter_strength + +/obj/effect/decal/cleanable/blood/hitsplatter/Destroy() + if(isturf(loc) && !skip) + playsound(src, 'sound/effects/splatter.ogg', 60, TRUE, -1) + if(blood_dna_info) + loc.add_blood_DNA(blood_dna_info) + return ..() + +/// Set the splatter up to fly through the air until it rounds out of steam or hits something. Contains sleep() pending imminent moveloop rework, don't call without async'ing it +/obj/effect/decal/cleanable/blood/hitsplatter/proc/fly_towards(turf/target_turf, range) + splatter_strength = range + for(var/i in 1 to range) + step_towards(src,target_turf) + sleep(2) // Will be resolved pending Potato's moveloop rework + for(var/atom/iter_atom in get_turf(src)) + if(hit_endpoint) + return + if(splatter_strength <= 0) + break + + if(isitem(iter_atom)) + iter_atom.add_blood_DNA(blood_dna_info) + splatter_strength-- + else if(ishuman(iter_atom)) + var/mob/living/carbon/human/splashed_human = iter_atom + if(splashed_human.wear_suit) + splashed_human.wear_suit.add_blood_DNA(blood_dna_info) + splashed_human.update_inv_wear_suit() //updates mob overlays to show the new blood (no refresh) + if(splashed_human.w_uniform) + splashed_human.w_uniform.add_blood_DNA(blood_dna_info) + splashed_human.update_inv_w_uniform() //updates mob overlays to show the new blood (no refresh) + splatter_strength-- + + if(splatter_strength <= 0) // we used all the puff so we delete it. + qdel(src) + return + + var/obj/effect/decal/cleanable/blood/newsplatter + if(splatter_strength <= 3.5) + newsplatter = new /obj/effect/decal/cleanable/blood/squirt(get_turf(src), get_dir(prev_loc, loc), blood_dna_info) + else + newsplatter = new /obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + newsplatter.add_blood_DNA(blood_dna_info) + prev_loc = loc + + qdel(src) + return + +/obj/effect/decal/cleanable/blood/hitsplatter/Bump(atom/bumped_atom) + if(!iswallturf(bumped_atom) && !istype(bumped_atom, /obj/structure/window)) + qdel(src) + return + + if(istype(bumped_atom, /obj/structure/window)) + var/obj/structure/window/bumped_window = bumped_atom + if(!bumped_window.fulltile) + qdel(src) + return + + hit_endpoint = TRUE + if(isturf(prev_loc)) + abstract_move(bumped_atom) + skip = TRUE + //Adjust pixel offset to make splatters appear on the wall + if(istype(bumped_atom, /obj/structure/window)) + land_on_window(bumped_atom) + else + var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new(prev_loc) + final_splatter.pixel_x = (dir == EAST ? 32 : (dir == WEST ? -32 : 0)) + final_splatter.pixel_y = (dir == NORTH ? 32 : (dir == SOUTH ? -32 : 0)) + else // This will only happen if prev_loc is not even a turf, which is highly unlikely. + abstract_move(bumped_atom) + qdel(src) + +/// A special case for hitsplatters hitting windows, since those can actually be moved around, store it in the window and slap it in the vis_contents +/obj/effect/decal/cleanable/blood/hitsplatter/proc/land_on_window(obj/structure/window/the_window) + if(!the_window.fulltile) + return + var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new + final_splatter.forceMove(the_window) + the_window.vis_contents += final_splatter + the_window.bloodied = TRUE + qdel(src) + +/obj/effect/decal/cleanable/blood/squirt + name = "blood trail" + icon_state = "squirt" + random_icon_states = null + +/obj/effect/decal/cleanable/blood/squirt/Initialize(mapload, direction, list/blood_dna) + . = ..() + dir = direction + var/obj/effect/decal/cleanable/blood/splatter/existing_blood = locate() in get_turf(src) + if(existing_blood?.absorb_squirts) + if(blood_dna) + existing_blood.add_blood_DNA(blood_dna) + existing_blood.bloodiness = min((existing_blood.bloodiness + bloodiness), BLOOD_AMOUNT_PER_DECAL) + return INITIALIZE_HINT_QDEL + +/obj/effect/decal/cleanable/blood/splatter/over_window // special layer/plane set to appear on windows + layer = ABOVE_WINDOW_LAYER + plane = GAME_PLANE + turf_loc_check = FALSE + alpha = 180 + absorb_squirts = FALSE diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm index 3f2b6ecaf94e..efa1de11103f 100644 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ b/code/game/objects/effects/effect_system/effects_other.dm @@ -81,6 +81,9 @@ /datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/I) I.setDir(holder.dir) +/datum/effect_system/trail_follow/ion/grav_allowed + nograv_required = FALSE + //Reagent-based explosion effect /datum/effect_system/reagents_explosion diff --git a/code/game/objects/effects/particle_emitter.dm b/code/game/objects/effects/particle_emitter.dm index 3ee4ab8ed461..cc4210f742a1 100644 --- a/code/game/objects/effects/particle_emitter.dm +++ b/code/game/objects/effects/particle_emitter.dm @@ -1,7 +1,70 @@ -/obj/effect/particle_emitter - name = "" +///objects can only have one particle on them at a time, so we use these abstract effects to hold and display the effects. You know, so multiple particle effects can exist at once. +///also because some objects do not display particles due to how their visuals are built +/obj/effect/abstract/particle_holder + name = "particle holder" + desc = "How are you reading this? Please make a bug report :)" + appearance_flags = KEEP_APART|KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE //movable appearance_flags plus KEEP_APART and KEEP_TOGETHER + vis_flags = VIS_INHERIT_PLANE + layer = ABOVE_ALL_MOB_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT anchored = TRUE - mouse_opacity = 0 + /// Holds info about how this particle emitter works + /// See \code\__DEFINES\particles.dm + var/particle_flags = NONE -/obj/effect/particle_emitter/Initialize(mapload, time) + var/atom/parent + +/obj/effect/abstract/particle_holder/Initialize(mapload, particle_path = /particles/smoke, particle_flags = NONE) . = ..() + if(!loc) + stack_trace("particle holder was created with no loc!") + return INITIALIZE_HINT_QDEL + // We nullspace ourselves because some objects use their contents (e.g. storage) and some items may drop everything in their contents on deconstruct. + parent = loc + loc = null + + // Mouse opacity can get set to opaque by some objects when placed into the object's contents (storage containers). + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + src.particle_flags = particle_flags + particles = new particle_path() + // /atom doesn't have vis_contents, /turf and /atom/movable do + var/atom/movable/lie_about_areas = parent + lie_about_areas.vis_contents += src + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(parent_deleted)) + + if(particle_flags & PARTICLE_ATTACH_MOB) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + on_move(parent, null, NORTH) + +/obj/effect/abstract/particle_holder/Destroy(force) + QDEL_NULL(particles) + parent = null + return ..() + +/// Non movables don't delete contents on destroy, so we gotta do this +/obj/effect/abstract/particle_holder/proc/parent_deleted(datum/source) + SIGNAL_HANDLER + qdel(src) + +/// signal called when a parent that's been hooked into this moves +/// does a variety of checks to ensure overrides work out properly +/obj/effect/abstract/particle_holder/proc/on_move(atom/movable/attached, atom/oldloc, direction) + SIGNAL_HANDLER + + if(!(particle_flags & PARTICLE_ATTACH_MOB)) + return + + //remove old + if(ismob(oldloc)) + var/mob/particle_mob = oldloc + particle_mob.vis_contents -= src + + // If we're sitting in a mob, we want to emit from it too, for vibes and shit + if(ismob(attached.loc)) + var/mob/particle_mob = attached.loc + particle_mob.vis_contents += src + +/// Sets the particles position to the passed coordinate list (X, Y, Z) +/// See [https://www.byond.com/docs/ref/#/{notes}/particles] for position documentation +/obj/effect/abstract/particle_holder/proc/set_particle_position(list/pos) + particles.position = pos diff --git a/code/game/objects/effects/particles/acid.dm b/code/game/objects/effects/particles/acid.dm new file mode 100644 index 000000000000..5ce0984991d8 --- /dev/null +++ b/code/game/objects/effects/particles/acid.dm @@ -0,0 +1,15 @@ +// Acid related particles. +/particles/acid + icon = 'icons/effects/particles/goop.dmi' + icon_state = list("goop_1" = 6, "goop_2" = 2, "goop_3" = 1) + width = 100 + height = 100 + count = 100 + spawning = 0.5 + color = "#00ea2b80" //to get 96 alpha + lifespan = 1.5 SECONDS + fade = 1 SECONDS + grow = -0.025 + gravity = list(0, 0.15) + position = generator(GEN_SPHERE, 0, 16, NORMAL_RAND) + spin = generator(GEN_NUM, -15, 15, NORMAL_RAND) diff --git a/code/game/objects/effects/particles/fire.dm b/code/game/objects/effects/particles/fire.dm new file mode 100644 index 000000000000..fb20dc778e5e --- /dev/null +++ b/code/game/objects/effects/particles/fire.dm @@ -0,0 +1,55 @@ +// Fire related particles. +/particles/bonfire + icon = 'icons/effects/particles/bonfire.dmi' + icon_state = "bonfire" + width = 100 + height = 100 + count = 1000 + spawning = 4 + lifespan = 0.7 SECONDS + fade = 1 SECONDS + grow = -0.01 + velocity = list(0, 0) + position = generator(GEN_CIRCLE, 0, 16, NORMAL_RAND) + drift = generator(GEN_VECTOR, list(0, -0.2), list(0, 0.2)) + gravity = list(0, 0.95) + scale = generator(GEN_VECTOR, list(0.3, 0.3), list(1,1), NORMAL_RAND) + rotation = 30 + spin = generator(GEN_NUM, -20, 20) + +/particles/embers + icon = 'icons/effects/particles/generic.dmi' + icon_state = list("dot" = 4,"cross" = 1,"curl" = 1) + width = 64 + height = 96 + count = 500 + spawning = 5 + lifespan = 3 SECONDS + fade = 1 SECONDS + color = 0 + color_change = 0.05 + gradient = list("#FBAF4D", "#FCE6B6", "#FD481C") + position = generator(GEN_BOX, list(-12,-16,0), list(12,16,0), NORMAL_RAND) + drift = generator(GEN_VECTOR, list(-0.1,0), list(0.1,0.025)) + spin = generator(GEN_NUM, list(-15,15), NORMAL_RAND) + scale = generator(GEN_VECTOR, list(0.5,0.5), list(2,2), NORMAL_RAND) + +/particles/embers/lava + width = 700 + height = 700 + gradient = list(LIGHT_COLOR_FLARE, LIGHT_COLOR_FLARE , COLOR_ALMOST_BLACK) + spawning = 1 + +/particles/lava + width = 700 + height = 700 + count = 500 + spawning = 1 + lifespan = 4 SECONDS + fade = 2 SECONDS + position = generator(GEN_CIRCLE, 16, 24, NORMAL_RAND) + drift = generator(GEN_VECTOR, list(-0.2, -0.2), list(0.6, 0.6)) + velocity = generator(GEN_CIRCLE, -6, 6, NORMAL_RAND) + friction = 0.15 + gradient = list(0,LIGHT_COLOR_FLARE , 0.75, COLOR_ALMOST_BLACK) + color_change = 0.125 diff --git a/code/game/objects/effects/particles/misc.dm b/code/game/objects/effects/particles/misc.dm new file mode 100644 index 000000000000..18db20d63798 --- /dev/null +++ b/code/game/objects/effects/particles/misc.dm @@ -0,0 +1,32 @@ +// General or un-matched particles, make a new file if a few can be sorted together. +/particles/pollen + icon = 'icons/effects/particles/pollen.dmi' + icon_state = "pollen" + width = 100 + height = 100 + count = 1000 + spawning = 4 + lifespan = 0.7 SECONDS + fade = 1 SECONDS + grow = -0.01 + velocity = list(0, 0) + position = generator(GEN_CIRCLE, 0, 16, NORMAL_RAND) + drift = generator(GEN_VECTOR, list(0, -0.2), list(0, 0.2)) + gravity = list(0, 0.95) + scale = generator(GEN_VECTOR, list(0.3, 0.3), list(1,1), NORMAL_RAND) + rotation = 30 + spin = generator(GEN_NUM, -20, 20) + +/particles/echo + icon = 'icons/effects/particles/echo.dmi' + icon_state = list("echo1" = 1, "echo2" = 1, "echo3" = 2) + width = 480 + height = 480 + count = 1000 + spawning = 0.5 + lifespan = 2 SECONDS + fade = 1 SECONDS + gravity = list(0, -0.1) + position = generator(GEN_BOX, list(-240, -240), list(240, 240), NORMAL_RAND) + drift = generator(GEN_VECTOR, list(-0.1, 0), list(0.1, 0)) + rotation = generator(GEN_NUM, 0, 360, NORMAL_RAND) diff --git a/code/game/objects/effects/particles/slime.dm b/code/game/objects/effects/particles/slime.dm new file mode 100644 index 000000000000..5cef9c976257 --- /dev/null +++ b/code/game/objects/effects/particles/slime.dm @@ -0,0 +1,22 @@ +/// Slime particles. +/particles/slime + icon = 'icons/effects/particles/goop.dmi' + icon_state = list("goop_1" = 6, "goop_2" = 2, "goop_3" = 1) + width = 100 + height = 100 + count = 100 + spawning = 0.5 + color = "#707070a0" + lifespan = 1.5 SECONDS + fade = 1 SECONDS + grow = -0.025 + gravity = list(0, -0.05) + position = generator(GEN_BOX, list(-8,-16,0), list(8,16,0), NORMAL_RAND) + spin = generator(GEN_NUM, -15, 15, NORMAL_RAND) + scale = list(0.75, 0.75) + +/// Rainbow slime particles. +/particles/slime/rainbow + gradient = list(0, "#f00a", 3, "#0ffa", 6, "#f00a", "loop", "space"=COLORSPACE_HSL) + color_change = 0.2 + color = generator(GEN_NUM, 0, 6, UNIFORM_RAND) diff --git a/code/game/objects/effects/particles/smoke.dm b/code/game/objects/effects/particles/smoke.dm new file mode 100644 index 000000000000..72807e778f56 --- /dev/null +++ b/code/game/objects/effects/particles/smoke.dm @@ -0,0 +1,66 @@ +// All the smoke variant particles. +/particles/smoke + icon = 'icons/effects/particles/smoke.dmi' + icon_state = list("smoke_1" = 1, "smoke_2" = 1, "smoke_3" = 2) + width = 100 + height = 100 + count = 1000 + spawning = 4 + lifespan = 1.5 SECONDS + fade = 1 SECONDS + velocity = list(0, 0.4, 0) + position = list(6, 0, 0) + drift = generator(GEN_SPHERE, 0, 2, NORMAL_RAND) + friction = 0.2 + gravity = list(0, 0.95) + grow = 0.05 + +/particles/smoke/turf_fire + position = generator(GEN_SPHERE, 16, 24, NORMAL_RAND) + +/particles/smoke/burning + position = list(0, 0, 0) + +/particles/smoke/burning/small + spawning = 1 + scale = list(0.8, 0.8) + velocity = list(0, 0.4, 0) + +/particles/smoke/steam + icon_state = list("steam_1" = 1, "steam_2" = 1, "steam_3" = 2) + fade = 1.5 SECONDS + +/particles/smoke/steam/mild + spawning = 1 + velocity = list(0, 0.3, 0) + friction = 0.25 + +/particles/smoke/steam/bad + icon_state = list("steam_1" = 1, "smoke_1" = 1, "smoke_2" = 1, "smoke_3" = 1) + spawning = 2 + velocity = list(0, 0.25, 0) + +/particles/smoke/steam/vent + position = generator(GEN_SPHERE, 16, 16, NORMAL_RAND) + lifespan = 2 SECONDS + spawning = 3 + +/particles/smoke/steam/vent/low + spawning = 1 + velocity = list(0, 0.3, 0) + friction = 0.25 + +/particles/smoke/steam/vent/high + spawning = 8 + velocity = list(0, 0.25, 0) + count = 1000 + +/particles/smoke/ash + icon_state = list("ash_1" = 2, "ash_2" = 2, "ash_3" = 1, "smoke_1" = 3, "smoke_2" = 2) + count = 500 + spawning = 1 + lifespan = 1 SECONDS + fade = 0.2 SECONDS + fadein = 0.7 SECONDS + position = generator(GEN_VECTOR, list(-3, 5, 0), list(3, 6.5, 0), NORMAL_RAND) + velocity = generator(GEN_VECTOR, list(-0.1, 0.4, 0), list(0.1, 0.5, 0), NORMAL_RAND) diff --git a/code/game/objects/effects/particles/water.dm b/code/game/objects/effects/particles/water.dm new file mode 100644 index 000000000000..456617cb5d8a --- /dev/null +++ b/code/game/objects/effects/particles/water.dm @@ -0,0 +1,17 @@ +// Water related particles. +/particles/droplets + icon = 'icons/effects/particles/generic.dmi' + icon_state = list("dot"=2,"drop"=1) + width = 32 + height = 36 + count = 5 + spawning = 0.2 + lifespan = 1 SECONDS + fade = 0.5 SECONDS + color = "#549EFF" + position = generator(GEN_BOX, list(-9,-9,0), list(9,18,0), NORMAL_RAND) + scale = generator(GEN_VECTOR, list(0.9,0.9), list(1.1,1.1), NORMAL_RAND) + gravity = list(0, -0.9) + +/particles/droplets/blood + spawning = 0.2 diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index cfd5b8ba34b9..f285aa2058f3 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -31,6 +31,7 @@ var/innate_accuracy_penalty = 0 var/last_effect = 0 var/force_teleport = FALSE + var/restrain_vlevel = TRUE /obj/effect/portal/anom name = "wormhole" @@ -156,7 +157,7 @@ no_effect = TRUE else last_effect = world.time - if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport)) + if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport, restrain_vlevel = restrain_vlevel)) if(istype(M, /obj/projectile)) var/obj/projectile/P = M P.ignore_source_check = TRUE @@ -183,6 +184,7 @@ desc = "An unwavering portal that will never fade." hardlinked = FALSE // dont qdel my portal nerd force_teleport = TRUE // force teleports because they're a mapmaker tool + restrain_vlevel = FALSE var/id // var edit or set id in map editor /obj/effect/portal/permanent/proc/set_linked() diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 4913f9b835ce..d36c427d5b22 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -469,6 +469,11 @@ duration = 12 shrink = FALSE +/obj/effect/temp_visual/light_ash + icon_state = "light_ash" + icon = 'icons/effects/weather_effects.dmi' + duration = 3.2 SECONDS + /obj/effect/temp_visual/warp_cube duration = 5 var/outgoing = TRUE diff --git a/code/game/objects/effects/turf_fire.dm b/code/game/objects/effects/turf_fire.dm index a0c9e0f95a9b..735d8226edfa 100644 --- a/code/game/objects/effects/turf_fire.dm +++ b/code/game/objects/effects/turf_fire.dm @@ -65,7 +65,7 @@ /obj/effect/abstract/turf_fire/Initialize(mapload, power, fire_color) . = ..() - particles = new /particles/lava + particles = new /particles/smoke/turf_fire var/turf/open/open_turf = loc if(open_turf.turf_fire) return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 88c5df2262da..6a47415140fe 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -240,6 +240,51 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb qdel(X) return ..() +/// Called when an action associated with our item is deleted +/obj/item/proc/on_action_deleted(datum/source) + SIGNAL_HANDLER + + if(!(source in actions)) + CRASH("An action ([source.type]) was deleted that was associated with an item ([src]), but was not found in the item's actions list.") + + LAZYREMOVE(actions, source) + + +/// Adds an item action to our list of item actions. +/// Item actions are actions linked to our item, that are granted to mobs who equip us. +/// This also ensures that the actions are properly tracked in the actions list and removed if they're deleted. +/// Can be be passed a typepath of an action or an instance of an action. +/obj/item/proc/add_item_action(action_or_action_type) + + var/datum/action/action + if(ispath(action_or_action_type, /datum/action)) + action = new action_or_action_type(src) + else if(istype(action_or_action_type, /datum/action)) + action = action_or_action_type + else + CRASH("item add_item_action got a type or instance of something that wasn't an action.") + + LAZYADD(actions, action) + RegisterSignal(action, COMSIG_PARENT_QDELETING, PROC_REF(on_action_deleted)) + grant_action_to_bearer(action) + return action + +/// Grant the action to anyone who has this item equipped to an appropriate slot +/obj/item/proc/grant_action_to_bearer(datum/action/action) + if(!ismob(loc)) + return + var/mob/holder = loc + give_item_action(action, holder, holder.get_slot_by_item(src)) + +/// Removes an instance of an action from our list of item actions. +/obj/item/proc/remove_item_action(datum/action/action) + if(!action) + return + + UnregisterSignal(action, COMSIG_PARENT_QDELETING) + LAZYREMOVE(actions, action) + qdel(action) + /obj/item/proc/check_allowed_items(atom/target, not_inside, target_self) if(((src in target) && !target_self) || (!isturf(target.loc) && !isturf(target) && not_inside)) return 0 @@ -535,9 +580,21 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb playsound(src, pickup_sound, PICKUP_SOUND_VOLUME, ignore_walls = FALSE) user.update_equipment_speed_mods() -///sometimes we only want to grant the item's action if it's equipped in a specific slot. -/obj/item/proc/item_action_slot_check(slot, mob/user) - if(slot == ITEM_SLOT_BACKPACK || slot == ITEM_SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there +/// Gives one of our item actions to a mob, when equipped to a certain slot +/obj/item/proc/give_item_action(datum/action/action, mob/to_who, slot) + // Some items only give their actions buttons when in a specific slot. + if(!item_action_slot_check(slot, to_who)) + // There is a chance we still have our item action currently, + // and are moving it from a "valid slot" to an "invalid slot". + // So call Remove() here regardless, even if excessive. + action.Remove(to_who) + return + + action.Grant(to_who) + +/// Sometimes we only want to grant the item's action if it's equipped in a specific slot. +/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/action) + if(slot & (ITEM_SLOT_BACKPACK|ITEM_SLOT_LEGCUFFED)) //these aren't true slots, so avoid granting actions there return FALSE return TRUE @@ -1128,6 +1185,9 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb /obj/item/proc/get_writing_implement_details() return null +/// Whether or not this item can be put into a storage item through attackby +/obj/item/proc/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user) + return TRUE /// How many different types of mats will be counted in a bite? #define MAX_MATS_PER_BITE 2 diff --git a/code/game/objects/items/attachments/stock.dm b/code/game/objects/items/attachments/stock.dm index 1fe286c14296..98ecbf18eaa8 100644 --- a/code/game/objects/items/attachments/stock.dm +++ b/code/game/objects/items/attachments/stock.dm @@ -38,3 +38,7 @@ /obj/item/attachment/foldable_stock/inteq icon_state = "skm-inteqsmg-stock" + +/obj/item/attachment/foldable_stock/spitter + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + icon_state = "spitter_stock" diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 0affcd107af0..5b91f6b7bd87 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -96,7 +96,7 @@ var/shield_icon = "shield-red" /obj/item/nullrod/staff/worn_overlays(isinhands) - . = list() + . = ..() if(isinhands) . += mutable_appearance('icons/effects/effects.dmi', shield_icon, MOB_LAYER + 0.01) diff --git a/code/game/objects/items/storage/ration.dm b/code/game/objects/items/storage/ration.dm index 482ba202a73e..b016cc339260 100644 --- a/code/game/objects/items/storage/ration.dm +++ b/code/game/objects/items/storage/ration.dm @@ -9,10 +9,20 @@ resistance_flags = FLAMMABLE drop_sound = 'sound/items/handling/cardboardbox_drop.ogg' pickup_sound = 'sound/items/handling/cardboardbox_pickup.ogg' + var/emblem_icon_state = "null" + var/ration_overlay = "null" /obj/item/storage/ration/Initialize(mapload) . = ..() update_icon() + update_overlays() + +/obj/item/storage/ration/update_overlays() + . = ..() + var/mutable_appearance/ration_overlay + if(emblem_icon_state) + ration_overlay = mutable_appearance(icon, emblem_icon_state) + add_overlay(ration_overlay) /obj/item/storage/ration/ComponentInitialize() . = ..() @@ -38,7 +48,7 @@ /obj/item/storage/ration/vegan_chili name = "vegan chili with beans ration" desc = "A complete meal package containing a hearty vegan chili with beans, complemented by vegetable crackers, savory cornbread, flavorful pizza crackers, and more. A perfect choice for plant-based nourishment." - + emblem_icon_state = "emblem_vegan_chili" /obj/item/storage/ration/vegan_chili/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/vegan_chili = 1, @@ -54,7 +64,7 @@ /obj/item/storage/ration/shredded_beef name = "shredded beef in barbecue sauce ration" desc = "Enjoy the rich and savory flavors of shredded beef in smoky barbecue sauce with this satisfying ration. Accompanied by a fruit puree, jerky wrap, cinnamon bun, and additional condiments, this ration is perfect for meat lovers." - + emblem_icon_state = "emblem_shredded_beef" /obj/item/storage/ration/shredded_beef/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/shredded_beef = 1, @@ -70,7 +80,7 @@ /obj/item/storage/ration/pork_spaghetti name = "spaghetti with pork and sauce ration" desc = "Indulge in a comforting meal of spaghetti with tender pork and savory sauce with this ration. Complemented by a toaster pastry, seasoned bread sticks, dried raisins, and other accompaniments, this ration offers a flavorful experience." - + emblem_icon_state = "emblem_pork_spaghetti" /obj/item/storage/ration/pork_spaghetti/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/pork_spaghetti = 1, @@ -86,7 +96,7 @@ /obj/item/storage/ration/fried_fish name = "fried fish chunks ration" desc = "Experience the crispy delight of fried fish chunks with this ration. Accompanied by an energy bar, tortillas, toasted corn kernels, and more, this ration provides a satisfying combination of flavors and textures." - + emblem_icon_state = "emblem_fried_fish" /obj/item/storage/ration/fried_fish/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/fried_fish = 1, @@ -103,7 +113,7 @@ /obj/item/storage/ration/beef_strips name = "beef strips in tomato sauce ration" desc = "Savor the deliciousness of tender beef strips in a flavorful tomato sauce with this ration. Enjoy a chocolate pudding, white wheat snack bread, blackberry preserves, and peppermint candy rings as delightful accompaniments." - + emblem_icon_state = "emblem_beef_strips" /obj/item/storage/ration/beef_strips/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/beef_strips = 1, @@ -120,7 +130,7 @@ /obj/item/storage/ration/chili_macaroni name = "chili and macaroni ration" desc = "Indulge in the comforting combination of chili and macaroni in this flavorful ration. Satisfy your taste buds with a mix of sweet and savory treats." - + emblem_icon_state = "emblem_chili_macaroni" /obj/item/storage/ration/chili_macaroni/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/chili_macaroni = 1, @@ -137,7 +147,7 @@ /obj/item/storage/ration/chicken_wings_hot_sauce name = "chicken wings in hot sauce ration" desc = "Experience the bold and spicy flavors of chicken wings drenched in hot sauce. This ration also includes a mix of delightful snacks for a well-rounded meal." - + emblem_icon_state = "emblem_chicken_wings_hot_sauce" /obj/item/storage/ration/chicken_wings_hot_sauce/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/chicken_wings_hot_sauce = 1, @@ -153,7 +163,7 @@ /obj/item/storage/ration/fish_stew name = "fish stew ration" desc = "Dive into the depths of flavor with this fish stew ration. Enjoy a hearty blend of seafood and vegetables, complemented by a selection of tasty accompaniments." - + emblem_icon_state = "emblem_fish_stew" /obj/item/storage/ration/fish_stew/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/fish_stew = 1, @@ -170,7 +180,7 @@ /obj/item/storage/ration/lemon_pepper_chicken name = "lemon pepper chicken ration" desc = "A tasty Lemon Pepper Chicken ration that combines the flavors of fruit and meat. Perfect for a satisfying meal." - + emblem_icon_state = "emblem_lemon_pepper_chicken" /obj/item/storage/ration/lemon_pepper_chicken/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/lemon_pepper_chicken = 1, @@ -186,7 +196,7 @@ /obj/item/storage/ration/sausage_peppers_onions name = "sausage, peppers and onions ration" desc = "Indulge in the delightful combination of juicy sausage, peppers, and onions in this hearty ration." - + emblem_icon_state = "emblem_sausage_peppers_onions" /obj/item/storage/ration/sausage_peppers_onions/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/sausage_peppers_onions = 1, @@ -202,7 +212,7 @@ /obj/item/storage/ration/pork_dumplings_chili_sauce name = "pork dumplings in chili sauce ration" desc = "Savor the rich flavors of pork dumplings in a spicy chili sauce, accompanied by a variety of complementary snacks." - + emblem_icon_state = "emblem_pork_dumplings_chili_sauce" /obj/item/storage/ration/pork_dumplings_chili_sauce/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/dumplings_chili_sauce = 1, @@ -218,7 +228,7 @@ /obj/item/storage/ration/battered_fish_sticks name = "battered fish sticks ration" desc = "Enjoy the crispy goodness of battered fish sticks, along with a selection of sides and a delectable dessert." - + emblem_icon_state = "emblem_battered_fish_sticks" /obj/item/storage/ration/battered_fish_sticks/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/battered_fish_sticks = 1, @@ -234,7 +244,7 @@ /obj/item/storage/ration/assorted_salted_offal name = "assorted salted offal ration" desc = "An adventurous choice, this ration offers an assortment of salted offal, providing a unique culinary experience." - + emblem_icon_state = "emblem_assorted_salted_offal" /obj/item/storage/ration/assorted_salted_offal/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/assorted_salted_offal = 1, @@ -250,7 +260,7 @@ /obj/item/storage/ration/maple_pork_sausage_patty name = "maple pork sausage patty ration" desc = "Start your day with a satisfying breakfast featuring a maple-infused pork sausage patty and a variety of treats." - + emblem_icon_state = "emblem_maple_pork_sausage_patty" /obj/item/storage/ration/maple_pork_sausage_patty/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/maple_pork_sausage_patty = 1, @@ -267,7 +277,7 @@ /obj/item/storage/ration/pepper_jack_beef_patty name = "jalapeno pepper jack beef patty ration" desc = "Experience a flavorful fusion of jalapeno, pepper jack cheese, and beef in this grilled beef patty ration." - + emblem_icon_state = "emblem_pepper_jack_beef_patty" /obj/item/storage/ration/pepper_jack_beef_patty/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/pepper_jack_beef_patty = 1, @@ -284,7 +294,7 @@ /obj/item/storage/ration/beef_goulash name = "beef goulash ration" desc = "Delight in the rich flavors of beef goulash, accompanied by a selection of sides and a sweet treat." - + emblem_icon_state = "emblem_beef_goulash" /obj/item/storage/ration/beef_goulash/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/beef_goulash = 1, @@ -301,7 +311,7 @@ /obj/item/storage/ration/pepperoni_pizza_slice name = "pepperoni pizza slice ration" desc = "Indulge in the classic taste of pepperoni pizza with this ration, complete with sides and a refreshing beverage." - + emblem_icon_state = "emblem_pepperoni_pizza_slice" /obj/item/storage/ration/pepperoni_pizza_slice/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/pepperoni_pizza_slice = 1, @@ -317,7 +327,7 @@ /obj/item/storage/ration/blackened_calamari name = "blackened calamari in red sauce ration" desc = "Enjoy the savory delight of blackened calamari served in a rich red sauce." - + emblem_icon_state = "emblem_blackened_calamari" /obj/item/storage/ration/blackened_calamari/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/blackened_calamari = 1, @@ -334,7 +344,7 @@ /obj/item/storage/ration/elbow_macaroni name = "elbow macaroni in tomato sauce ration" desc = "Savor the comforting taste of elbow macaroni in a delicious tomato sauce." - + emblem_icon_state = "emblem_elbow_macaroni" /obj/item/storage/ration/elbow_macaroni/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/elbow_macaroni = 1, @@ -351,7 +361,7 @@ /obj/item/storage/ration/cheese_pizza_slice name = "cheese pizza slice ration" desc = "Experience the timeless flavor of a classic cheese pizza slice." - + emblem_icon_state = "emblem_cheese_pizza_slice" /obj/item/storage/ration/cheese_pizza_slice/PopulateContents() var/static/items_inside = list( /obj/item/reagent_containers/food/snacks/ration/entree/cheese_pizza_slice = 1, @@ -368,7 +378,7 @@ /obj/item/storage/ration/crayons name = "military grade crayon ration" desc = "Proven to increase kill count by atleast 1." - + emblem_icon_state = "emblem_crayons" /obj/item/storage/ration/crayons/PopulateContents() var/static/items_inside = list( /obj/item/toy/crayon/red = 1, diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm index 002b72633294..2c8fda3ec5d7 100644 --- a/code/game/objects/items/storage/wallets.dm +++ b/code/game/objects/items/storage/wallets.dm @@ -19,26 +19,21 @@ /obj/item/spacecash/bundle, /obj/item/holochip, /obj/item/card, - /obj/item/clothing/mask/cigarette, /obj/item/flashlight/pen, /obj/item/seeds, - /obj/item/stack/medical, /obj/item/toy/crayon, /obj/item/coin, /obj/item/dice, /obj/item/disk, - /obj/item/implanter, /obj/item/lighter, + /obj/item/key/ship, + /obj/item/gun/ballistic/derringer, /obj/item/lipstick, /obj/item/match, /obj/item/paper, /obj/item/pen, /obj/item/photo, - /obj/item/reagent_containers/dropper, - /obj/item/reagent_containers/syringe, - /obj/item/screwdriver, - /obj/item/stamp), - list(/obj/item/screwdriver/power)) + /obj/item/stamp)) /obj/item/storage/wallet/Exited(atom/movable/AM) . = ..() diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm index 4095d159ea82..50f709dcd65f 100644 --- a/code/game/objects/items/tanks/watertank.dm +++ b/code/game/objects/items/tanks/watertank.dm @@ -377,7 +377,7 @@ //Todo : cache these. /obj/item/reagent_containers/chemtank/worn_overlays(isinhands = FALSE) //apply chemcolor and level - . = list() + . = ..() //inhands + reagent_filling if(!isinhands && reagents.total_volume) var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "backpackmob-10") diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index 3e4ab0d15de1..7c35ddd67d62 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -53,7 +53,7 @@ . += base_overlay /obj/item/screwdriver/worn_overlays(isinhands = FALSE, icon_file) - . = list() + . = ..() if(isinhands && random_color) var/mutable_appearance/M = mutable_appearance(icon_file, "screwdriver_head") M.appearance_flags = RESET_COLOR diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index a76334a0b7ea..89eb9b0019e4 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -366,7 +366,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/cane name = "cane" - desc = "A cane used by a true gentleman. Or a clown." + desc = "A cane used by a true gentleman." icon = 'icons/obj/items_and_weapons.dmi' icon_state = "cane" item_state = "stick" diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 78cfa10a2e0b..97b8c4348353 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -210,6 +210,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, "fire", 0) if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF)) resistance_flags |= ON_FIRE + burning_particles = new(src, /particles/smoke/burning) SSfire_burning.processing[src] = src update_appearance() return 1 @@ -226,6 +227,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e if(resistance_flags & ON_FIRE) resistance_flags &= ~ON_FIRE update_appearance() + QDEL_NULL(burning_particles) SSfire_burning.processing -= src ///Called when the obj is hit by a tesla bolt. diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index d4ad3f0e679e..0ffeaa673b53 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -43,6 +43,8 @@ vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. + var/obj/effect/abstract/particle_holder/burning_particles + FASTDMM_PROP(\ pinned_vars = list("name", "dir")\ ) diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 0fca2bcca6ee..1698f90ec7cd 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -1056,3 +1056,39 @@ T.air.adjust_moles(GAS_CO2, -amt) T.atmos_spawn_air("o2=[amt];TEMP=293.15") lastcycle = world.time + +/obj/structure/fluff/steam_vent + name = "steam vent" + desc = "A outlet for steam, usually for water coming in contact with steam pipes." + icon = 'icons/obj/structures.dmi' + icon_state = "steamvent" + deconstructible = FALSE + layer = GAS_PUMP_LAYER + + var/particle_to_spawn = /particles/smoke/steam/vent + var/obj/effect/particle_holder/part_hold + +/obj/structure/fluff/steam_vent/Initialize() + . = ..() + part_hold = new(get_turf(src)) + part_hold.layer = EDGED_TURF_LAYER + part_hold.particles = new particle_to_spawn() + underlays.Cut() + +/obj/structure/fluff/steam_vent/Destroy() + . = ..() + QDEL_NULL(part_hold) + +/obj/structure/fluff/steam_vent/low + particle_to_spawn = /particles/smoke/steam/vent/low + +/obj/structure/fluff/steam_vent/high + particle_to_spawn = /particles/smoke/steam/vent/high + +/obj/effect/particle_holder + name = "" + anchored = TRUE + mouse_opacity = 0 + +/obj/effect/particle_emitter/Initialize(mapload, time) + . = ..() diff --git a/code/game/objects/structures/table_flipped.dm b/code/game/objects/structures/table_flipped.dm index c006695a7df0..28af5d0b63bd 100644 --- a/code/game/objects/structures/table_flipped.dm +++ b/code/game/objects/structures/table_flipped.dm @@ -17,10 +17,22 @@ AddElement(/datum/element/connect_loc, loc_connections) +/obj/structure/flippedtable/examine(mob/user) + . = ..() + . += span_notice("You could right the [name] by control shift-clicking it.") + +/obj/structure/flippedtable/proc/check_dir() + if(dir == NORTHEAST || dir == SOUTHEAST) + return EAST + if(dir == NORTHWEST || dir == SOUTHWEST) + return WEST + return dir + /obj/structure/flippedtable/CanAllowThrough(atom/movable/mover, turf/target) . = ..() - var/attempted_dir = get_dir(loc, target) - if(table_type == /obj/structure/table/glass) //Glass table, jolly ranchers pass + var/table_dir = check_dir() + var/attempted_dir = get_dir(loc, mover) + if(table_type == /obj/structure/table/glass) //Glass table, lasers can pass if(istype(mover) && (mover.pass_flags & PASSGLASS)) return TRUE if(istype(mover, /obj/projectile)) @@ -29,20 +41,20 @@ if(proj_obj.trajectory && angle2dir_cardinal(proj_obj.trajectory.angle) == dir) return TRUE return FALSE - return attempted_dir != dir + return attempted_dir != table_dir /obj/structure/flippedtable/proc/on_exit(datum/source, atom/movable/exiter, direction) SIGNAL_HANDLER - + var/table_dir = check_dir() if(exiter == src) return // Let's not block ourselves. - if(table_type == /obj/structure/table/glass) //Glass table, jolly ranchers pass + if(table_type == /obj/structure/table/glass) //Glass table, lasers pass if(istype(exiter) && (exiter.pass_flags & PASSGLASS)) return if(istype(exiter, /obj/projectile)) return - if(direction == dir) + if(direction == table_dir) exiter.Bump(src) return COMPONENT_ATOM_BLOCK_EXIT return @@ -51,10 +63,10 @@ . = ..() if(!istype(user) || !user.can_interact_with(src)) return FALSE - user.visible_message("[user] starts flipping [src]!", "You start flipping over the [src]!") + user.visible_message(span_danger("[user] starts flipping [src]!"), span_notice("You start flipping over the [src]!")) if(do_after(user, max_integrity/4)) var/obj/structure/table/table_unflip = new table_type(src.loc) table_unflip.obj_integrity = obj_integrity - user.visible_message("[user] flips over the [src]!", "You flip over the [src]!") + user.visible_message(span_danger("[user] flips over the [src]!"), span_notice("You flip over the [src]!")) playsound(src, 'sound/items/trayhit2.ogg', 100) qdel(src) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 49c3823cf1ce..5420cc06b490 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -32,6 +32,9 @@ hitsound_type = PROJECTILE_HITSOUND_GLASS + /// If some inconsiderate jerk has had their blood spilled on this window, thus making it cleanable + var/bloodied = FALSE + /obj/structure/window/examine(mob/user) . = ..() if(flags_1 & NODECONSTRUCT_1) diff --git a/code/game/turfs/open/acid.dm b/code/game/turfs/open/acid.dm index 50c7ee7bf98c..86260871c522 100644 --- a/code/game/turfs/open/acid.dm +++ b/code/game/turfs/open/acid.dm @@ -1,8 +1,8 @@ -/turf/open/acid +/turf/open/water/acid name = "acid lake" + desc = "A lake of acid." icon_state = "acid" - gender = PLURAL - baseturfs = /turf/open/acid + baseturfs = /turf/open/water/acid slowdown = 2 light_range = 2 @@ -10,90 +10,77 @@ light_color = LIGHT_COLOR_SLIME_LAMP bullet_bounce_sound = 'sound/items/welder2.ogg' + planetary_atmos = FALSE footstep = FOOTSTEP_LAVA barefootstep = FOOTSTEP_LAVA clawfootstep = FOOTSTEP_LAVA heavyfootstep = FOOTSTEP_LAVA -/turf/open/acid/CanAllowThrough(atom/movable/passing_atom, turf/target) + reagent_to_extract = /datum/reagent/toxin/acid + extracted_reagent_visible_name = "acid" + +/turf/open/water/acid/CanAllowThrough(atom/movable/passing_atom, turf/target) if(ishostile(passing_atom)) return FALSE return ..() -/turf/open/acid/ex_act(severity, target) +/turf/open/water/acid/ex_act(severity, target) contents_explosion(severity, target) -/turf/open/acid/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent) - return - -/turf/open/acid/Melt() +/turf/open/water/acid/Melt() to_be_destroyed = FALSE return src -/turf/open/acid/acid_act(acidpwr, acid_volume) +/turf/open/water/acid/acid_act(acidpwr, acid_volume) return -/turf/open/acid/MakeDry(wet_setting = TURF_WET_WATER) +/turf/open/water/acid/MakeDry(wet_setting = TURF_WET_WATER) return -/turf/open/acid/airless +/turf/open/water/acid/airless initial_gas_mix = AIRLESS_ATMOS -/turf/open/acid/Entered(atom/movable/AM) +/turf/open/water/acid/Entered(atom/movable/AM) . = ..() if(melt_stuff(AM)) START_PROCESSING(SSobj, src) -/turf/open/acid/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) +/turf/open/water/acid/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(melt_stuff(AM)) START_PROCESSING(SSobj, src) -/turf/open/acid/process() +/turf/open/water/acid/process() if(!melt_stuff()) STOP_PROCESSING(SSobj, src) -/turf/open/acid/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) - switch(the_rcd.mode) - if(RCD_FLOORWALL) - return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 3) - return FALSE - -/turf/open/acid/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_FLOORWALL) - to_chat(user, "You build a floor.") - PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) - return TRUE - return FALSE - -/turf/open/acid/singularity_act() +/turf/open/water/acid/singularity_act() return -/turf/open/acid/singularity_pull(S, current_size) +/turf/open/water/acid/singularity_pull(S, current_size) return -/turf/open/acid/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) +/turf/open/water/acid/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) underlay_appearance.icon = 'icons/turf/floors.dmi' underlay_appearance.icon_state = "basalt" return TRUE -/turf/open/acid/attackby(obj/item/C, mob/user, params) +/turf/open/water/acid/attackby(obj/item/_item, mob/user, params) ..() - if(istype(C, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = C + if(istype(_item, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = _item var/obj/structure/lattice/H = locate(/obj/structure/lattice, src) if(H) - to_chat(user, "There is already a lattice here!") + to_chat(user, span_warning("There is already a lattice here!")) return if(R.use(2)) - to_chat(user, "You construct a catwalk.") + to_chat(user, span_notice("You construct a catwalk.")) playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) new /obj/structure/lattice/catwalk(locate(x, y, z)) else - to_chat(user, "You need one rod to build a lattice.") + to_chat(user, span_warning("You need one rod to build a lattice.")) return -/turf/open/acid/proc/is_safe_to_cross() +/turf/open/water/acid/proc/is_safe_to_cross() //if anything matching this typecache is found in the lava, we don't burn things var/static/list/acid_safeties_typecache = typecacheof(list(/obj/structure/catwalk, /obj/structure/stone_tile, /obj/structure/lattice/)) var/list/found_safeties = typecache_filter_list(contents, acid_safeties_typecache) @@ -103,7 +90,7 @@ return LAZYLEN(found_safeties) -/turf/open/acid/proc/melt_stuff(thing_to_melt) +/turf/open/water/acid/proc/melt_stuff(thing_to_melt) if(is_safe_to_cross()) return FALSE . = FALSE @@ -156,6 +143,6 @@ if(L) //mobs turning into object corpses could get deleted here. L.acid_act(50, 100) -/turf/open/acid/whitesands +/turf/open/water/acid/whitesands planetary_atmos = TRUE initial_gas_mix = WHITESANDS_ATMOS diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index eb2132940bd9..90fd6610721c 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -220,19 +220,13 @@ /turf/open/lava/smooth/airless initial_gas_mix = AIRLESS_ATMOS -/particles/lava - width = 700 - height = 700 - count = 1000 - spawning = 1 - lifespan = 3 SECONDS - fade = 2 SECONDS - position = generator("circle", 16, 24, NORMAL_RAND) - drift = generator("vector", list(-0.2, -0.2), list(0.2, 0.2)) - velocity = generator("circle", -6, 6, NORMAL_RAND) - friction = 0.15 - gradient = list(0,LIGHT_COLOR_FLARE , 0.75, COLOR_ALMOST_BLACK) - color_change = 0.125 +/obj/effect/particle_holder + name = "" + anchored = TRUE + mouse_opacity = 0 + +/obj/effect/particle_emitter/Initialize(mapload, time) + . = ..() /obj/effect/particle_emitter/lava - particles = new/particles/lava + particles = new/particles/embers/lava diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index 9128844e1a2f..f94db41d4713 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -18,27 +18,33 @@ var/datum/reagent/reagent_to_extract = /datum/reagent/water var/extracted_reagent_visible_name = "water" -/* -/turf/open/water/attackby(obj/item/tool, mob/user, params) +/turf/open/water/examine(mob/user) + . = ..() + if(reagent_to_extract) + . += span_notice("You could probably scoop some of the [extracted_reagent_visible_name] if you had a beaker...") + +/turf/open/water/attackby(obj/item/_item, mob/user, params) + if(istype(_item, /obj/item/fish)) + to_chat(user, span_notice("You toss the [_item.name] into the [name].")) + playsound(_item, "sound/effects/bigsplash.ogg", 90) + qdel(_item) + if(istype(_item, /obj/item/reagent_containers/glass)) + extract_reagents(_item,user,params) + + . = ..() + +/turf/open/water/proc/extract_reagents(obj/item/reagent_containers/glass/container, mob/user, params) if(!reagent_to_extract) - return ..() - var/obj/item/reagent_containers/glass/container = tool - if(!istype(tool, /obj/item/reagent_containers)) - return ..() + return FALSE + if(!container.is_refillable()) + to_chat(user, span_danger("\The [container]'s cap is on! Take it off first.")) + return FALSE if(container.reagents.total_volume >= container.volume) - to_chat(user, "[container] is full.") - return + to_chat(user, span_danger("\The [container] is full.")) + return FALSE container.reagents.add_reagent(reagent_to_extract, rand(5, 10)) - user.visible_message("[user] scoops [extracted_reagent_visible_name] from the [src] with \the [container].", "You scoop out [extracted_reagent_visible_name] from the [src] using \the [container].") + user.visible_message(span_notice("[user] scoops [extracted_reagent_visible_name] from the [src] with \the [container]."), span_notice("You scoop out [extracted_reagent_visible_name] from the [src] using \the [container].")) return TRUE -*/ - -/turf/open/water/attackby(obj/item/fish, mob/user, params) - . = ..() - if(istype(fish, /obj/item/fish)) - to_chat(user, "You toss the [fish.name] into the water.") - playsound(fish, "sound/effects/bigsplash.ogg", 90) - qdel(fish) /turf/open/water/can_have_cabling() return FALSE @@ -52,7 +58,7 @@ /turf/open/water/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) switch(passed_mode) if(RCD_FLOORWALL) - to_chat(user, "You build a floor.") + to_chat(user, span_notice("You build a floor.")) PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) return TRUE return FALSE @@ -85,7 +91,7 @@ /turf/open/water/tar name = "tar pit" - desc = "Shallow tar. Will slow you down significantly. You could use a beaker to scoop some out..." + desc = "Shallow tar. Will slow you down significantly." color = "#473a3a" light_range = 0 slowdown = 2 diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 53d17bf325aa..69c9248d6fb9 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -423,7 +423,7 @@ return 0 /obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands) - . = list() + . = ..() if(!isinhands && current_charges) . += mutable_appearance('icons/effects/cult_effects.dmi', "shield-cult", MOB_LAYER + 0.01) diff --git a/code/modules/cargo/packs/ammo.dm b/code/modules/cargo/packs/ammo.dm index efcc24cc7ae6..d790cbbdf650 100644 --- a/code/modules/cargo/packs/ammo.dm +++ b/code/modules/cargo/packs/ammo.dm @@ -134,7 +134,7 @@ /datum/supply_pack/ammo/gal308_ammo name = "CM-GAL .308 Magazine Crate" desc = "Contains a .308 CM-GAL magazine for the CM-GAL rifle, containing ten rounds." - contains = list(/obj/item/ammo_box/magazine/gal) + contains = list(/obj/item/ammo_box/magazine/f4_308) cost = 1000 /datum/supply_pack/ammo/gar_ammo @@ -169,10 +169,16 @@ /datum/supply_pack/ammo/a762_ammo_box name = "7.62x40mm CLIP Ammo Box Crate" - desc = "Contains a eighty-round 7.62x40mm CLIP box for the SKM rifles." + desc = "Contains a one hundred and twenty-round 7.62x40mm CLIP box for the SKM rifles." contains = list(/obj/item/ammo_box/a762_40) cost = 500 +/datum/supply_pack/ammo/a556_ammo_box + name = "5.56x39mm CLIP Ammo Box Crate" + desc = "Contains a one hundred and twenty-round 5.56x39mm CLIP box for most newer rifles." + contains = list(/obj/item/ammo_box/a556_39) + cost = 450 + /datum/supply_pack/ammo/a357_ammo_box name = ".357 Ammo Box Crate" desc = "Contains a fifty-round .357 box for revolvers such as the Scarborough Revolver and the HP Firebrand." diff --git a/code/modules/cargo/packs/food.dm b/code/modules/cargo/packs/food.dm index 9c366406af5c..6bf438f22867 100644 --- a/code/modules/cargo/packs/food.dm +++ b/code/modules/cargo/packs/food.dm @@ -8,7 +8,7 @@ /datum/supply_pack/food/donkpockets name = "Donk Pocket Variety Crate" desc = "Featuring a line up of Donk Co.'s most popular pastry!" - cost = 2000 + cost = 500 contains = list(/obj/item/storage/box/donkpockets/donkpocketspicy, /obj/item/storage/box/donkpockets/donkpocketteriyaki, /obj/item/storage/box/donkpockets/donkpocketpizza, @@ -25,7 +25,7 @@ /datum/supply_pack/food/pizza name = "Pizza Crate" desc = "Best prices on this side of the galaxy. All deliveries are guaranteed to be 99.5% anomaly-free!" - cost = 3000// Best prices this side of the galaxy. + cost = 750// Best prices this side of the galaxy. contains = list(/obj/item/pizzabox/margherita, /obj/item/pizzabox/mushroom, /obj/item/pizzabox/meat, @@ -42,6 +42,19 @@ fourfiveeight.boxtag = P.boxtag qdel(P) +/datum/supply_pack/food/ration + name = "Ration Crate" + desc = "6 standerd issue rations." + cost = 500 + contains = list(/obj/effect/spawner/lootdrop/ration, + /obj/effect/spawner/lootdrop/ration, + /obj/effect/spawner/lootdrop/ration, + /obj/effect/spawner/lootdrop/ration, + /obj/effect/spawner/lootdrop/ration, + /obj/effect/spawner/lootdrop/ration) + crate_name = "ration crate" + crate_type = /obj/structure/closet/crate + /* Ingredients */ @@ -49,7 +62,7 @@ /datum/supply_pack/food/ingredients_basic name = "Basic Ingredients Crate" desc = "Get things cooking with this crate full of useful ingredients! Contains a dozen eggs, two slabs of meat, some flour, some rice, a bottle of milk, a bottle of soymilk, and a bag of sugar." - cost = 1000 + cost = 300 contains = list(/obj/item/reagent_containers/food/condiment/flour, /obj/item/reagent_containers/food/condiment/flour, /obj/item/reagent_containers/food/condiment/rice, @@ -63,16 +76,17 @@ crate_name = "food crate" crate_type = /obj/structure/closet/crate/freezer -/datum/supply_pack/food/ingredients_specialized - name = "Advanced Cooking Crate" - desc = "For the discerning chef. Contains a bottle of enzyme, a salt shaker, a pepper mill, a bottle of ketchup, a bottle of hot sauce, and a bottle of cream." - cost = 2000 +/datum/supply_pack/food/ingredients_condiments + name = "Condiments Crate" + desc = "A variety of garnishes for topping off your dish with a little extra pizzaz. Contains a bottle of enzyme, a salt shaker, a pepper mill, a bottle of ketchup, a bottle of hot sauce, a bottle of BBQ sauce, and a bottle of cream." + cost = 250 contains = list(/obj/item/reagent_containers/food/condiment/enzyme, /obj/item/reagent_containers/food/condiment/saltshaker, /obj/item/reagent_containers/food/condiment/peppermill, /obj/item/reagent_containers/food/condiment/ketchup, /obj/item/reagent_containers/food/condiment/hotsauce, - /obj/item/reagent_containers/food/drinks/bottle/cream + /obj/item/reagent_containers/food/drinks/bottle/cream, + /obj/item/reagent_containers/food/condiment/bbqsauce ) crate_name = "condiments crate" crate_type = /obj/structure/closet/crate/freezer @@ -80,7 +94,7 @@ /datum/supply_pack/food/ingredients_randomized name = "Exotic Meat Crate" desc = "The best cuts in the whole galaxy. Probably." - cost = 1000 + cost = 500 contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime, /obj/item/reagent_containers/food/snacks/meat/slab/killertomato, /obj/item/reagent_containers/food/snacks/meat/slab/bear, @@ -103,7 +117,7 @@ /datum/supply_pack/food/ingredients_randomized/meat name = "Standard Meat Crate" desc = "Less interesting cuts of meat, but filling nonetheless." - cost = 1500 + cost = 300 contains = list(/obj/item/reagent_containers/food/snacks/meat/slab, /obj/item/reagent_containers/food/snacks/meat/slab/chicken, /obj/item/reagent_containers/food/snacks/meat/slab/synthmeat, @@ -116,7 +130,7 @@ /datum/supply_pack/food/ingredients_randomized/vegetables name = "Vegetables Crate" desc = "Grown in vats." - cost = 1300 + cost = 250 contains = list(/obj/item/reagent_containers/food/snacks/grown/chili, /obj/item/reagent_containers/food/snacks/grown/corn, /obj/item/reagent_containers/food/snacks/grown/tomato, @@ -132,7 +146,7 @@ /datum/supply_pack/food/ingredients_randomized/fruits name = "Fruit Crate" desc = "Rich of vitamins, may contain oranges." - cost = 1500 + cost = 250 contains = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lime, /obj/item/reagent_containers/food/snacks/grown/citrus/orange, /obj/item/reagent_containers/food/snacks/grown/citrus/lemon, @@ -147,7 +161,7 @@ /datum/supply_pack/food/ingredients_randomized/grains name = "Grains Crate" desc = "A crate full of various grains. How interesting." - cost = 1000 + cost = 250 contains = list(/obj/item/reagent_containers/food/snacks/grown/wheat, /obj/item/reagent_containers/food/snacks/grown/wheat, /obj/item/reagent_containers/food/snacks/grown/wheat, //Weighted to be more common @@ -162,7 +176,7 @@ /datum/supply_pack/food/ingredients_randomized/bread name = "Bread Crate" desc = "A crate full of various breads. Bready to either be eaten or made into delicious meals." - cost = 1000 + cost = 300 contains = list(/obj/item/food/bread/plain, /obj/item/food/breadslice/plain, /obj/item/food/breadslice/plain, @@ -181,17 +195,16 @@ /datum/supply_pack/food/grill name = "Grilling Starter Kit" desc = "Sometimes the stresses of the world are too much to bear. Some times, for God's sake, you just want to grill. This crate is for those times." - cost = 5000 + cost = 1000 contains = list(/obj/item/stack/sheet/mineral/coal/five, - /obj/machinery/grill/unwrenched, - /obj/item/reagent_containers/food/drinks/soda_cans/xeno_energy) + /obj/machinery/grill/unwrenched) crate_name = "grilling starter kit crate" crate_type = /obj/structure/closet/crate/large /datum/supply_pack/food/grillfuel name = "Grilling Fuel Kit" desc = "Contains propane and propane accessories. (Note: doesn't contain any actual propane.)" - cost = 2000 + cost = 250 contains = list(/obj/item/stack/sheet/mineral/coal/ten) crate_name = "grilling fuel kit crate" @@ -202,7 +215,7 @@ /datum/supply_pack/food/hydrotank name = "Hydroponics Backpack Crate" desc = "Bring on the flood with this high-capacity backpack crate. Contains 500 units of life-giving H2O." - cost = 1000 + cost = 750 contains = list(/obj/item/watertank) crate_name = "hydroponics backpack crate" crate_type = /obj/structure/closet/crate/hydroponics @@ -210,7 +223,7 @@ /datum/supply_pack/food/gardening name = "Gardening Crate" desc = "Supplies for growing a great garden! Contains two bottles of ammonia, two Plant-B-Gone spray bottles, a hatchet, cultivator, plant analyzer, as well as a pair of leather gloves and a botanist's apron." - cost = 1500 + cost = 500 contains = list(/obj/item/reagent_containers/spray/plantbgone, /obj/item/reagent_containers/spray/plantbgone, /obj/item/reagent_containers/glass/bottle/ammonia, @@ -227,7 +240,7 @@ /datum/supply_pack/food/weedcontrol name = "Weed Control Crate" desc = "Contains a scythe, gasmask, and two anti-weed defoliant grenades, for when your garden grows out of control." - cost = 1500 + cost = 750 contains = list(/obj/item/scythe, /obj/item/clothing/mask/gas, /obj/item/grenade/chem_grenade/antiweed, @@ -238,7 +251,7 @@ /datum/supply_pack/food/seeds name = "Seeds Crate" desc = "Big things have small beginnings. Contains fourteen different seeds." - cost = 2000 + cost = 750 contains = list(/obj/item/seeds/chili, /obj/item/seeds/cotton, /obj/item/seeds/berry, @@ -259,7 +272,7 @@ /datum/supply_pack/food/exoticseeds name = "Exotic Seeds Crate" desc = "Any entrepreneuring botanist's dream. Contains eleven different seeds, including two mystery seeds!" - cost = 3000 + cost = 1000 contains = list(/obj/item/seeds/nettle, /obj/item/seeds/plump, /obj/item/seeds/liberty, @@ -281,7 +294,7 @@ /datum/supply_pack/food/beekeeping_suits name = "Beekeeper Suit Crate" desc = "Bee business booming? Better be benevolent and boost botany by bestowing bi-Beekeeper-suits! Contains two beekeeper suits and matching headwear." - cost = 2000 + cost = 1000 contains = list(/obj/item/clothing/head/beekeeper_head, /obj/item/clothing/suit/beekeeper_suit, /obj/item/clothing/head/beekeeper_head, @@ -292,7 +305,7 @@ /datum/supply_pack/food/beekeeping_fullkit name = "Beekeeping Starter Crate" desc = "BEES BEES BEES. Contains three honey frames, a beekeeper suit and helmet, flyswatter, bee house, and, of course, a pure-bred Nanotrasen-Standardized Queen Bee!" - cost = 3000 + cost = 2000 contains = list(/obj/structure/beebox/unwrenched, /obj/item/honey_frame, /obj/item/honey_frame, @@ -304,16 +317,5 @@ crate_name = "beekeeping starter crate" crate_type = /obj/structure/closet/crate/hydroponics -/datum/supply_pack/food/ration - name = "Ration Crate" - desc = "6 standerd issue rations." - cost = 2000 - contains = list(/obj/effect/spawner/lootdrop/ration, - /obj/effect/spawner/lootdrop/ration, - /obj/effect/spawner/lootdrop/ration, - /obj/effect/spawner/lootdrop/ration, - /obj/effect/spawner/lootdrop/ration, - /obj/effect/spawner/lootdrop/ration) - crate_name = "ration crate" - crate_type = /obj/structure/closet/crate + diff --git a/code/modules/client/loadout/loadout_accessories.dm b/code/modules/client/loadout/loadout_accessories.dm index c1e4d7a088a8..a8acc1544654 100644 --- a/code/modules/client/loadout/loadout_accessories.dm +++ b/code/modules/client/loadout/loadout_accessories.dm @@ -50,7 +50,39 @@ display_name = "tie, recolorable" path = /obj/item/clothing/neck/tie +//Gloves + +/datum/gear/accessory/gloves + subtype_path = /datum/gear/accessory/gloves + slot = ITEM_SLOT_GLOVES + +/datum/gear/accessory/gloves/black + display_name = "gloves, black" + description = "Standard hand coverings for everyday use." + path = /obj/item/clothing/gloves/color/black + +/datum/gear/accessory/gloves/white + display_name = "gloves, white" + description = "Standard hand coverings for everyday use." + path = /obj/item/clothing/gloves/color/white + +/datum/gear/accessory/gloves/brown + display_name = "gloves, brown" + description = "Standard hand coverings for everyday use." + path = /obj/item/clothing/gloves/color/brown + +/datum/gear/accessory/gloves/fingerless + display_name = "gloves, fingerless" + description = "Radical hand coverings for everyday use." + path = /obj/item/clothing/gloves/fingerless + +/datum/gear/accessory/gloves/evening + display_name = "gloves, evening" + description = "Excessively fancy elbow-length gloves." + path = /obj/item/clothing/gloves/color/evening + //Bone + /datum/gear/accessory/fangnecklace display_name = "wolf fang necklace" path = /obj/item/clothing/neck/fangnecklace @@ -60,6 +92,36 @@ path = /obj/item/clothing/accessory/bonearmlet slot = null +//Masks + +/datum/gear/accessory/mask + subtype_path = /datum/gear/accessory/mask + slot = ITEM_SLOT_MASK + +/datum/gear/accessory/mask/bandana/red + display_name = "bandana, red" + path = /obj/item/clothing/mask/bandana/red + +/datum/gear/accessory/mask/bandana/skull + display_name = "bandana, skull" + path = /obj/item/clothing/mask/bandana/skull + +/datum/gear/accessory/mask/bandana/black + display_name = "bandana, black" + path = /obj/item/clothing/mask/bandana/black + +/datum/gear/accessory/mask/bandana/blue + display_name = "bandana, blue" + path = /obj/item/clothing/mask/bandana/blue + +/datum/gear/accessory/mask/surgical + display_name = "surgical mask" + path = /obj/item/clothing/mask/surgical + +/datum/gear/accessory/mask/balaclava + display_name = "balaclava" + path = /obj/item/clothing/mask/balaclava + //Misc /datum/gear/accessory/waistcoat @@ -72,23 +134,11 @@ path = /obj/item/clothing/neck/stethoscope allowed_roles = list("Medical Doctor", "Chief Medical Officer") -/datum/gear/accessory/gloves/black - display_name = "black gloves" - description = "Standard hand coverings for everyday use." - path = /obj/item/clothing/gloves/color/black - -/datum/gear/accessory/gloves/white - display_name = "white gloves" - description = "Standard hand coverings for everyday use." - path = /obj/item/clothing/gloves/color/white +/datum/gear/accessory/headphones + display_name = "headphones" + slot = ITEM_SLOT_EARS + path = /obj/item/instrument/piano_synth/headphones -/datum/gear/accessory/gloves/fingerless - display_name = "fingerless gloves" - description = "Radical hand coverings for everyday use." - path = /obj/item/clothing/gloves/fingerless - -/datum/gear/accessory/gloves/evening - display_name = "evening gloves" - description = "Excessively fancy elbow-length gloves." - path = /obj/item/clothing/gloves/color/evening - slot = ITEM_SLOT_GLOVES +/datum/gear/accessory/pocketprotector + display_name = "pocket protector" + path = /obj/item/clothing/accessory/pocketprotector diff --git a/code/modules/client/loadout/loadout_eyewear.dm b/code/modules/client/loadout/loadout_eyewear.dm index 99e868ad0854..3ea37d68fed8 100644 --- a/code/modules/client/loadout/loadout_eyewear.dm +++ b/code/modules/client/loadout/loadout_eyewear.dm @@ -46,7 +46,10 @@ description = "A blindfold you can still see through." path = /obj/item/clothing/glasses/trickblindfold - +/datum/gear/eyewear/doubleeyepatch + display_name = "double eyepatch" + description = "Two eyepatches at once! Effectively a blindfold, though." + path = /obj/item/clothing/glasses/blindfold/eyepatch /datum/gear/eyewear/glasses/cold display_name = "cold goggles" diff --git a/code/modules/client/loadout/loadout_footwear.dm b/code/modules/client/loadout/loadout_footwear.dm index dd4632f9bc34..3e65cd9ed5d3 100644 --- a/code/modules/client/loadout/loadout_footwear.dm +++ b/code/modules/client/loadout/loadout_footwear.dm @@ -64,3 +64,17 @@ /datum/gear/footwear/color/white display_name = "white shoes" path = /obj/item/clothing/shoes/sneakers/white + +//Cowboy boots + +/datum/gear/footwear/cowboy + display_name = "cowboy boots, brown" + path = /obj/item/clothing/shoes/cowboy + +/datum/gear/footwear/cowboy/black + display_name = "cowboy boots, black" + path = /obj/item/clothing/shoes/cowboy/black + +/datum/gear/footwear/cowboy/white + display_name = "cowboy boots, white" + path = /obj/item/clothing/shoes/cowboy/white diff --git a/code/modules/client/loadout/loadout_general.dm b/code/modules/client/loadout/loadout_general.dm index 8bb3ff3cb69d..712500f9fd62 100644 --- a/code/modules/client/loadout/loadout_general.dm +++ b/code/modules/client/loadout/loadout_general.dm @@ -14,10 +14,6 @@ display_name = "lipstick, red" path = /obj/item/lipstick -/datum/gear/balaclava - display_name = "balaclava" - path = /obj/item/clothing/mask/balaclava - /datum/gear/vape display_name = "vape" path = /obj/item/clothing/mask/vape @@ -26,10 +22,6 @@ display_name = "e-cigar" path = /obj/item/clothing/mask/vape/cigar -/datum/gear/bandana - display_name = "bandana, red" - path = /obj/item/clothing/mask/bandana/red - /datum/gear/flask display_name = "flask" path = /obj/item/reagent_containers/food/drinks/flask @@ -58,12 +50,17 @@ display_name = "toy, deck of cards" path = /obj/item/toy/cards/deck +/datum/gear/kotahi + display_name = "toy, deck of KOTAHI cards" + path = /obj/item/toy/cards/deck/kotahi + /datum/gear/eightball display_name = "toy, magic eight ball" path = /obj/item/toy/eightball /datum/gear/pai display_name = "personal AI device" + description = "A synthetic friend that fits in your pocket." path = /obj/item/paicard /datum/gear/tablet @@ -86,6 +83,10 @@ display_name = "pen, four-color" path = /obj/item/pen/fourcolor +/datum/gear/fountainpen + display_name = "pen, fountain" + path = /obj/item/pen/fountain + /datum/gear/paperbin display_name = "paper bin" path = /obj/item/paper_bin @@ -127,7 +128,6 @@ display_name = "toy, rilena tali plushie" path = /obj/item/toy/plush/tali -// Shiptest edit /datum/gear/amongus display_name = "toy, suspicious pill plushie" path = /obj/item/toy/plush/among @@ -150,8 +150,6 @@ display_name = "table bell, brass" path = /obj/item/table_bell/brass -// End Shiptest - /datum/gear/flashlight display_name = "tool, flashlight" path = /obj/item/flashlight @@ -160,11 +158,11 @@ display_name = "tool, emergency crowbar" path = /obj/item/crowbar/red -/datum/gear/surgical_mask - display_name = "surgical mask" - path = /obj/item/clothing/mask/surgical - /datum/gear/rilena_poster display_name = "poster, rilena" path = /obj/item/poster/random_rilena description = "A random poster of the RILENA series." + +/datum/gear/camera + display_name = "polaroid camera" + path = /obj/item/camera diff --git a/code/modules/client/loadout/loadout_hat.dm b/code/modules/client/loadout/loadout_hat.dm index 23e34d7d19c3..32384a0d59fa 100644 --- a/code/modules/client/loadout/loadout_hat.dm +++ b/code/modules/client/loadout/loadout_hat.dm @@ -27,6 +27,10 @@ display_name = "beret, red" path = /obj/item/clothing/head/beret +/datum/gear/hat/beret/black + display_name = "beret, black" + path = /obj/item/clothing/head/beret/black + /datum/gear/hat/beret/departmental display_name = "beret, departmental" path = /obj/item/clothing/head/beret/grey @@ -74,24 +78,83 @@ path = /obj/item/clothing/head/beret/eng/hazard allowed_roles = list("Station Engineer", "Atmospheric Technician", "Chief Engineer") +//Soft caps + +/datum/gear/hat/softcap/red + display_name = "cap, red" + path = /obj/item/clothing/head/soft/red + +/datum/gear/hat/softcap/blue + display_name = "cap, blue" + path = /obj/item/clothing/head/soft/blue + +/datum/gear/hat/softcap/grey + display_name = "cap, grey" + path = /obj/item/clothing/head/soft/grey + +/datum/gear/hat/softcap/white + display_name = "cap, white" + path = /obj/item/clothing/head/soft/mime + +/datum/gear/hat/softcap/black + display_name = "cap, black" + path = /obj/item/clothing/head/soft/black + +//Beanies + +/datum/gear/hat/beanie + display_name = "beanie, white" + path = /obj/item/clothing/head/beanie + +/datum/gear/hat/beanie/black + display_name = "beanie, black" + path = /obj/item/clothing/head/beanie/black + +/datum/gear/hat/beanie/red + display_name = "beanie, red" + path = /obj/item/clothing/head/beanie/red + +/datum/gear/hat/beanie/green + display_name = "beanie, green" + path = /obj/item/clothing/head/beanie/green + +/datum/gear/hat/beanie/purple + display_name = "beanie, purple" + path = /obj/item/clothing/head/beanie/purple + +/datum/gear/hat/beanie/blue + display_name = "beanie, blue" + path = /obj/item/clothing/head/beanie/darkblue + +/datum/gear/hat/beanie/orange + display_name = "beanie, orange" + path = /obj/item/clothing/head/beanie/orange //Misc +/datum/gear/hat/bowler + display_name = "bowler hat" + path = /obj/item/clothing/head/bowler + /datum/gear/hat/that display_name = "top hat" path = /obj/item/clothing/head/that /datum/gear/hat/fedora - display_name = "fedora" + display_name = "fedora, black" path = /obj/item/clothing/head/fedora +/datum/gear/hat/fedora/white + display_name = "fedora, white" + path = /obj/item/clothing/head/fedora/white + +/datum/gear/hat/fedora/beige + display_name = "fedora, beige" + path = /obj/item/clothing/head/fedora/beige + /datum/gear/hat/flatcap display_name = "flatcap" path = /obj/item/clothing/head/flatcap -/datum/gear/hat/beanie - display_name = "beanie" - path = /obj/item/clothing/head/beanie - /datum/gear/hat/wig display_name = "wig" path = /obj/item/clothing/head/wig diff --git a/code/modules/client/loadout/loadout_suit.dm b/code/modules/client/loadout/loadout_suit.dm index f8757bfa5b38..217998802d48 100644 --- a/code/modules/client/loadout/loadout_suit.dm +++ b/code/modules/client/loadout/loadout_suit.dm @@ -55,8 +55,6 @@ display_name = "suit jacket, charcoal" path = /obj/item/clothing/suit/toggle/lawyer/charcoal -/datum/gear/suit/jacket/navy //why is this blank? i dont know - /datum/gear/suit/jacket/hoodie_black display_name = "hoodie, black" path = /obj/item/clothing/suit/hooded/hoodie/black @@ -93,6 +91,19 @@ display_name = "hazard jacket" path = /obj/item/clothing/suit/toggle/hazard +//Suspenders +/datum/gear/suit/suspenders/red + display_name = "suspenders, red" + path = /obj/item/clothing/suit/toggle/suspenders + +/datum/gear/suit/suspenders/blue + display_name = "suspenders, blue" + path = /obj/item/clothing/suit/toggle/suspenders/blue + +/datum/gear/suit/suspenders/gray + display_name = "suspenders, gray" + path = /obj/item/clothing/suit/toggle/suspenders/gray + //Misc /datum/gear/suit/grponcho display_name = "poncho, green" @@ -119,3 +130,11 @@ display_name = "floral shirt" description = "From grills to guns, this shirt's seen it all." path = /obj/item/clothing/suit/hawaiian + +/datum/gear/suit/hazardvest + display_name = "hazard vest" + path = /obj/item/clothing/suit/hazardvest + +/datum/gear/suit/longcoat + display_name = "longcoat" + path = /obj/item/clothing/suit/longcoat diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 006074e74bcd..317828283dbd 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1435,7 +1435,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) balance -= initial(quirk_type.value) switch(change_type) if("species") - if((quirk_name in SSquirks.species_blacklist) && (pref_species.id in SSquirks.species_blacklist[quirk_name])) + if((quirk_name in SSquirks.species_blacklist) && (target_species.id in SSquirks.species_blacklist[quirk_name])) all_quirks_new -= quirk_name balance += initial(quirk_type.value) if("mood") diff --git a/code/modules/clothing/factions/clip.dm b/code/modules/clothing/factions/clip.dm index ce87ea624d7a..c8a0dbdda278 100644 --- a/code/modules/clothing/factions/clip.dm +++ b/code/modules/clothing/factions/clip.dm @@ -415,26 +415,31 @@ supports_variations = VOX_VARIATION -/obj/item/storage/belt/military/clip/p16/PopulateContents() +/obj/item/storage/belt/military/clip/cm82/PopulateContents() for(var/i in 1 to 4) new /obj/item/ammo_box/magazine/p16(src) new /obj/item/grenade/frag(src) -/obj/item/storage/belt/military/clip/gal/PopulateContents() +/obj/item/storage/belt/military/clip/f4/PopulateContents() for(var/i in 1 to 4) - new /obj/item/ammo_box/magazine/gal(src) + new /obj/item/ammo_box/magazine/f4_308(src) new /obj/item/grenade/frag(src) /obj/item/storage/belt/military/clip/cm5/PopulateContents() for(var/i in 1 to 4) - new /obj/item/ammo_box/magazine/smgm9mm(src) + new /obj/item/ammo_box/magazine/cm5_9mm(src) new /obj/item/grenade/frag(src) /obj/item/storage/belt/military/clip/cm15/PopulateContents() for(var/i in 1 to 5) - new /obj/item/ammo_box/magazine/cm15_mag(src) + new /obj/item/ammo_box/magazine/cm15_12g(src) new /obj/item/grenade/frag(src) +/obj/item/storage/belt/military/clip/e50/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_w_class = WEIGHT_CLASS_NORMAL + /obj/item/storage/belt/military/clip/e50/PopulateContents() for(var/i in 1 to 5) new /obj/item/stock_parts/cell/gun/large(src) diff --git a/code/modules/clothing/factions/frontiersmen.dm b/code/modules/clothing/factions/frontiersmen.dm index 9bac6c40a004..d232f775e607 100644 --- a/code/modules/clothing/factions/frontiersmen.dm +++ b/code/modules/clothing/factions/frontiersmen.dm @@ -10,6 +10,7 @@ can_adjust = FALSE icon = 'icons/obj/clothing/faction/frontiersmen/uniforms.dmi' mob_overlay_icon = 'icons/mob/clothing/faction/frontiersmen/uniforms.dmi' + supports_variations = VOX_VARIATION /obj/item/clothing/under/frontiersmen/deckhand name = "\improper deckhand jumpsuit" @@ -58,6 +59,7 @@ icon = 'icons/obj/clothing/faction/frontiersmen/suits.dmi' mob_overlay_icon = 'icons/mob/clothing/faction/frontiersmen/suits.dmi' blood_overlay_type = "armor" + supports_variations = VOX_VARIATION /obj/item/clothing/suit/armor/vest/marine/frontier name = "light tactical armor vest" @@ -162,17 +164,20 @@ name = "\improper frontiersmen commander's cap" desc = "An imposing peaked cap, meant for a commander of the Frontiersmen." icon_state = "frontier_cap" + supports_variations = VOX_VARIATION /obj/item/clothing/head/frontier/admiral name = "\improper frontiersmen admiral's cap" desc = "An imposing peaked cap meant for only the highest of officers of the Frontiersmen pirate fleet." icon_state = "frontier_admiral_cap" + supports_variations = VOX_VARIATION /obj/item/clothing/head/helmet/bulletproof/x11/frontier name = "\improper frontiersmen X-11 helmet" desc = "A heavily modified X-11 pattern helmet used by the Frontiersmen pirate fleet." icon_state = "x11helm_frontier" unique_reskin = null + supports_variations = VOX_VARIATION /obj/item/clothing/head/helmet/bulletproof/x11/frontier/fireproof name = "\improper fireproof frontiersmen X-11 helmet" @@ -249,9 +254,14 @@ new /obj/item/ammo_box/magazine/skm_762_40(src) new /obj/item/grenade/frag(src) -/obj/item/storage/belt/security/military/frontiersmen/aps_mp_ammo/PopulateContents() //replace with spitter. remind me. +/obj/item/storage/belt/security/military/frontiersmen/mauler_mp_ammo/PopulateContents() for(var/i in 1 to 4) - new /obj/item/ammo_box/magazine/pistolm9mm(src) + new /obj/item/ammo_box/magazine/m9mm_mauler(src) + new /obj/item/grenade/frag(src) + +/obj/item/storage/belt/security/military/frontiersmen/spitter_ammo/PopulateContents() + for(var/i in 1 to 4) + new /obj/item/ammo_box/magazine/spitter_9mm(src) new /obj/item/grenade/frag(src) /obj/item/storage/belt/security/military/frontiersmen/flamer/PopulateContents() diff --git a/code/modules/clothing/factions/gezena.dm b/code/modules/clothing/factions/gezena.dm index 96c6eee3c734..eabc0fe752c4 100644 --- a/code/modules/clothing/factions/gezena.dm +++ b/code/modules/clothing/factions/gezena.dm @@ -36,10 +36,12 @@ item_state = "bluecloth" blood_overlay_type = "coat" togglename = "zipper" - body_parts_covered = CHEST + body_parts_covered = CHEST|GROIN|ARMS + cold_protection = CHEST|GROIN|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) pocket_storage_component_path = /datum/component/storage/concrete/pockets/exo supports_variations = DIGITIGRADE_VARIATION_NO_NEW_ICON - armor = list("melee" = 20, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0) //Armored suit @@ -130,6 +132,9 @@ desc = "The standard cap of the PGF military, in Navy colors. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects." icon_state = "navalflap" item_state = "bluecloth" + cold_protection = HEAD + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) /obj/item/clothing/head/gezena/marine name = "\improper PGFMC Cap" @@ -137,7 +142,7 @@ icon_state = "marinehat" item_state = "marinecloth" -/obj/item/clothing/head/gezena/marine/flap +/obj/item/clothing/head/gezena/flap/marine name = "\improper PGFMC Betzu-il cap" desc = "The standard cap of the PGF military, in Marine Corps colors. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects." icon_state = "marineflap" @@ -149,7 +154,7 @@ icon_state = "squadhat" item_state = "marinecloth" -/obj/item/clothing/head/gezena/marine/lead/flap +/obj/item/clothing/head/gezena/flap/marine/lead name = "\improper PGFMC Commander's' Betzu-il cap" desc = "The standard cap of the PGF military, in Marine Corps colors. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects. The silver markings denote it as a commander's cap." icon_state = "squadflap" @@ -161,7 +166,7 @@ icon_state = "medichat" item_state = "whitecloth" -/obj/item/clothing/head/gezena/medic/flap +/obj/item/clothing/head/gezena/flap/medic name = "\improper PGF medic Betzu-il cap" desc = "The standard cap of the PGF military. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects. The coloring indicates the wearer as a medical officer." icon_state = "medicflap" @@ -260,8 +265,8 @@ item_state = "blackcloth" /obj/item/clothing/neck/cloak/gezena/lead - name = "sergeant's Azuilhauz" - desc = "The “Aziulhauz”, or “rank-cape”, is the method with which PGF military members display their rank to others. Wearing one while on duty is required by uniform code. This variant displays the wearer's rank as a squad commander." + name = "marine officer's Azuilhauz" + desc = "The “Aziulhauz”, or “rank-cape”, is the method with which PGF military members display their rank to others. Wearing one while on duty is required by uniform code. This variant displays the wearer's rank as a squad leader." icon_state = "squadcape" item_state = "blackcloth" @@ -278,7 +283,7 @@ item_state = "whitecloth" /obj/item/clothing/neck/cloak/gezena/command - name = "officer's Azuilhauz" + name = "navy officer's Azuilhauz" desc = "The “Aziulhauz”, or “rank-cape”, is the method with which PGF military members display their rank to others. Wearing one while on duty is required by uniform code. This variant displays the wearer's rank as an officer." icon_state = "commandcape" item_state = "whitecloth" diff --git a/code/modules/clothing/factions/ngr.dm b/code/modules/clothing/factions/ngr.dm index aa094e1a1ed0..4a70d10327c6 100644 --- a/code/modules/clothing/factions/ngr.dm +++ b/code/modules/clothing/factions/ngr.dm @@ -231,6 +231,7 @@ icon_state = "ngr_shemagh" icon = 'icons/obj/clothing/faction/ngr/neck.dmi' mob_overlay_icon = 'icons/mob/clothing/faction/ngr/neck.dmi' + supports_variations = VOX_VARIATION ////////// //Belts// diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 59530e24a542..c1b54fc5adc5 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -378,7 +378,7 @@ colored_before = TRUE /obj/item/clothing/glasses/blindfold/white/worn_overlays(isinhands = FALSE, file2use) - . = list() + . = ..() if(!isinhands && ishuman(loc) && !colored_before) var/mob/living/carbon/human/H = loc var/mutable_appearance/M = mutable_appearance('icons/mob/clothing/eyes.dmi', "blindfoldwhite") diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 61c06125d8f8..a6e9f22ea3e7 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -25,7 +25,7 @@ return TRUE /obj/item/clothing/gloves/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves") diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm index 4039402588fd..aa1114e6b182 100644 --- a/code/modules/clothing/head/_head.dm +++ b/code/modules/clothing/head/_head.dm @@ -60,7 +60,7 @@ /obj/item/clothing/head/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet") diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 24e2f95f03bd..5b8e228b49ee 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -241,7 +241,7 @@ return ..() /obj/item/clothing/head/wig/worn_overlays(isinhands = FALSE, file2use) - . = list() + . = ..() if(!isinhands) var/datum/sprite_accessory/S = GLOB.hairstyles_list[hairstyle] if(!S) diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm index 03ca246b60af..a4c1d5d509fa 100644 --- a/code/modules/clothing/masks/_masks.dm +++ b/code/modules/clothing/masks/_masks.dm @@ -32,7 +32,7 @@ /obj/item/clothing/mask/proc/handle_speech() /obj/item/clothing/mask/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(body_parts_covered & HEAD) if(damaged_clothes) diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 3f431df81ccb..2165baaa2b0e 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -11,7 +11,7 @@ greyscale_icon_state = "scarf" /obj/item/clothing/neck/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(body_parts_covered & HEAD) if(damaged_clothes) @@ -167,6 +167,7 @@ desc = "An outdated medical apparatus for listening to the sounds of the human body. It also makes you look like you know what you're doing." icon_state = "stethoscope" cuttable = FALSE + supports_variations = VOX_VARIATION /obj/item/clothing/neck/stethoscope/attack(mob/living/carbon/human/M, mob/living/user) if(ishuman(M) && isliving(user)) @@ -267,6 +268,7 @@ name = "shemagh" desc = "An oversized shemagh, for those with a keen sense of fashion, or those operating tactically." icon_state = "shemagh" + supports_variations = VOX_VARIATION //The three following scarves don't have the scarf subtype //This is because Ian can equip anything from that subtype @@ -275,21 +277,25 @@ name = "striped red scarf" icon_state = "stripedredscarf" custom_price = 10 + supports_variations = VOX_VARIATION /obj/item/clothing/neck/stripedgreenscarf name = "striped green scarf" icon_state = "stripedgreenscarf" custom_price = 10 + supports_variations = VOX_VARIATION /obj/item/clothing/neck/stripedbluescarf name = "striped blue scarf" icon_state = "stripedbluescarf" custom_price = 10 + supports_variations = VOX_VARIATION /obj/item/clothing/neck/stripedsolgovscarf name = "striped solgov scarf" icon_state = "stripedsolgovscarf" custom_price = 10 + supports_variations = VOX_VARIATION /obj/item/clothing/neck/petcollar name = "pet collar" diff --git a/code/modules/clothing/outfits/ert/frontiersmen_ert.dm b/code/modules/clothing/outfits/ert/frontiersmen_ert.dm index 1ab6b3cf320e..d68c49e61778 100644 --- a/code/modules/clothing/outfits/ert/frontiersmen_ert.dm +++ b/code/modules/clothing/outfits/ert/frontiersmen_ert.dm @@ -139,16 +139,29 @@ backpack_contents = list(/obj/item/clothing/mask/gas/frontiersmen) /datum/outfit/job/frontiersmen/ert/grunt/skm - name = "ERT - Frontiersman Grunt (SKM-24)" + name = "ERT - Frontiersman Grunt (SKM-24 AR)" suit_store = /obj/item/gun/ballistic/automatic/assault/skm belt = /obj/item/storage/belt/security/military/frontiersmen/skm_ammo -/datum/outfit/job/frontiersmen/ert/grunt/aps_mp //remember. Remind me to replace this with the spitter. - name = "ERT - Frontiersman Grunt (Stechkin APS)" +/datum/outfit/job/frontiersmen/ert/grunt/mauler_mp + name = "ERT - Frontiersman Grunt (Mauler MP)" - suit_store = /obj/item/gun/ballistic/automatic/pistol/APS - belt = /obj/item/storage/belt/security/military/frontiersmen/aps_mp_ammo + suit_store = /obj/item/gun/ballistic/automatic/pistol/mauler + belt = /obj/item/storage/belt/security/military/frontiersmen/mauler_mp_ammo + +/datum/outfit/job/frontiersmen/ert/grunt/spitter_mp + name = "ERT - Frontiersman Grunt (Spitter MP)" + + suit_store = /obj/item/gun/ballistic/automatic/pistol/spitter + belt = /obj/item/storage/belt/security/military/frontiersmen/spitter_ammo + +/datum/outfit/job/frontiersmen/ert/grunt/pounder_smg + name = "ERT - Frontiersman Grunt (Pounder SMG)" + + suit_store = /obj/item/gun/ballistic/automatic/smg/pounder + belt = null + backpack_contents = list(/obj/item/clothing/mask/gas/frontiersmen=1, /obj/item/ammo_box/magazine/c22lr_pounder_pan=2) /datum/outfit/job/frontiersmen/ert/leader name = "ERT - Frontiersman Officer" @@ -177,9 +190,9 @@ mask = /obj/item/clothing/mask/surgical gloves = /obj/item/clothing/gloves/color/latex/nitrile belt = /obj/item/storage/belt/medical/webbing/frontiersmen/surgery - suit_store = /obj/item/gun/ballistic/automatic/pistol/APS + suit_store = /obj/item/gun/ballistic/automatic/pistol/mauler - backpack_contents = list(/obj/item/clothing/mask/gas/frontiersmen, /obj/item/storage/firstaid/medical=1, /obj/item/reagent_containers/hypospray/medipen/stimpack=3, /obj/item/ammo_box/magazine/pistolm9mm=2) + backpack_contents = list(/obj/item/clothing/mask/gas/frontiersmen, /obj/item/storage/firstaid/medical=1, /obj/item/reagent_containers/hypospray/medipen/stimpack=3, /obj/item/ammo_box/magazine/m9mm_mauler=2) /datum/outfit/job/frontiersmen/ert/engineer @@ -211,7 +224,7 @@ /datum/outfit/job/frontiersmen/ert/sentry_lmg - name = "ERT - Frontiersman Sentry (SKM-24v)" + name = "ERT - Frontiersman Sentry (SKM-24v LMG)" head = /obj/item/clothing/head/helmet/marine/frontier mask = /obj/item/clothing/mask/gas/sechailer/balaclava @@ -222,3 +235,18 @@ belt = /obj/item/gun/ballistic/revolver/mateba backpack_contents = list(/obj/item/ammo_box/magazine/skm_762_40/drum=2,/obj/item/ammo_box/a357=2,/obj/item/grenade/frag=1,/obj/item/radio=1) + +/datum/outfit/job/frontiersmen/ert/sentry_shredder + name = "ERT - Frontiersman Sentry (Shredder LMG)" + + head = /obj/item/clothing/head/helmet/marine/frontier + mask = /obj/item/clothing/mask/gas/sechailer/balaclava + suit = /obj/item/clothing/suit/armor/vest/marine/frontier + gloves = /obj/item/clothing/gloves/combat + + belt = /obj/item/gun/ballistic/revolver/mateba + + + l_hand = /obj/item/gun/ballistic/automatic/hmg/shredder // this doesnt even fit on the suit storage slot + + backpack_contents = list(/obj/item/ammo_box/magazine/m12_shredder=2,/obj/item/ammo_box/a357=2,/obj/item/grenade/frag=1,/obj/item/radio=1) diff --git a/code/modules/clothing/outfits/ert/minutemen_ert.dm b/code/modules/clothing/outfits/ert/minutemen_ert.dm index bc440d3bc612..482777814c64 100644 --- a/code/modules/clothing/outfits/ert/minutemen_ert.dm +++ b/code/modules/clothing/outfits/ert/minutemen_ert.dm @@ -4,7 +4,7 @@ job_icon = "clip_cmm2" suit = /obj/item/clothing/suit/armor/vest/marine/heavy - suit_store = /obj/item/gun/ballistic/shotgun/bulldog/minutemen + suit_store = /obj/item/gun/ballistic/shotgun/cm15 mask = /obj/item/clothing/mask/gas/clip head = /obj/item/clothing/head/helmet/riot/clip belt = /obj/item/storage/belt/military/clip/cm15 @@ -58,7 +58,7 @@ belt = /obj/item/storage/belt/military/clip/e50 uniform = /obj/item/clothing/under/clip/officer suit = /obj/item/clothing/suit/armor/vest/marine - suit_store = /obj/item/gun/energy/laser/e50 + suit_store = /obj/item/gun/energy/laser/e50/clip r_pocket = /obj/item/grenade/c4 l_pocket = /obj/item/reagent_containers/hypospray/medipen/stimpack diff --git a/code/modules/clothing/outfits/factions/minutemen.dm b/code/modules/clothing/outfits/factions/minutemen.dm index 5c038b05f181..8a04a0fb76f8 100644 --- a/code/modules/clothing/outfits/factions/minutemen.dm +++ b/code/modules/clothing/outfits/factions/minutemen.dm @@ -552,16 +552,16 @@ backpack_contents = list(/obj/item/clothing/mask/gas/clip=1, /obj/item/storage/ration/chicken_wings_hot_sauce=1) /datum/outfit/job/clip/minutemen/grunt/dressed/armed - name = "CLIP Minutemen - Minuteman (Armed - CM-16)" + name = "CLIP Minutemen - Minuteman (Armed - CM-82)" - suit_store = /obj/item/gun/ballistic/automatic/assault/p16/minutemen - belt = /obj/item/storage/belt/military/clip/p16 + suit_store = /obj/item/gun/ballistic/automatic/assault/cm82 + belt = /obj/item/storage/belt/military/clip/cm82 -/datum/outfit/job/clip/minutemen/grunt/dressed/armed/f4 //f4 is rename of GAL, don't wanna repath upon adding the clip guns though, if i forget to remove this during then, fucking yell at me - name = "CLIP Minutemen - Minuteman (Armed - CM-GAL)" +/datum/outfit/job/clip/minutemen/grunt/dressed/armed/f4 + name = "CLIP Minutemen - Minuteman (Armed - F4)" - suit_store = /obj/item/gun/ballistic/automatic/marksman/gal - belt = /obj/item/storage/belt/military/clip/gal + suit_store = /obj/item/gun/ballistic/automatic/marksman/f4 + belt = /obj/item/storage/belt/military/clip/f4 /datum/outfit/job/clip/minutemen/grunt/dressed/armed/cm5 name = "CLIP Minutemen - Minuteman (Armed - CM-5)" @@ -580,9 +580,9 @@ belt = /obj/item/storage/belt/military/clip/engi /datum/outfit/job/clip/minutemen/grunt/dressed/engi/armed - name = "CLIP Minutemen - Field Engineer (Armed - CM-16)" + name = "CLIP Minutemen - Field Engineer (Armed - CM-82)" - suit_store = /obj/item/gun/ballistic/automatic/assault/p16/minutemen + suit_store = /obj/item/gun/ballistic/automatic/assault/cm82 backpack_contents = list(/obj/item/clothing/mask/gas/clip=1, /obj/item/storage/ration/chili_macaroni=1, /obj/item/grenade/c4=2, /obj/item/ammo_box/magazine/p16=3) /datum/outfit/job/clip/minutemen/grunt/dressed/med @@ -598,20 +598,25 @@ suit_store = /obj/item/gun/ballistic/automatic/smg/cm5 - backpack_contents = list(/obj/item/clothing/mask/gas/clip=1, /obj/item/storage/ration/cheese_pizza_slice, /obj/item/defibrillator/compact/loaded=1, /obj/item/storage/firstaid/medical=1, /obj/item/ammo_box/magazine/smgm9mm=3) + backpack_contents = list(/obj/item/clothing/mask/gas/clip=1, /obj/item/storage/ration/cheese_pizza_slice, /obj/item/defibrillator/compact/loaded=1, /obj/item/storage/firstaid/medical=1, /obj/item/ammo_box/magazine/cm5_9mm=3) + +/obj/item/storage/belt/military/clip/gunner/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_w_class = WEIGHT_CLASS_NORMAL /obj/item/storage/belt/military/clip/gunner/PopulateContents() for(var/i in 1 to 5) - new /obj/item/ammo_box/magazine/skm_762_40/extended(src) + new /obj/item/ammo_box/magazine/cm40_762_40_box(src) new /obj/item/grenade/frag(src) /datum/outfit/job/clip/minutemen/grunt/dressed/gunner_armed - name = "CLIP Minutemen - Field Gunner (Armed - SKM-24u)" //See above, replace with CLIP LMG when added + name = "CLIP Minutemen - Field Gunner (Armed - CM-40)" id_assignment = "Machinegunner" accessory = /obj/item/clothing/accessory/armband belt = /obj/item/storage/belt/military/clip/gunner - suit_store = /obj/item/gun/ballistic/automatic/hmg/skm_lmg/extended + suit_store = /obj/item/gun/ballistic/automatic/hmg/cm40 backpack_contents = list(/obj/item/clothing/mask/gas/clip=1, /obj/item/reagent_containers/food/snacks/rationpack=1) @@ -639,8 +644,8 @@ /datum/outfit/job/clip/minutemen/grunt/lead/armed name = "CLIP Minutemen - Field Sergeant (Armed)" - suit_store = /obj/item/gun/ballistic/automatic/assault/p16/minutemen - belt = /obj/item/storage/belt/military/clip/p16 + suit_store = /obj/item/gun/ballistic/automatic/assault/cm82 + belt = /obj/item/storage/belt/military/clip/cm82 //replace commander with the cm23 when its impemented, see the cm-f4 above backpack_contents = list(/obj/item/clothing/mask/gas/clip=1, /obj/item/reagent_containers/food/snacks/rationpack=1, /obj/item/gun/ballistic/automatic/pistol/commander=1) diff --git a/code/modules/clothing/outfits/factions/syndicate.dm b/code/modules/clothing/outfits/factions/syndicate.dm index 93df3985b26c..479965e756b4 100644 --- a/code/modules/clothing/outfits/factions/syndicate.dm +++ b/code/modules/clothing/outfits/factions/syndicate.dm @@ -931,7 +931,7 @@ l_pocket = /obj/item/restraints/handcuffs r_pocket = /obj/item/assembly/flash/handheld -/datum/outfit/job/syndicate/security/gorlex +/datum/outfit/job/syndicate/security/gorlex/pilot name = "Syndicate - Pilot (Hardliner)" id_assignment = "Pilot" job_icon = "securityofficer" diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 336ac43c7d4d..cd4474588090 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -29,7 +29,7 @@ var/atom/movable/screen/alert/our_alert /obj/item/clothing/shoes/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe") diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 9db68d733bd1..3c54e44cf035 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -734,23 +734,6 @@ item_state = "capspacesuit" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/swat/captain - //Clown -/obj/item/clothing/head/helmet/space/hardsuit/clown - name = "cosmohonk hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-humor environment. Has radiation shielding." - icon_state = "hardsuit0-clown" - item_state = "hardsuit0-clown" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 60, "acid" = 30) - hardsuit_type = "clown" - -/obj/item/clothing/suit/space/hardsuit/clown - name = "cosmohonk hardsuit" - desc = "A special suit that protects against hazardous, low humor environments. Has radiation shielding. Only a true clown can wear it." - icon_state = "hardsuit-clown" - item_state = "clown_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 60, "acid" = 30) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/clown - //Old Prototype /obj/item/clothing/head/helmet/space/hardsuit/ancient name = "prototype RIG hardsuit helmet" @@ -863,7 +846,7 @@ C.update_inv_wear_suit() /obj/item/clothing/suit/space/hardsuit/shielded/worn_overlays(isinhands) - . = list() + . = ..() if(!isinhands) . += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) @@ -1045,7 +1028,7 @@ item_state = "independent_sec_helm" hardsuit_type = "independent-sec" armor = list("melee" = 35, "bullet" = 25, "laser" = 20,"energy" = 40, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - supports_variations = VOX_VARIATION + supports_variations = VOX_VARIATION | SNOUTED_VARIATION /obj/item/clothing/suit/space/hardsuit/security/independent icon_state = "hardsuit-independent-sec" @@ -1056,7 +1039,7 @@ hardsuit_type = "independent-sec" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/independent armor = list("melee" = 35, "bullet" = 25, "laser" = 20, "energy" = 40, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - supports_variations = VOX_VARIATION + supports_variations = VOX_VARIATION | DIGITIGRADE_VARIATION //Mining /obj/item/clothing/head/helmet/space/hardsuit/mining/independent diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index 0e7edb63f068..30d3c3c3c9ba 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -19,7 +19,7 @@ mob_overlay_icon = 'icons/mob/clothing/suit.dmi' /obj/item/clothing/suit/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform") diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index 994b4cf7cf6d..312f14dec507 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -114,7 +114,8 @@ item_state = "highvis" blood_overlay_type = "coat" body_parts_covered = CHEST|ARMS - cold_protection = CHEST|GROIN|ARMS + cold_protection = CHEST|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT togglename = "zipper" allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/t_scanner, /obj/item/radio) resistance_flags = NONE @@ -195,7 +196,7 @@ //Mime /obj/item/clothing/suit/toggle/suspenders name = "suspenders" - desc = "They suspend the illusion of the mime's play." + desc = "The symbol of hard labor and dirty jobs." icon = 'icons/obj/clothing/belts.dmi' icon_state = "suspenders" blood_overlay_type = "armor" //it's the less thing that I can put here diff --git a/code/modules/clothing/towels.dm b/code/modules/clothing/towels.dm index 22c1b29976fc..f35c608a042a 100644 --- a/code/modules/clothing/towels.dm +++ b/code/modules/clothing/towels.dm @@ -32,7 +32,7 @@ item_flags = NOBLUDGEON resistance_flags = FLAMMABLE flags_inv = HIDEHAIR // Only relevant when in head shape, but useful to keep around regardless. - supports_variations = DIGITIGRADE_VARIATION_NO_NEW_ICON + supports_variations = DIGITIGRADE_VARIATION_NO_NEW_ICON | VOX_VARIATION /// The shape we're currently in. var/shape = TOWEL_FOLDED diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index a28d6d323a83..bc8cb512906b 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -27,7 +27,7 @@ supports_variations = VOX_VARIATION /obj/item/clothing/under/worn_overlays(isinhands = FALSE) - . = list() + . = ..() if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform") diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index 6ffea82fb945..ec10f7dfb0f2 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -762,7 +762,7 @@ desc = "If you ever wondered where air came from..." list_reagents = list(/datum/reagent/oxygen = 6, /datum/reagent/nitrogen = 24) icon = 'icons/obj/food/ration.dmi' - icon_state = "ration_package" + icon_state = "ration_drink" drop_sound = 'sound/items/handling/cardboardbox_drop.ogg' pickup_sound = 'sound/items/handling/cardboardbox_pickup.ogg' in_container = TRUE @@ -773,7 +773,7 @@ /obj/item/reagent_containers/food/drinks/ration/proc/open_ration(mob/user) to_chat(user, "You tear open \the [src].") - playsound(user.loc, 'sound/effects/rip3.ogg', 50) + playsound(user.loc, 'sound/items/glass_cap.ogg', 50) reagents.flags |= OPENCONTAINER spillable = TRUE diff --git a/code/modules/food_and_drinks/food/ration.dm b/code/modules/food_and_drinks/food/ration.dm index 261fe707ed7f..6766a6aedbda 100644 --- a/code/modules/food_and_drinks/food/ration.dm +++ b/code/modules/food_and_drinks/food/ration.dm @@ -83,7 +83,7 @@ list_reagents = list(/datum/reagent/consumable/nutriment = 4) /obj/item/reagent_containers/food/snacks/ration/snack - icon_state = "ration_side" + icon_state = "ration_snack" list_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/consumable/sugar = 3) /obj/item/reagent_containers/food/snacks/ration/bar @@ -92,7 +92,7 @@ /obj/item/reagent_containers/food/snacks/ration/condiment name = "condiment pack" - desc = "Just your average condiment pacl." + desc = "Just your average condiment pack." icon_state = "ration_condi" volume = 10 amount_per_transfer_from_this = 10 @@ -126,7 +126,7 @@ /obj/item/reagent_containers/food/snacks/ration/pack name = "powder pack" desc = "Mix into a bottle of water and shake." - icon_state = "ration_condi" + icon_state = "ration_pack" volume = 10 amount_per_transfer_from_this = 10 possible_transfer_amounts = list() @@ -742,84 +742,105 @@ /obj/item/reagent_containers/food/snacks/ration/condiment/cheese_spread name = "cheese spread pack" + filling_color = "#ffcc00" list_reagents = list(/datum/reagent/consumable/cheese_spread = 8) /obj/item/reagent_containers/food/snacks/ration/condiment/hot_cheese_spread name = "jalapeno cheddar cheese spread pack" + filling_color = "#ffaa00" list_reagents = list(/datum/reagent/consumable/cheese_spread = 5 , /datum/reagent/consumable/capsaicin = 3) /obj/item/reagent_containers/food/snacks/ration/condiment/garlic_cheese_spread name = "garlic parmesan cheese spread pack" + filling_color = "#ffff00" list_reagents = list(/datum/reagent/consumable/cheese_spread = 8) /obj/item/reagent_containers/food/snacks/ration/condiment/bacon_cheddar_cheese_spread name = "bacon cheddar cheese spread pack" + filling_color = "#ff9900" list_reagents = list(/datum/reagent/consumable/cheese_spread = 8) /obj/item/reagent_containers/food/snacks/ration/condiment/peanut_butter name = "peanut butter pack" + filling_color = "#664400" list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/peanut_butter = 5) /obj/item/reagent_containers/food/snacks/ration/condiment/chunky_peanut_butter name = "chunky peanut butter pack" + filling_color = "#663300" list_reagents = list(/datum/reagent/consumable/peanut_butter = 10) /obj/item/reagent_containers/food/snacks/ration/condiment/maple_syrup name = "maple syrup pack" + filling_color = "#661100" list_reagents = list(/datum/reagent/consumable/sugar = 10) /obj/item/reagent_containers/food/snacks/ration/pack/chocolate_protein_beverage name = "chocolate hazelnut protein drink powder pack" + filling_color = "#664400" list_reagents = list(/datum/reagent/consumable/coco = 5, /datum/reagent/consumable/eggyolk = 5) /obj/item/reagent_containers/food/snacks/ration/pack/fruit_beverage name = "fruit punch beverage powder, carb-electrolyte pack" + filling_color = "#ff4400" list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/applejuice = 2, /datum/reagent/consumable/orangejuice = 2) /obj/item/reagent_containers/food/snacks/ration/pack/fruit_smoothie_beverage name = "tropical blend fruit and vegetable smoothie powder pack" + filling_color = "#ffaa00" list_reagents = list(/datum/reagent/consumable/pineapplejuice = 3, /datum/reagent/consumable/orangejuice = 3, /datum/reagent/consumable/eggyolk = 3) /obj/item/reagent_containers/food/snacks/ration/pack/grape_beverage name = "grape beverage powder, carb-fortified pack" + filling_color = "#9900ff" list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/grapejuice = 5) /obj/item/reagent_containers/food/snacks/ration/pack/grape_beverage_sugar_free name = "sugar-free grape beverage base powder" + filling_color = "#9900ff" list_reagents = list(/datum/reagent/consumable/grapejuice = 10) /obj/item/reagent_containers/food/snacks/ration/pack/lemonade_beverage name = "lemonade drink powder pack" + filling_color = "#ffff80" list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/lemonjuice = 5) /obj/item/reagent_containers/food/snacks/ration/pack/lemonade_beverage_suger_free name = "lemonade sugar-free beverage base pack" + filling_color = "#ffff00" list_reagents = list(/datum/reagent/consumable/lemonjuice = 10) /obj/item/reagent_containers/food/snacks/ration/pack/orange_beverage name = "orange beverage powder, carb-fortified pack" + filling_color = "#ffbb00" list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/orangejuice = 5) /obj/item/reagent_containers/food/snacks/ration/pack/orange_beverage_sugar_free name = "orange beverage base, sugar-free pack" + filling_color = "#ff9900" list_reagents = list(/datum/reagent/consumable/orangejuice = 10) /obj/item/reagent_containers/food/snacks/ration/pack/cherry_beverage name = "cherry high-energy beverage powder pack" + filling_color = "#ff5555" list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/cherryjelly = 5) /obj/item/reagent_containers/food/snacks/ration/pack/pineapple_beverage name = "pinapple fruit beverage base pack" + filling_color = "#fff111" list_reagents = list(/datum/reagent/consumable/pineapplejuice = 10) /obj/item/reagent_containers/food/snacks/ration/pack/freeze_dried_coffee_orange name = "freeze-dried coffee flavored with orange pack" + filling_color = "#cc7400" list_reagents = list(/datum/reagent/consumable/coffee = 5, /datum/reagent/consumable/orangejuice = 3) /obj/item/reagent_containers/food/snacks/ration/pack/freeze_dried_coffee_chocolate name = "freeze-dried coffee flavored with chocolate pack" + filling_color = "#803300" list_reagents = list(/datum/reagent/consumable/coffee = 5, /datum/reagent/consumable/coco = 3) /obj/item/reagent_containers/food/snacks/ration/pack/freeze_dried_coffee_hazelnut name = "freeze-dried coffee flavored with hazelnut pack" + filling_color = "#553300" list_reagents = list(/datum/reagent/consumable/coffee = 5, /datum/reagent/consumable/coco = 3) diff --git a/code/modules/food_and_drinks/kitchen_machinery/grill.dm b/code/modules/food_and_drinks/kitchen_machinery/grill.dm index c349c7511752..c150fe94498d 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/grill.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/grill.dm @@ -9,7 +9,7 @@ layer = BELOW_OBJ_LAYER use_power = NO_POWER_USE var/grill_fuel = 0 - var/obj/item/reagent_containers/food/grilled_item + var/obj/item/reagent_containers/food/snacks/grilled_item var/grill_time = 0 var/datum/looping_sound/grill/grill_loop @@ -27,68 +27,74 @@ else icon_state = "grill_open" return ..() + +/obj/machinery/grill/examine(mob/user) + . = ..() + if(grill_fuel) + . += span_notice("\The [src] has [grill_fuel] units of fuel left.") + else + . += span_warning("\The [src] is out of fuel! Add some wood or coal!") + /obj/machinery/grill/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/stack/sheet/mineral/coal) || istype(I, /obj/item/stack/sheet/mineral/wood)) var/obj/item/stack/S = I var/stackamount = S.get_amount() - to_chat(user, "You put [stackamount] [I]s in [src].") + to_chat(user, span_notice("You put [stackamount] [I]s in [src].")) if(istype(I, /obj/item/stack/sheet/mineral/coal)) - grill_fuel += (500 * stackamount) - else grill_fuel += (50 * stackamount) + else + grill_fuel += (5 * stackamount) S.use(stackamount) update_appearance() return if(I.resistance_flags & INDESTRUCTIBLE) - to_chat(user, "You don't feel it would be wise to grill [I]...") + to_chat(user, span_warning("You don't feel it would be wise to grill [I]...")) return ..() - if(istype(I, /obj/item/reagent_containers)) - if(istype(I, /obj/item/reagent_containers/food) && !istype(I, /obj/item/reagent_containers/food/drinks)) - var/obj/item/reagent_containers/food/food_item = I - if(HAS_TRAIT(food_item, TRAIT_NODROP) || (food_item.item_flags & (ABSTRACT | DROPDEL))) - return ..() - else if(food_item.foodtype & GRILLED) - to_chat(user, "[food_item] has already been grilled!") - return - else if(!grill_fuel) - to_chat(user, "There is not enough fuel!") - return - else if(!grilled_item && user.transferItemToLoc(food_item, src)) - grilled_item = food_item - grilled_item.foodtype |= GRILLED - to_chat(user, "You put the [grilled_item] on [src].") - update_appearance() - grill_loop.start() - return - else - if(I.reagents.has_reagent(/datum/reagent/consumable/xeno_energy)) - grill_fuel += (20 * (I.reagents.get_reagent_amount(/datum/reagent/consumable/xeno_energy))) - to_chat(user, "You pour the Monkey Energy in [src].") - I.reagents.remove_reagent(/datum/reagent/consumable/xeno_energy, I.reagents.get_reagent_amount(/datum/reagent/consumable/xeno_energy)) - update_appearance() - return + + if(istype(I, /obj/item/reagent_containers/food/snacks)) + var/obj/item/reagent_containers/food/snacks/food_item = I + if(HAS_TRAIT(food_item, TRAIT_NODROP) || (food_item.item_flags & (ABSTRACT | DROPDEL))) + return ..() + else if(food_item.foodtype & GRILLED) + to_chat(user, span_notice("[food_item] has already been grilled!")) + return + else if(grill_fuel <= 0) + to_chat(user, span_warning("There is not enough fuel!")) + return + else if(grilled_item) + to_chat(user,span_warning("\The [src] is already grilling something, take it out first!")) + return + else if(user.transferItemToLoc(food_item, src)) + START_PROCESSING(SSmachines, src) + grilled_item = food_item + to_chat(user, span_notice("You put the [grilled_item] on [src].")) + update_appearance() + grill_loop.start() + return ..() /obj/machinery/grill/process() ..() + if(!grilled_item) + return PROCESS_KILL update_appearance() - if(!grill_fuel) + if(grill_fuel <= 0) + grill_fuel = 0 + visible_message(span_warning("\The [src] is out of fuel!")) + if(grilled_item) + grilled_item.forceMove(loc) + finish_grill() return - else - grill_fuel -= 1 - if(prob(1)) - var/datum/effect_system/smoke_spread/bad/smoke = new - smoke.set_up(1, loc) - smoke.start() - if(grilled_item) - grill_time += 1 - grill_fuel -= 10 - grilled_item.AddComponent(/datum/component/sizzle) + grill_time += 1 + grill_fuel -= 1 + if(prob(1)) + var/datum/effect_system/smoke_spread/bad/smoke = new + smoke.set_up(1, loc) + smoke.start() /obj/machinery/grill/Exited(atom/movable/AM) if(AM == grilled_item) finish_grill() - grilled_item = null . = ..() /obj/machinery/grill/Destroy() @@ -118,13 +124,15 @@ /obj/machinery/grill/attack_hand(mob/user) if(grilled_item) - to_chat(user, "You take out [grilled_item] from [src].") + to_chat(user, span_notice("You take out [grilled_item] from [src].")) grilled_item.forceMove(drop_location()) update_appearance() return return ..() /obj/machinery/grill/proc/finish_grill() + if(grill_time >= 10 && grilled_item.cooked_type) + grilled_item = grilled_item.microwave_act() switch(grill_time) //no 0-9 to prevent spam if(10 to 15) grilled_item.name = "lightly-grilled [grilled_item.name]" @@ -141,8 +149,12 @@ grilled_item.name = "Powerfully Grilled [grilled_item.name]" grilled_item.desc = "A [grilled_item.name]. Reminds you of your wife, wait, no, it's prettier!" grilled_item.foodtype |= FRIED + grilled_item.AddComponent(/datum/component/sizzle, (grill_time * 7.5)) + grilled_item.foodtype |= GRILLED grill_time = 0 grill_loop.stop() + grilled_item = null + update_appearance() /obj/machinery/grill/unwrenched anchored = FALSE diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm index 2762892110f8..0df04a08658b 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm @@ -362,7 +362,7 @@ name = "flameless ration heater" desc = "A magnisium based ration heater. It can be used to heat up entrees and other food items. reaches the same temperature as a microwave with half the volume." icon = 'icons/obj/food/ration.dmi' - icon_state = "ration_package" + icon_state = "ration_heater" grind_results = list(/datum/reagent/iron = 10, /datum/reagent/water = 10, /datum/reagent/consumable/sodiumchloride = 5) heat = 3800 var/obj/item/tocook = null diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm index 772893e3ff16..b5c4c2c42b85 100644 --- a/code/modules/food_and_drinks/pizzabox.dm +++ b/code/modules/food_and_drinks/pizzabox.dm @@ -96,7 +96,7 @@ . += tag_overlay /obj/item/pizzabox/worn_overlays(isinhands, icon_file) - . = list() + . = ..() var/current_offset = 2 if(isinhands) for(var/V in boxes) //add EXTRA BOX per box diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm index 0a0b00094e79..c13983c6792d 100644 --- a/code/modules/food_and_drinks/recipes/processor_recipes.dm +++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm @@ -31,7 +31,7 @@ /datum/food_processor_process/spaghetti input = /obj/item/reagent_containers/food/snacks/doughslice - output = /obj/item/food/spaghetti + output = /obj/item/food/spaghetti/raw /datum/food_processor_process/corn input = /obj/item/reagent_containers/food/snacks/grown/corn diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index e48f4d5af5aa..a2b48d9319b9 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -1103,7 +1103,7 @@ C.update_inv_wear_suit() /obj/item/clothing/suit/armor/ascetic/worn_overlays(isinhands) - . = list() + . = ..() if(!isinhands) . += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER - 0.01) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 4a768fc1c501..c15c4a1af835 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -495,6 +495,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(isobserver(usr)) //Make sure they're an observer! + var/list/dest = list() //List of possible destinations (mobs) var/target = null //Chosen target. diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index da3bc7c2dfb5..86d54577538c 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -57,7 +57,7 @@ var/list/misc = list() var/list/npcs = list() - var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE, only_realname = TRUE) + var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE) for (var/name in pois) var/list/serialized = list() serialized["name"] = name @@ -67,8 +67,6 @@ serialized["ref"] = REF(poi) var/mob/M = poi - - serialized["fake_name"] = M.name if (istype(M)) if (isobserver(M)) ghosts += list(serialized) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 1910347e4fdd..8e2962e70d2f 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -51,19 +51,31 @@ if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS) if(prob(10)) to_chat(src, "You feel terribly bloated.") + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - if(prob(5)) + + if(prob(1)) to_chat(src, "You feel [word].") - adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1)) + if(oxyloss < 20) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) - if(prob(5)) - blur_eyes(6) + if(eye_blurry < 50) + adjust_blurriness(5) + if(oxyloss < 40) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + else + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1)) + + if(prob(15)) + Unconscious(rand(2 SECONDS,6 SECONDS)) to_chat(src, "You feel very [word].") + if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) - adjustOxyLoss(5) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + adjustToxLoss(2) if(prob(15)) - Unconscious(rand(20,60)) + Unconscious(rand(2 SECONDS,6 SECONDS)) to_chat(src, "You feel extremely [word].") if(-INFINITY to BLOOD_VOLUME_SURVIVE) if(!HAS_TRAIT(src, TRAIT_NODEATH)) @@ -81,25 +93,76 @@ BP.adjust_bleeding(0.1, BLOOD_LOSS_DAMAGE_MAXIMUM) limb_bleed += BP.bleeding + var/message_cooldown = 5 SECONDS + var/bleeeding_wording +// var/bleed_change_wording + switch(limb_bleed) + if(0 to 0.5) + bleeeding_wording = "You hear droplets of blood drip down." + message_cooldown *= 2.5 + if(0.5 to 1) + bleeeding_wording = "You feel your blood flow quietly to the floor." + message_cooldown *= 2 + if(1 to 2) + bleeeding_wording = "The flow of blood leaving your body onto the ground is worrying..." + message_cooldown *= 1.7 + if(2 to 4) + bleeeding_wording = "You're losing blood very fast, which is freaking you out!" + message_cooldown *= 1.5 + if(4 to INFINITY) + bleeeding_wording = "Your heartbeat beats unstably fast as you lose a massive amount of blood!!" + if(limb_bleed && !bleedsuppress && !HAS_TRAIT(src, TRAIT_FAKEDEATH)) bleed(limb_bleed) + if(!blood_particle) + blood_particle = new(src, /particles/droplets/blood, PARTICLE_ATTACH_MOB) + blood_particle.particles.color = dna.blood_type.color //mouthful + blood_particle.particles.spawning = (limb_bleed/2) + blood_particle.particles.count = (round(clamp((limb_bleed * 2), 1, INFINITY))) + + if(COOLDOWN_FINISHED(src, bloodloss_message) && bleeeding_wording) + to_chat(src, span_warning("[bleeeding_wording]")) + COOLDOWN_START(src, bloodloss_message, message_cooldown) + else + if(blood_particle) + QDEL_NULL(blood_particle) + //Makes a blood drop, leaking amt units of blood from the mob /mob/living/carbon/proc/bleed(amt) if(blood_volume) blood_volume = max(blood_volume - amt, 0) if (prob(sqrt(amt)*BLOOD_DRIP_RATE_MOD)) if(isturf(src.loc) && !isgroundlessturf(src.loc)) //Blood loss still happens in locker, floor stays clean - if(amt >= 10) - add_splatter_floor(src.loc) + if(amt >= 2) + add_splatter_floor(src.loc, amt = amt) else - add_splatter_floor(src.loc, 1) + add_splatter_floor(src.loc, TRUE, amt) /mob/living/carbon/human/bleed(amt) amt *= physiology.bleed_mod if(!(NOBLOOD in dna.species.species_traits)) ..() +/** + * This proc is a helper for spraying blood for things like slashing/piercing wounds and dismemberment. + * + * The strength of the splatter in the second argument determines how much it can dirty and how far it can go + * + * Arguments: + * * splatter_direction: Which direction the blood is flying + * * splatter_strength: How many tiles it can go, and how many items it can pass over and dirty + */ +/mob/living/carbon/proc/spray_blood(splatter_direction, splatter_strength = 3) + if(!isturf(loc)) + return + var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc) + +// our_splatter.transfer_mob_blood_dna(return_blood_DNA(src)) + our_splatter.blood_dna_info = get_blood_dna_list() + our_splatter.transfer_mob_blood_dna(src) + var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength) + INVOKE_ASYNC(our_splatter, TYPE_PROC_REF(/obj/effect/decal/cleanable/blood/hitsplatter, fly_towards), targ, splatter_strength) /mob/living/proc/restore_blood() @@ -229,13 +292,14 @@ return blood_type.color //to add a splatter of blood or other mob liquid. -/mob/living/proc/add_splatter_floor(turf/T, small_drip) +/mob/living/proc/add_splatter_floor(turf/T, small_drip, amt) if(get_blood_id() != /datum/reagent/blood) return if(!T) T = get_turf(src) var/list/temp_blood_DNA + if(small_drip) // Only a certain number of drips (or one large splatter) can be on a given turf. var/obj/effect/decal/cleanable/blood/drip/drop = locate() in T @@ -248,7 +312,7 @@ else temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter qdel(drop)//the drip is replaced by a bigger splatter - else + else if (amt < 2) drop = new(T, get_static_viruses()) drop.transfer_mob_blood_dna(src) return @@ -261,7 +325,11 @@ B = candidate break if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) + if(amt > 4) + B = new /obj/effect/decal/cleanable/blood(T, get_static_viruses()) + else + B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) + if(QDELETED(B)) //Give it up return B.bloodiness = min((B.bloodiness + BLOOD_AMOUNT_PER_DECAL), BLOOD_POOL_MAX) @@ -269,11 +337,11 @@ if(temp_blood_DNA) B.add_blood_DNA(temp_blood_DNA) -/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip) +/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip, amt) if(!(NOBLOOD in dna.species.species_traits)) ..() -/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip) +/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip, amt) if(!T) T = get_turf(src) var/obj/effect/decal/cleanable/xenoblood/B = locate() in T.contents @@ -281,7 +349,7 @@ B = new(T) B.add_blood_DNA(list("UNKNOWN DNA" = "X*")) -/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip) +/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip, amt) if(!T) T = get_turf(src) var/obj/effect/decal/cleanable/oil/B = locate() in T.contents diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 43cefa251e34..6a34e87fdf42 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -653,3 +653,20 @@ ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) else if(getOxyLoss() <= 50) REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + +/mob/living/carbon/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE) + var/mob/living/carbon/human/current_user = src //is this a good idea? who can say? + var/armor = run_armor_check(def_zone, P.flag, P.armour_penetration, silent = TRUE) + var/on_hit_state = P.on_hit(src, armor, piercing_hit) + if(!P.nodamage && on_hit_state != BULLET_ACT_BLOCK && !QDELETED(src)) //QDELETED literally just for the instagib rifle. Yeah. + apply_damage(P.damage, P.damage_type, def_zone, armor, sharpness = TRUE) + if(P.damage-armor >= 15 && P.damage_type == BRUTE && (!armor || prob(40) || P.damage-armor >= 25)) + spray_blood(get_dir(P.starting,src), (P.damage-armor)/5) + var/obj/item/bodypart/targeted_bodypart = null + bleed((P.damage-armor)/2) + + recoil_camera(src, clamp((P.damage-armor)/4,0.5,10), clamp((P.damage-armor)/4,0.5,10), P.damage/8, P.Angle) + apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, armor, P.stamina, P.jitter, P.paralyze, P.immobilize) + if(P.dismemberment) + check_projectile_dismemberment(P, def_zone) + return on_hit_state ? BULLET_ACT_HIT : BULLET_ACT_BLOCK diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm index 8c1a36c2061b..1804a1497187 100644 --- a/code/modules/mob/living/carbon/death.dm +++ b/code/modules/mob/living/carbon/death.dm @@ -31,6 +31,9 @@ if(prob(50)) step(W, pick(GLOB.alldirs)) var/atom/Tsec = drop_location() + var/amount_of_streams_to_spawn = rand(2,4) + for(var/i in 1 to amount_of_streams_to_spawn) + spray_blood(pick(GLOB.alldirs), rand(1,6)) for(var/mob/M in src) M.forceMove(Tsec) visible_message("[M] bursts out of [src]!") diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 567523c11d79..df006ead1f39 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -77,3 +77,7 @@ /// How many "units of blood" we have on our hands var/blood_in_hands = 0 + ///blood particle effect + var/obj/effect/abstract/particle_holder/blood_particle + + COOLDOWN_DECLARE(bloodloss_message) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 77e2045e357c..aab8b681bf50 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -26,25 +26,8 @@ return 0 return ..() -/mob/living/carbon/human/experience_pressure_difference(pressure_difference) - if(pressure_difference > 100) - playsound_local(null, 'sound/effects/space_wind_big.ogg', clamp(pressure_difference / 50, 10, 100), 1) - else - playsound_local(null, 'sound/effects/space_wind.ogg', clamp(pressure_difference, 10, 100), 1) - if(shoes && istype(shoes, /obj/item/clothing)) - var/obj/item/clothing/S = shoes - if((S.clothing_flags & NOSLIP)) - return 0 - return ..() - -/mob/living/carbon/human/mob_has_gravity() - . = ..() - if(!.) - if(mob_negates_gravity()) - . = 1 - /mob/living/carbon/human/mob_negates_gravity() - return ((shoes && shoes.negates_gravity()) || (dna.species.negates_gravity(src))) + return dna.species.negates_gravity(src) || ..() /mob/living/carbon/human/Move(NewLoc, direct) . = ..() diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 756af00f1839..25e045064a44 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -334,7 +334,8 @@ if(equip_to_slot_if_possible(thing, slot_type)) update_inv_hands() return - if(!SEND_SIGNAL(equipped_item, COMSIG_CONTAINS_STORAGE)) // not a storage item + var/datum/component/storage/storage = equipped_item.GetComponent(/datum/component/storage) + if(!storage) if(!thing) equipped_item.attack_hand(src) else @@ -344,10 +345,11 @@ if(!SEND_SIGNAL(equipped_item, COMSIG_TRY_STORAGE_INSERT, thing, src)) to_chat(src, "You can't fit [thing] into your [equipped_item.name]!") return - if(!equipped_item.contents.len) // nothing to take out + var/atom/real_location = storage.real_location() + if(!real_location.contents.len) // nothing to take out to_chat(src, "There's nothing in your [equipped_item.name] to take out!") return - var/obj/item/stored = equipped_item.contents[equipped_item.contents.len] + var/obj/item/stored = real_location.contents[real_location.contents.len] if(!stored || stored.on_found(src)) return stored.attack_hand(src) // take out thing from item in storage slot diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 0f3ab70e4034..540dddb9a489 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -741,13 +741,13 @@ There are several things that need to be remembered: update_hud_neck(I) if(!(ITEM_SLOT_NECK in check_obscured_slots())) - if(dna.species.bodytype & BODYTYPE_VOX) // there is neither a vox or kepori neck path, we just tell it to greyscale no matter what -// if(I.supports_variations & VOX_VARIATION) -// icon_file = VOX_NECK_PATH -// if(I.vox_override_icon) -// icon_file = I.vox_override_icon -// else - handled_by_bodytype = TRUE + if(dna.species.bodytype & BODYTYPE_VOX) // there is no kepori neck path, we just tell it to greyscale no matter what + if(I.supports_variations & VOX_VARIATION) + icon_file = VOX_NECK_PATH + if(I.vox_override_icon) + icon_file = I.vox_override_icon + else + handled_by_bodytype = TRUE else if(dna.species.bodytype & BODYTYPE_KEPORI) // if(I.supports_variations & KEPORI_VARIATION) @@ -804,7 +804,7 @@ There are several things that need to be remembered: handled_by_bodytype = TRUE if(!icon_exists(icon_file, RESOLVE_ICON_STATE(I))) - icon_file = DEFAULT_BACK_PATH + icon_file = I.mob_overlay_icon ? I.mob_overlay_icon : DEFAULT_BACK_PATH handled_by_bodytype = TRUE var/use_autogen = handled_by_bodytype ? dna.species : null diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index d5b97a942da2..9525ebd6ec9b 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -14,6 +14,30 @@ return legcuffed return null +/mob/living/carbon/get_slot_by_item(obj/item/looking_for) + if(looking_for == back) + return ITEM_SLOT_BACK + + if(back && (looking_for in back)) + return ITEM_SLOT_BACKPACK + + if(looking_for == wear_mask) + return ITEM_SLOT_MASK + + if(looking_for == wear_neck) + return ITEM_SLOT_NECK + + if(looking_for == head) + return ITEM_SLOT_HEAD + + if(looking_for == handcuffed) + return ITEM_SLOT_HANDCUFFED + + if(looking_for == legcuffed) + return ITEM_SLOT_LEGCUFFED + + return ..() + /mob/living/carbon/proc/equip_in_one_of_slots(obj/item/I, list/slots, qdel_on_fail = 1) for(var/slot in slots) if(equip_to_slot_if_possible(I, slots[slot], qdel_on_fail = 0, disable_warning = TRUE)) diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index c80c9a821fd0..34bd7dd8632c 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -211,6 +211,9 @@ //eg: ammo counters, primed grenade flashing, etc. //"icon_file" is used automatically for inhands etc. to make sure it gets the right inhand file /obj/item/proc/worn_overlays(isinhands = FALSE, icon_file) + SHOULD_CALL_PARENT(TRUE) + RETURN_TYPE(/list) + . = list() diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 6615edb051f0..0687d90da75b 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -1,4 +1,4 @@ -/mob/living/gib(no_brain, no_organs, no_bodyparts) +/mob/living/gib(no_brain, no_organs, no_bodyparts, safe_gib = FALSE) var/prev_lying = lying_angle if(stat != DEAD) death(TRUE) @@ -12,7 +12,8 @@ spread_bodyparts(no_brain, no_organs) spawn_gibs(no_bodyparts) - qdel(src) + if(!safe_gib) + qdel(src) /mob/living/proc/gib_animation() return diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index a25025294497..c054b7d50bf4 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -122,7 +122,7 @@ return /mob/living/proc/handle_gravity() - var/gravity = mob_has_gravity() + var/gravity = has_gravity() update_gravity(gravity) if(gravity > STANDARD_GRAVITY) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 269c74a837bd..1258df8b84c5 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -97,6 +97,7 @@ if(m_intent == MOVE_INTENT_WALK) return TRUE + SEND_SIGNAL(src, COMSIG_LIVING_MOB_BUMP, M) //Even if we don't push/swap places, we "touched" them, so spread fire spreadFire(M) @@ -831,7 +832,7 @@ return pick("trails_1", "trails_2") /mob/living/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0) - if(buckled) + if(buckled || mob_negates_gravity()) return if(client && client.move_delay >= world.time + world.tick_lag*2) pressure_resistance_prob_delta -= 30 diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm index 80c643e0ceef..9813ac88d43e 100644 --- a/code/modules/mob/living/silicon/damage_procs.dm +++ b/code/modules/mob/living/silicon/damage_procs.dm @@ -1,5 +1,5 @@ -/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, break_modifier = 1, sharpness = FALSE) +/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, break_modifier = 1, sharpness = FALSE) var/hit_percent = (100-blocked)/100 if((!damage || (!forced && hit_percent <= 0))) return 0 diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 5229967f0374..01610cd13644 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -235,6 +235,9 @@ if(!target && trash) //Then for trash. target = scan(/obj/item/trash) + if(!target && trash) //Then for Chainsmokers. + target = scan(/obj/item/cigbutt) + if(!target && trash) //Search for dead mices. target = scan(/obj/item/reagent_containers/food/snacks/deadmouse) @@ -317,6 +320,7 @@ target_types += list( /obj/item/trash, /obj/item/reagent_containers/food/snacks/deadmouse, + /obj/item/cigbutt, ) target_types = typecacheof(target_types) @@ -340,7 +344,7 @@ else if(istype(A, /obj/item) || istype(A, /obj/effect/decal/remains)) visible_message("[src] sprays hydrofluoric acid at [A]!") playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6) - A.acid_act(75, 10) + A.acid_act(100, 10) target = null else if(istype(A, /mob/living/simple_animal/hostile/cockroach) || istype(A, /mob/living/simple_animal/mouse)) var/mob/living/simple_animal/M = target diff --git a/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm b/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm index ddae7a07397b..968e1f7b55ff 100644 --- a/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm +++ b/code/modules/mob/living/simple_animal/hostile/human/frontiersman.dm @@ -39,7 +39,7 @@ icon_state = "frontiersmanranged_mask" icon_living = "frontiersmanranged_mask" loot = list(/obj/effect/mob_spawn/human/corpse/frontier/ranged, - /obj/item/gun/ballistic/revolver, + /obj/item/gun/ballistic/revolver/syndicate, /obj/item/clothing/mask/gas/sechailer, /obj/item/tank/internals/emergency_oxygen/engi) 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) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 06c7a9af52d8..b1788a7aa50d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -313,6 +313,14 @@ /mob/proc/get_item_by_slot(slot_id) return null +/// Gets what slot the item on the mob is held in. +/// Returns null if the item isn't in any slots on our mob. +/// Does not check if the passed item is null, which may result in unexpected outcoms. +/mob/proc/get_slot_by_item(obj/item/looking_for) + if(looking_for in held_items) + return ITEM_SLOT_HANDS + + return null ///Is the mob incapacitated /mob/proc/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, check_immobilized = FALSE) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 42d217cf96e4..bd1227d94d7d 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -28,6 +28,45 @@ zone = BODY_ZONE_CHEST return zone +/// Returns a generic path of the object based on the slot +/proc/get_path_by_slot(slot_id) + switch(slot_id) + if(ITEM_SLOT_BACK) + return /obj/item/storage/backpack + if(ITEM_SLOT_MASK) + return /obj/item/clothing/mask + if(ITEM_SLOT_NECK) + return /obj/item/clothing/neck + if(ITEM_SLOT_HANDCUFFED) + return /obj/item/restraints/handcuffs + if(ITEM_SLOT_LEGCUFFED) + return /obj/item/restraints/legcuffs + if(ITEM_SLOT_BELT) + return /obj/item/storage/belt + if(ITEM_SLOT_ID) + return /obj/item/card/id + if(ITEM_SLOT_EARS) + return /obj/item/clothing/ears + if(ITEM_SLOT_EYES) + return /obj/item/clothing/glasses + if(ITEM_SLOT_GLOVES) + return /obj/item/clothing/gloves + if(ITEM_SLOT_HEAD) + return /obj/item/clothing/head + if(ITEM_SLOT_FEET) + return /obj/item/clothing/shoes + if(ITEM_SLOT_OCLOTHING) + return /obj/item/clothing/suit + if(ITEM_SLOT_ICLOTHING) + return /obj/item/clothing/under + if(ITEM_SLOT_LPOCKET) + return /obj/item + if(ITEM_SLOT_RPOCKET) + return /obj/item + if(ITEM_SLOT_SUITSTORE) + return /obj/item + return null + /** * Return the zone or randomly, another valid zone * diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm new file mode 100644 index 000000000000..1df1f7b8894b --- /dev/null +++ b/code/modules/mod/mod_actions.dm @@ -0,0 +1,193 @@ +/datum/action/item_action/mod + background_icon_state = "bg_tech_blue" + icon_icon = 'icons/mob/actions/actions_mod.dmi' + check_flags = AB_CHECK_CONSCIOUS + /// Whether this action is intended for the AI. Stuff breaks a lot if this is done differently. + var/ai_action = FALSE + +/datum/action/item_action/mod/New(Target) + ..() + if(!istype(Target, /obj/item/mod/control)) + qdel(src) + return + if(ai_action) + background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND + +/datum/action/item_action/mod/Grant(mob/user) + var/obj/item/mod/control/mod = target + if(ai_action && user != mod.ai) + return + else if(!ai_action && user == mod.ai) + return + return ..() + +/datum/action/item_action/mod/Remove(mob/user) + var/obj/item/mod/control/mod = target + if(ai_action && user != mod.ai) + return + else if(!ai_action && user == mod.ai) + return + return ..() + +/datum/action/item_action/mod/Trigger(trigger_flags) + if(!IsAvailable()) + return FALSE + var/obj/item/mod/control/mod = target + if(mod.malfunctioning && prob(75)) + mod.balloon_alert(usr, "button malfunctions!") + return FALSE + return TRUE + +/datum/action/item_action/mod/deploy + name = "Deploy MODsuit" + desc = "LMB: Deploy/Undeploy part. Alt Click: Deploy/Undeploy full suit." + button_icon_state = "deploy" + +/datum/action/item_action/mod/deploy/Trigger(trigger_flags) + . = ..() + if(!.) + return + var/obj/item/mod/control/mod = target + if(trigger_flags & TRIGGER_SECONDARY_ACTION) + mod.quick_deploy(usr) + else + mod.choose_deploy(usr) + +/datum/action/item_action/mod/deploy/ai + ai_action = TRUE + +/datum/action/item_action/mod/activate + name = "Activate MODsuit" + desc = "LMB: Activate/Deactivate suit with prompt. Alt Click: Activate/Deactivate suit skipping prompt." + button_icon_state = "activate" + /// First time clicking this will set it to TRUE, second time will activate it. + var/ready = FALSE + +/datum/action/item_action/mod/activate/Trigger(trigger_flags) + . = ..() + if(!.) + return + if(!(trigger_flags & TRIGGER_SECONDARY_ACTION) && !ready) + ready = TRUE + button_icon_state = "activate-ready" + if(!ai_action) + background_icon_state = "bg_tech" + UpdateButtonIcon() + addtimer(CALLBACK(src, PROC_REF(reset_ready)), 3 SECONDS) + return + var/obj/item/mod/control/mod = target + reset_ready() + mod.toggle_activate(usr) + +/// Resets the state requiring to be doubleclicked again. +/datum/action/item_action/mod/activate/proc/reset_ready() + ready = FALSE + button_icon_state = initial(button_icon_state) + if(!ai_action) + background_icon_state = initial(background_icon_state) + UpdateButtonIcon() + +/datum/action/item_action/mod/activate/ai + ai_action = TRUE + +/datum/action/item_action/mod/module + name = "Toggle Module" + desc = "Toggle a MODsuit module." + button_icon_state = "module" + +/datum/action/item_action/mod/module/Trigger(trigger_flags) + . = ..() + if(!.) + return + var/obj/item/mod/control/mod = target + mod.quick_module(usr) + +/datum/action/item_action/mod/module/ai + ai_action = TRUE + +/datum/action/item_action/mod/panel + name = "MODsuit Panel" + desc = "Open the MODsuit's panel." + button_icon_state = "panel" + +/datum/action/item_action/mod/panel/Trigger(trigger_flags) + . = ..() + if(!.) + return + var/obj/item/mod/control/mod = target + mod.ui_interact(usr) + +/datum/action/item_action/mod/panel/ai + ai_action = TRUE + +/datum/action/item_action/mod/pinned_module + desc = "Activate the module." + /// Overrides the icon applications. + var/override = FALSE + /// Module we are linked to. + var/obj/item/mod/module/module + /// A ref to the mob we are pinned to. + var/pinner_ref + +/datum/action/item_action/mod/pinned_module/New(Target, obj/item/mod/module/linked_module, mob/user) + if(isAI(user)) + ai_action = TRUE + ..() + module = linked_module + name = "Activate [capitalize(linked_module.name)]" + desc = "Quickly activate [linked_module]." + icon_icon = linked_module.icon + button_icon_state = linked_module.icon_state + RegisterSignal(linked_module, COMSIG_MODULE_ACTIVATED, PROC_REF(on_module_activate)) + RegisterSignal(linked_module, COMSIG_MODULE_DEACTIVATED, PROC_REF(on_module_deactivate)) + RegisterSignal(linked_module, COMSIG_MODULE_USED, PROC_REF(on_module_use)) + +/datum/action/item_action/mod/pinned_module/Destroy() + module.pinned_to -= pinner_ref + module = null + return ..() + +/datum/action/item_action/mod/pinned_module/Grant(mob/user) + var/user_ref = REF(user) + if(!pinner_ref) + pinner_ref = user_ref + module.pinned_to[pinner_ref] = src + else if(pinner_ref != user_ref) + return + return ..() + +/datum/action/item_action/mod/pinned_module/Trigger(trigger_flags) + . = ..() + if(!.) + return + module.on_select() + +/datum/action/item_action/mod/pinned_module/ApplyIcon(atom/movable/screen/movable/action_button/current_button, force) + . = ..(current_button, force = TRUE) + if(override) + return + var/obj/item/mod/control/mod = target + if(module == mod.selected_module) + current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected", layer = FLOAT_LAYER-0.1)) + else if(module.active) + current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_active", layer = FLOAT_LAYER-0.1)) + if(!COOLDOWN_FINISHED(module, cooldown_timer)) + var/image/cooldown_image = image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown") + current_button.add_overlay(cooldown_image) + addtimer(CALLBACK(current_button, TYPE_PROC_REF(/image, cut_overlay), cooldown_image), COOLDOWN_TIMELEFT(module, cooldown_timer)) + + +/datum/action/item_action/mod/pinned_module/proc/on_module_activate(datum/source) + SIGNAL_HANDLER + + UpdateButtonIcon() + +/datum/action/item_action/mod/pinned_module/proc/on_module_deactivate(datum/source) + SIGNAL_HANDLER + + UpdateButtonIcon() + +/datum/action/item_action/mod/pinned_module/proc/on_module_use(datum/source) + SIGNAL_HANDLER + + UpdateButtonIcon() diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm new file mode 100644 index 000000000000..cb61728f2cbb --- /dev/null +++ b/code/modules/mod/mod_activation.dm @@ -0,0 +1,244 @@ +#define MOD_ACTIVATION_STEP_FLAGS IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM|IGNORE_INCAPACITATED + +/// Creates a radial menu from which the user chooses parts of the suit to deploy/retract. Repeats until all parts are extended or retracted. +/obj/item/mod/control/proc/choose_deploy(mob/user) + if(!length(mod_parts)) + return + var/list/display_names = list() + var/list/items = list() + for(var/obj/item/part as anything in mod_parts) + display_names[part.name] = REF(part) + var/image/part_image = image(icon = part.icon, icon_state = part.icon_state) + if(part.loc != src) + part_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active") + items += list(part.name = part_image) + var/pick = show_radial_menu(user, src, items, custom_check = FALSE, require_near = TRUE, tooltips = TRUE) + if(!pick) + return + var/part_reference = display_names[pick] + var/obj/item/part = locate(part_reference) in mod_parts + if(!istype(part) || user.incapacitated()) + return + if((active && part != helmet) || activating) + balloon_alert(user, "deactivate the suit first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + var/parts_to_check = mod_parts - part + if(part.loc == src) + deploy(user, part) + for(var/obj/item/checking_part as anything in parts_to_check) + if(checking_part.loc != src) + continue + choose_deploy(user) + break + else + retract(user, part) + for(var/obj/item/checking_part as anything in parts_to_check) + if(checking_part.loc == src) + continue + choose_deploy(user) + break + +/// Quickly deploys all parts (or retracts if all are on the wearer) +/obj/item/mod/control/proc/quick_deploy(mob/user) + if(active || activating) + balloon_alert(user, "deactivate the suit first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + var/deploy = FALSE + for(var/obj/item/part as anything in mod_parts) + if(part.loc != src) + continue + deploy = TRUE + for(var/obj/item/part as anything in mod_parts) + if(deploy && part.loc == src) + deploy(null, part) + else if(!deploy && part.loc != src) + retract(null, part) + wearer.visible_message(span_notice("[wearer]'s [src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss."), + span_notice("[src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss."), + span_hear("You hear a mechanical hiss.")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + return TRUE + +/// Deploys a part of the suit onto the user. +/obj/item/mod/control/proc/deploy(mob/user, obj/item/part) + if(part.loc != src) + if(!user) + return FALSE + balloon_alert(user, "[part.name] already deployed!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + if(part in overslotting_parts) + var/obj/item/overslot = wearer.get_item_by_slot(part.slot_flags) + if(overslot) + overslotting_parts[part] = overslot + wearer.transferItemToLoc(overslot, part, force = TRUE) + RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit)) + if(wearer.equip_to_slot_if_possible(part, part.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) + ADD_TRAIT(part, TRAIT_NODROP, MOD_TRAIT) + if(!user) + return TRUE + wearer.visible_message(span_notice("[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss."), + span_notice("[part] deploy[part.p_s()] with a mechanical hiss."), + span_hear("You hear a mechanical hiss.")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + return TRUE + else + if(!user) + return FALSE + balloon_alert(user, "bodypart clothed!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + +/// Retract a part of the suit from the user. +/obj/item/mod/control/proc/retract(mob/user, obj/item/part) + if(part.loc == src) + if(!user) + return FALSE + balloon_alert(user, "[part.name] already retracted!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + REMOVE_TRAIT(part, TRAIT_NODROP, MOD_TRAIT) + wearer.transferItemToLoc(part, src, force = TRUE) + if(overslotting_parts[part]) + UnregisterSignal(part, COMSIG_ATOM_EXITED) + var/obj/item/overslot = overslotting_parts[part] + if(!wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) + wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE) + overslotting_parts[part] = null + if(!user) + return + wearer.visible_message(span_notice("[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss."), + span_notice("[part] retract[part.p_s()] back into [src] with a mechanical hiss."), + span_hear("You hear a mechanical hiss.")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + +/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on +/obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE) + if(!wearer) + if(!force_deactivate) + balloon_alert(user, "put suit on back!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE)) + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + for(var/obj/item/part as anything in mod_parts) + if(!force_deactivate && part.loc == src) + balloon_alert(user, "deploy all parts first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(locked && !active && !allowed(user) && !force_deactivate) + balloon_alert(user, "access insufficient!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(!get_charge() && !force_deactivate) + balloon_alert(user, "suit not powered!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(open && !force_deactivate) + balloon_alert(user, "close the suit panel!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(activating) + if(!force_deactivate) + balloon_alert(user, "suit already [active ? "shutting down" : "starting up"]!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + for(var/obj/item/mod/module/module as anything in modules) + if(!module.active || module.allowed_inactive) + continue + module.on_deactivation(display_message = FALSE) + activating = TRUE + to_chat(wearer, span_notice("MODsuit [active ? "shutting down" : "starting up"].")) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) + to_chat(wearer, span_notice("[boots] [active ? "relax their grip on your legs" : "seal around your feet"].")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + seal_part(boots, seal = !active) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) + to_chat(wearer, span_notice("[gauntlets] [active ? "become loose around your fingers" : "tighten around your fingers and wrists"].")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + seal_part(gauntlets, seal = !active) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) + to_chat(wearer, span_notice("[chestplate] [active ? "releases your chest" : "cinches tightly against your chest"].")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + seal_part(chestplate, seal = !active) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) + to_chat(wearer, span_notice("[helmet] hisses [active ? "open" : "closed"].")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + seal_part(helmet, seal = !active) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) + to_chat(wearer, span_notice("Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer].")) + if(ai) + to_chat(ai, span_notice("SYSTEMS [active ? "DEACTIVATED. GOODBYE" : "ACTIVATED. WELCOME"]: \"[ai]\"")) + finish_activation(on = !active) + if(active) + playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000) + if(!malfunctioning) + wearer.playsound_local(get_turf(src), 'sound/mecha/nominal.ogg', 50) + else + playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000) + activating = FALSE + return TRUE + +///Seals or unseals the given part +/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal) + if(seal) + part.clothing_flags |= part.visor_flags + part.flags_inv |= part.visor_flags_inv + part.flags_cover |= part.visor_flags_cover + part.heat_protection = initial(part.heat_protection) + part.cold_protection = initial(part.cold_protection) + part.alternate_worn_layer = null + else + part.flags_cover &= ~part.visor_flags_cover + part.flags_inv &= ~part.visor_flags_inv + part.clothing_flags &= ~part.visor_flags + part.heat_protection = NONE + part.cold_protection = NONE + part.alternate_worn_layer = mod_parts[part] + if(part == boots) + boots.icon_state = "[skin]-boots[seal ? "-sealed" : ""]" + wearer.update_inv_shoes() + if(part == gauntlets) + gauntlets.icon_state = "[skin]-gauntlets[seal ? "-sealed" : ""]" + wearer.update_inv_gloves() + if(part == chestplate) + chestplate.icon_state = "[skin]-chestplate[seal ? "-sealed" : ""]" + wearer.update_inv_wear_suit() + wearer.update_inv_w_uniform() + if(part == helmet) + helmet.icon_state = "[skin]-helmet[seal ? "-sealed" : ""]" + wearer.update_inv_head() + wearer.update_inv_wear_mask() + wearer.update_inv_glasses() + wearer.update_hair() + +/// Finishes the suit's activation, starts processing +/obj/item/mod/control/proc/finish_activation(on) + active = on + if(active) + for(var/obj/item/mod/module/module as anything in modules) + module.on_suit_activation() + START_PROCESSING(SSobj, src) + else + for(var/obj/item/mod/module/module as anything in modules) + module.on_suit_deactivation() + STOP_PROCESSING(SSobj, src) + update_speed() + update_icon_state() + wearer.update_inv_back(slot_flags) + +/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits. +/obj/item/mod/control/proc/quick_activation() + var/seal = TRUE + for(var/obj/item/part as anything in mod_parts) + if(!deploy(null, part)) + seal = FALSE + if(!seal) + return + for(var/obj/item/part as anything in mod_parts) + seal_part(part, seal = TRUE) + finish_activation(on = TRUE) + +/obj/item/mod/control/proc/has_wearer() + return wearer diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm new file mode 100644 index 000000000000..a05717970348 --- /dev/null +++ b/code/modules/mod/mod_ai.dm @@ -0,0 +1,125 @@ +/** + * Simple proc to insert the pAI into the MODsuit. + * + * user - The person trying to put the pAI into the MODsuit. + * card - The pAI card we're slotting in the MODsuit. + */ + +/obj/item/mod/control/proc/insert_pai(mob/user, obj/item/paicard/card) + if(ai) + balloon_alert(user, "ai already installed!") + return + if(!card.pai || !card.pai.mind) + balloon_alert(user, "pai unresponsive!") + return + balloon_alert(user, "transferring to suit...") + if(!do_after(user, 5 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return FALSE + if(!user.transferItemToLoc(card, src)) + return + + card.pai.canholo = FALSE + ai = card.pai + balloon_alert(user, "pAI transferred to suit") + balloon_alert(ai, "transferred to a suit") + ai.remote_control = src + for(var/datum/action/action as anything in actions) + action.Grant(ai) + return TRUE + +/** + * Simple proc to extract the pAI from the MODsuit. It's the proc to call if you want to take it out, + * remove_pai() is there so atom_destruction() doesn't have any risk of sleeping. + * + * user - The person trying to take out the pAI from the MODsuit. + * forced - Whether or not we skip the checks and just eject the pAI. Defaults to FALSE. + * feedback - Whether to give feedback via balloon alerts or not. Defaults to TRUE. + */ +/obj/item/mod/control/proc/extract_pai(mob/user, forced = FALSE, feedback = TRUE) + if(!ai) + if(user && feedback) + balloon_alert(user, "no pAI to remove!") + return + if(!ispAI(ai)) + if(user && feedback) + balloon_alert(user, "onboard AI cannot fit in this card!") + return + if(!forced) + if(!open) + if(user && feedback) + balloon_alert(user, "open the suit panel!") + return FALSE + if(!do_after(user, 5 SECONDS, target = src)) + if(user && feedback) + balloon_alert(user, "interrupted!") + return FALSE + + remove_pai(feedback) + + if(feedback && user) + balloon_alert(user, "pAI removed from the suit") + +/** + * Simple proc that handles the safe removal of the pAI from a MOD control unit. + * + * Arguments: + * * feedback - Whether or not we want to give balloon alert feedback to the ai. Defaults to FALSE. + */ +/obj/item/mod/control/proc/remove_pai(feedback = FALSE) + if(!ispAI(ai)) + return + var/mob/living/silicon/pai/pai = ai + var/turf/drop_off = get_turf(src) + if(drop_off) // In case there's no drop_off, the pAI will simply get deleted. + pai.card.forceMove(drop_off) + + for(var/datum/action/action as anything in actions) + if(action.owner == pai) + action.Remove(pai) + + if(feedback) + balloon_alert(pai, "removed from a suit") + pai.remote_control = null + pai.canholo = TRUE + pai = null + +#define MOVE_DELAY 2 +#define WEARER_DELAY 1 +#define LONE_DELAY 5 +#define CELL_PER_STEP (DEFAULT_CHARGE_DRAIN * 2.5) +#define AI_FALL_TIME (1 SECONDS) + +/*obj/item/mod/control/relaymove(mob/user, direction) + var/cell = get_cell() + if((!active && wearer) || !cell || cell.charge < CELL_PER_STEP || user != ai || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE)) + return FALSE + var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? SQRT_2 : 1) * (wearer ? WEARER_DELAY : LONE_DELAY) + if(wearer && !wearer.Process_Spacemove(direction)) + return FALSE + else if(!wearer && (!has_gravity() || !isturf(loc))) + return FALSE + COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown) + cell.charge = max(0, cell.charge - CELL_PER_STEP) + playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE) + if(ismovable(wearer?.loc)) + return wearer.loc.relaymove(wearer, direction) + else if(wearer) + + var/atom/movable/mover = wearer || src + return step(mover, direction) + +#undef MOVE_DELAY +#undef WEARER_DELAY +#undef LONE_DELAY +#undef CELL_PER_STEP +#undef AI_FALL_TIME + + return + REMOVE_TRAIT(wearer, TRAIT_MOBILITY_NOREST, MOD_TRAIT) + +/obj/item/mod/control/ui_state(mob/user) + if(user == ai) + return GLOB.contained_state + return ..() +*/ diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm new file mode 100644 index 000000000000..7a9e710c9382 --- /dev/null +++ b/code/modules/mod/mod_clothes.dm @@ -0,0 +1,56 @@ +/obj/item/clothing/head/mod + name = "MOD helmet" + desc = "A helmet for a MODsuit." + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + icon_state = "standard-helmet" + base_icon_state = "helmet" + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + body_parts_covered = HEAD + heat_protection = HEAD + cold_protection = HEAD + obj_flags = IMMUTABLE_SLOW + visor_flags = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|ALLOWINTERNALS + +/obj/item/clothing/suit/mod + name = "MOD chestplate" + desc = "A chestplate for a MODsuit." + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + icon_state = "standard-chestplate" + base_icon_state = "chestplate" + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + blood_overlay_type = "armor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + body_parts_covered = CHEST|GROIN + heat_protection = CHEST|GROIN + cold_protection = CHEST|GROIN + obj_flags = IMMUTABLE_SLOW + +/obj/item/clothing/gloves/mod + name = "MOD gauntlets" + desc = "A pair of gauntlets for a MODsuit." + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + icon_state = "standard-gauntlets" + base_icon_state = "gauntlets" + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + body_parts_covered = HANDS|ARMS + heat_protection = HANDS|ARMS + cold_protection = HANDS|ARMS + obj_flags = IMMUTABLE_SLOW + +/obj/item/clothing/shoes/mod + name = "MOD boots" + desc = "A pair of boots for a MODsuit." + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + icon_state = "standard-boots" + base_icon_state = "boots" + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + body_parts_covered = FEET|LEGS + heat_protection = FEET|LEGS + cold_protection = FEET|LEGS + obj_flags = IMMUTABLE_SLOW + supports_variations = DIGITIGRADE_VARIATION + can_be_tied = FALSE + visor_flags_inv = HIDESHOES diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm new file mode 100644 index 000000000000..0f37a4fd1f11 --- /dev/null +++ b/code/modules/mod/mod_construction.dm @@ -0,0 +1,275 @@ +/obj/item/mod/construction + desc = "A part used in MOD construction." + icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' + item_state = "rack_parts" + +/obj/item/mod/construction/helmet + name = "MOD helmet" + icon_state = "helmet" + +/obj/item/mod/construction/helmet/examine(mob/user) + . = ..() + . += span_notice("You could insert these into a MOD shell...") + +/obj/item/mod/construction/chestplate + name = "MOD chestplate" + icon_state = "chestplate" + +/obj/item/mod/construction/chestplate/examine(mob/user) + . = ..() + . += span_notice("You could insert these into a MOD shell...") + +/obj/item/mod/construction/gauntlets + name = "MOD gauntlets" + icon_state = "gauntlets" + +/obj/item/mod/construction/gauntlets/examine(mob/user) + . = ..() + . += span_notice("You could insert these into a MOD shell...") + +/obj/item/mod/construction/boots + name = "MOD boots" + icon_state = "boots" + +/obj/item/mod/construction/boots/examine(mob/user) + . = ..() + . += span_notice("You could insert these into a MOD shell...") + +/obj/item/mod/construction/broken_core + name = "broken MOD core" + icon_state = "mod-core" + desc = "An internal power source for a Modular Outerwear Device. You don't seem to be able to source any power from this one, though." + +/obj/item/mod/construction/broken_core/examine(mob/user) + . = ..() + . += span_notice("You could repair it with a screwdriver...") + +/obj/item/mod/construction/broken_core/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(!tool.use_tool(src, user, 5 SECONDS, volume = 30)) + return + new /obj/item/mod/core/standard(drop_location()) + qdel(src) + +/obj/item/mod/construction/plating + name = "MOD external plating" + desc = "External plating used to finish a MOD control unit." + icon_state = "standard-plating" + var/datum/mod_theme/theme = /datum/mod_theme + +/obj/item/mod/construction/plating/Initialize(mapload) + . = ..() + var/datum/mod_theme/used_theme = GLOB.mod_themes[theme] + name = "MOD [used_theme.name] external plating" + desc = "[desc] [used_theme.desc]" + icon_state = "[used_theme.default_skin]-plating" + +/obj/item/mod/construction/plating/engineering + theme = /datum/mod_theme/engineering + +/obj/item/mod/construction/plating/atmospheric + theme = /datum/mod_theme/atmospheric + +/obj/item/mod/construction/plating/medical + theme = /datum/mod_theme/medical + +/obj/item/mod/construction/plating/security + theme = /datum/mod_theme/security + +#define START_STEP "start" +#define CORE_STEP "core" +#define SCREWED_CORE_STEP "screwed_core" +#define HELMET_STEP "helmet" +#define CHESTPLATE_STEP "chestplate" +#define GAUNTLETS_STEP "gauntlets" +#define BOOTS_STEP "boots" +#define WRENCHED_ASSEMBLY_STEP "wrenched_assembly" +#define SCREWED_ASSEMBLY_STEP "screwed_assembly" + +/obj/item/mod/construction/shell + name = "MOD shell" + icon_state = "mod-construction_start" + desc = "A MOD shell." + var/obj/item/core + var/obj/item/helmet + var/obj/item/chestplate + var/obj/item/gauntlets + var/obj/item/boots + var/step = START_STEP + +/obj/item/mod/construction/shell/examine(mob/user) + . = ..() + var/display_text + switch(step) + if(START_STEP) + display_text = "It looks like it's missing a MOD core..." + if(CORE_STEP) + display_text = "The core seems loose..." + if(SCREWED_CORE_STEP) + display_text = "It looks like it's missing a helmet..." + if(HELMET_STEP) + display_text = "It looks like it's missing a chestplate..." + if(CHESTPLATE_STEP) + display_text = "It looks like it's missing gauntlets..." + if(GAUNTLETS_STEP) + display_text = "It looks like it's missing boots..." + if(BOOTS_STEP) + display_text = "The assembly seems unsecured..." + if(WRENCHED_ASSEMBLY_STEP) + display_text = "The assembly seems loose..." + if(SCREWED_ASSEMBLY_STEP) + display_text = "All it's missing is external plating..." + . += span_notice(display_text) + +/obj/item/mod/construction/shell/attackby(obj/item/part, mob/user, params) + . = ..() + switch(step) + if(START_STEP) + if(!istype(part, /obj/item/mod/core)) + return + if(!user.transferItemToLoc(part, src)) + balloon_alert(user, "core stuck to your hand!") + return + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "core inserted") + core = part + step = CORE_STEP + if(CORE_STEP) + if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct + if(part.use_tool(src, user, 0, volume=30)) + balloon_alert(user, "core screwed") + step = SCREWED_CORE_STEP + else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + core.forceMove(drop_location()) + balloon_alert(user, "core taken out") + step = START_STEP + if(SCREWED_CORE_STEP) + if(istype(part, /obj/item/mod/construction/helmet)) //Construct + if(!user.transferItemToLoc(part, src)) + balloon_alert(user, "helmet stuck to your hand!") + return + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "helmet added") + helmet = part + step = HELMET_STEP + else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + balloon_alert(user, "core unscrewed") + step = CORE_STEP + if(HELMET_STEP) + if(istype(part, /obj/item/mod/construction/chestplate)) //Construct + if(!user.transferItemToLoc(part, src)) + balloon_alert(user, "chestplate stuck to your hand!") + return + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "chestplate added") + chestplate = part + step = CHESTPLATE_STEP + else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + helmet.forceMove(drop_location()) + balloon_alert(user, "helmet removed") + helmet = null + step = SCREWED_CORE_STEP + if(CHESTPLATE_STEP) + if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct + if(!user.transferItemToLoc(part, src)) + balloon_alert(user, "gauntlets stuck to your hand!") + return + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "gauntlets added") + gauntlets = part + step = GAUNTLETS_STEP + else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + chestplate.forceMove(drop_location()) + balloon_alert(user, "chestplate removed") + chestplate = null + step = HELMET_STEP + if(GAUNTLETS_STEP) + if(istype(part, /obj/item/mod/construction/boots)) //Construct + if(!user.transferItemToLoc(part, src)) + balloon_alert(user, "boots added") + return + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "you fit [part] onto [src].") + boots = part + step = BOOTS_STEP + else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + gauntlets.forceMove(drop_location()) + balloon_alert(user, "gauntlets removed") + gauntlets = null + step = CHESTPLATE_STEP + if(BOOTS_STEP) + if(part.tool_behaviour == TOOL_WRENCH) //Construct + if(part.use_tool(src, user, 0, volume=30)) + balloon_alert(user, "assembly secured") + step = WRENCHED_ASSEMBLY_STEP + else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + boots.forceMove(drop_location()) + balloon_alert(user, "boots removed") + boots = null + step = GAUNTLETS_STEP + if(WRENCHED_ASSEMBLY_STEP) + if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct + if(part.use_tool(src, user, 0, volume=30)) + balloon_alert(user, "assembly screwed") + step = SCREWED_ASSEMBLY_STEP + else if(part.tool_behaviour == TOOL_WRENCH) //Deconstruct + if(part.use_tool(src, user, 0, volume=30)) + balloon_alert(user, "assembly unsecured") + step = BOOTS_STEP + if(SCREWED_ASSEMBLY_STEP) + if(istype(part, /obj/item/mod/construction/plating)) //Construct + var/obj/item/mod/construction/plating/external_plating = part + if(!user.transferItemToLoc(part, src)) + return + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "suit finished") + var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core) + core = null + qdel(src) + user.put_in_hands(mod) + else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct + if(part.use_tool(src, user, 0, volume=30)) + balloon_alert(user, "assembly unscrewed") + step = SCREWED_ASSEMBLY_STEP + update_icon_state() + +/obj/item/mod/construction/shell/update_icon_state() + . = ..() + icon_state = "mod-construction_[step]" + +/obj/item/mod/construction/shell/Destroy() + QDEL_NULL(core) + QDEL_NULL(helmet) + QDEL_NULL(chestplate) + QDEL_NULL(gauntlets) + QDEL_NULL(boots) + return ..() + +/obj/item/mod/construction/shell/handle_atom_del(atom/deleted_atom) + if(deleted_atom == core) + core = null + if(deleted_atom == helmet) + helmet = null + if(deleted_atom == chestplate) + chestplate = null + if(deleted_atom == gauntlets) + gauntlets = null + if(deleted_atom == boots) + boots = null + return ..() + +#undef START_STEP +#undef CORE_STEP +#undef SCREWED_CORE_STEP +#undef HELMET_STEP +#undef CHESTPLATE_STEP +#undef GAUNTLETS_STEP +#undef BOOTS_STEP +#undef WRENCHED_ASSEMBLY_STEP +#undef SCREWED_ASSEMBLY_STEP diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm new file mode 100644 index 000000000000..0199662862f8 --- /dev/null +++ b/code/modules/mod/mod_control.dm @@ -0,0 +1,713 @@ +/// MODsuits, trade-off between armor and utility +/obj/item/mod + name = "Base MOD" + desc = "You should not see this, yell at a coder!" + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + +/obj/item/mod/control + name = "MOD control unit" + desc = "The control unit of a Modular Outerwear Device, a powered, back-mounted suit that protects against various environments." + icon_state = "control" + base_icon_state = "control" + item_state = "mod_control" + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + strip_delay = 10 SECONDS + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "fire" = 0, "acid" = 0) + actions_types = list( + /datum/action/item_action/mod/deploy, + /datum/action/item_action/mod/activate, + /datum/action/item_action/mod/panel, + /datum/action/item_action/mod/module, + /datum/action/item_action/mod/deploy/ai, + /datum/action/item_action/mod/activate/ai, + /datum/action/item_action/mod/panel/ai, + /datum/action/item_action/mod/module/ai, + ) + resistance_flags = NONE + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + siemens_coefficient = 0.5 + //alternate_worn_layer = HAND_LAYER+0.1 //we want it to go above generally everything, but not hands + /// The MOD's theme, decides on some stuff like armor and statistics. + var/datum/mod_theme/theme = /datum/mod_theme + /// Looks of the MOD. + var/skin = "standard" + /// Theme of the MOD TGUI + var/ui_theme = "ntos" + /// If the suit is deployed and turned on. + var/active = FALSE + /// If the suit wire/module hatch is open. + var/open = FALSE + /// If the suit is ID locked. + var/locked = FALSE + /// If the suit is malfunctioning. + var/malfunctioning = FALSE + /// If the suit is currently activating/deactivating. + var/activating = FALSE + /// How long the MOD is electrified for. + var/seconds_electrified = MACHINE_NOT_ELECTRIFIED + /// If the suit interface is broken. + var/interface_break = FALSE + /// How much module complexity can this MOD carry. + var/complexity_max = DEFAULT_MAX_COMPLEXITY + /// How much module complexity this MOD is carrying. + var/complexity = 0 + /// Power usage of the MOD. + var/charge_drain = DEFAULT_CHARGE_DRAIN + /// Slowdown of the MOD when not active. + var/slowdown_inactive = 1.25 + /// Slowdown of the MOD when active. + var/slowdown_active = 0.75 + /// How long this MOD takes each part to seal. + var/activation_step_time = MOD_ACTIVATION_STEP_TIME + /// Extended description of the theme. + var/extended_desc + /// MOD helmet. + var/obj/item/clothing/head/mod/helmet + /// MOD chestplate. + var/obj/item/clothing/suit/mod/chestplate + /// MOD gauntlets. + var/obj/item/clothing/gloves/mod/gauntlets + /// MOD boots. + var/obj/item/clothing/shoes/mod/boots + /// MOD core. + var/obj/item/mod/core/core + /// Associated list of parts (helmet, chestplate, gauntlets, boots) to their unsealed worn layer. + var/list/mod_parts = list() + /// Associated list of parts that can overslot to their overslot (overslot means the part can cover another layer of clothing). + var/list/overslotting_parts = list() + /// Modules the MOD should spawn with. + var/list/initial_modules = list() + /// Modules the MOD currently possesses. + var/list/modules = list() + /// Currently used module. + var/obj/item/mod/module/selected_module + /// AI mob inhabiting the MOD. + var/mob/living/silicon/ai/ai + /// Delay between moves as AI. + var/movedelay = 0 + /// Cooldown for AI moves. + COOLDOWN_DECLARE(cooldown_mod_move) + /// Person wearing the MODsuit. + var/mob/living/carbon/human/wearer + +/obj/item/mod/control/Initialize(mapload, datum/mod_theme/new_theme, new_skin, obj/item/mod/core/new_core) + . = ..() + if(new_theme) + theme = new_theme + theme = GLOB.mod_themes[theme] + slot_flags = theme.slot_flags + extended_desc = theme.extended_desc + slowdown_inactive = theme.slowdown_inactive + slowdown_active = theme.slowdown_active + complexity_max = theme.complexity_max + ui_theme = theme.ui_theme + charge_drain = theme.charge_drain + initial_modules += theme.inbuilt_modules + wires = new /datum/wires/mod(src) + if(length(req_access)) + locked = TRUE + new_core?.install(src) + helmet = new /obj/item/clothing/head/mod(src) + mod_parts += helmet + chestplate = new /obj/item/clothing/suit/mod(src) + chestplate.allowed = typecacheof(theme.allowed_suit_storage) + mod_parts += chestplate + gauntlets = new /obj/item/clothing/gloves/mod(src) + mod_parts += gauntlets + boots = new /obj/item/clothing/shoes/mod(src) + mod_parts += boots + var/list/all_parts = mod_parts + src + for(var/obj/item/part as anything in all_parts) + part.name = "[theme.name] [part.name]" + part.desc = "[part.desc] [theme.desc]" + part.armor = getArmor(arglist(theme.armor)) + part.resistance_flags = theme.resistance_flags + part.flags_1 |= theme.atom_flags //flags like initialization or admin spawning are here, so we cant set, have to add + part.heat_protection = NONE + part.cold_protection = NONE + part.max_heat_protection_temperature = theme.max_heat_protection_temperature + part.min_cold_protection_temperature = theme.min_cold_protection_temperature + part.siemens_coefficient = theme.siemens_coefficient + for(var/obj/item/part as anything in mod_parts) + RegisterSignal(part, COMSIG_OBJ_DESTRUCTION, PROC_REF(on_part_destruction)) + RegisterSignal(part, COMSIG_PARENT_QDELETING, PROC_REF(on_part_deletion)) + set_mod_skin(new_skin || theme.default_skin) + update_speed() + for(var/obj/item/mod/module/module as anything in initial_modules) + module = new module(src) + install(module) + RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit)) + movedelay = CONFIG_GET(number/movedelay/run_delay) + +/obj/item/mod/control/Destroy() + if(active) + STOP_PROCESSING(SSobj, src) + for(var/obj/item/mod/module/module as anything in modules) + uninstall(module, deleting = TRUE) + for(var/obj/item/part as anything in mod_parts) + overslotting_parts -= part + var/atom/deleting_atom + if(!QDELETED(helmet)) + deleting_atom = helmet + helmet = null + mod_parts -= deleting_atom + qdel(deleting_atom) + if(!QDELETED(chestplate)) + deleting_atom = chestplate + chestplate = null + mod_parts -= deleting_atom + qdel(deleting_atom) + if(!QDELETED(gauntlets)) + deleting_atom = gauntlets + gauntlets = null + mod_parts -= deleting_atom + qdel(deleting_atom) + if(!QDELETED(boots)) + deleting_atom = boots + boots = null + mod_parts -= deleting_atom + qdel(deleting_atom) + if(core) + QDEL_NULL(core) + QDEL_NULL(wires) + return ..() + +/obj/item/mod/control/obj_destruction(damage_flag) + for(var/obj/item/mod/module/module as anything in modules) + uninstall(module) + for(var/obj/item/part as anything in mod_parts) + if(!overslotting_parts[part]) + continue + var/obj/item/overslot = overslotting_parts[part] + overslot.forceMove(drop_location()) + overslotting_parts[part] = null + /*if(ai) + ai.controlled_equipment = null + ai.remote_control = null + for(var/datum/action/action as anything in actions) + if(action.owner == ai) + action.Remove(ai) + new /obj/item/mod/ai_minicard(drop_location(), ai)*/ + return ..() + +/obj/item/mod/control/examine(mob/user) + . = ..() + if(active) + . += span_notice("Charge: [core ? "[get_charge_percent()]%" : "No core"].") + . += span_notice("Selected module: [selected_module || "None"].") + if(!open && !active) + . += span_notice("You could put it on your back to turn it on.") + . += span_notice("You could open the cover with a screwdriver.") + else if(open) + . += span_notice("You could close the cover with a screwdriver.") + . += span_notice("You could use modules on it to install them.") + . += span_notice("You could remove modules with a crowbar.") + . += span_notice("You could update the access lock with an ID.") + . += span_notice("You could access the wire panel with a wire tool.") + if(core) + . += span_notice("You could remove [core] with a wrench.") + else + . += span_notice("You could use a MOD core on it to install one.") + if(ai) + . += span_notice("You could remove [ai] with an intellicard.") + else + . += span_notice("You could install an AI with an intellicard.") + +/obj/item/mod/control/examine_more(mob/user) + . = ..() + . += "[extended_desc]" + +/obj/item/mod/control/process(delta_time) + if(seconds_electrified > MACHINE_NOT_ELECTRIFIED) + seconds_electrified-- + if(!get_charge() && active && !activating) + power_off() + return PROCESS_KILL + var/malfunctioning_charge_drain = 0 + if(malfunctioning) + malfunctioning_charge_drain = rand(1,20) + subtract_charge((charge_drain + malfunctioning_charge_drain)*delta_time) + update_charge_alert() + for(var/obj/item/mod/module/module as anything in modules) + if(malfunctioning && module.active && DT_PROB(5, delta_time)) + module.on_deactivation(display_message = TRUE) + module.on_process(delta_time) + +/obj/item/mod/control/equipped(mob/user, slot) + ..() + if(slot == slot_flags) + set_wearer(user) + else if(wearer) + unset_wearer() + +/obj/item/mod/control/dropped(mob/user) + . = ..() + if(!wearer) + return + clean_up() + +/obj/item/mod/control/item_action_slot_check(slot) + if(slot & slot_flags) + return TRUE + +/obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) + . = ..() + if(!wearer || old_loc != wearer || loc == wearer) + return + clean_up() + +/obj/item/mod/control/allow_attack_hand_drop(mob/user) + if(user != wearer) + return ..() + for(var/obj/item/part as anything in mod_parts) + if(part.loc != src) + balloon_alert(user, "retract parts first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) + return FALSE + +/obj/item/mod/control/MouseDrop(atom/over_object) + if(usr != wearer || !istype(over_object, /atom/movable/screen/inventory/hand)) + return ..() + for(var/obj/item/part as anything in mod_parts) + if(part.loc != src) + balloon_alert(wearer, "retract parts first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) + return + if(!wearer.incapacitated()) + var/atom/movable/screen/inventory/hand/ui_hand = over_object + if(wearer.putItemFromInventoryInHandIfPossible(src, ui_hand.held_index)) + add_fingerprint(usr) + return ..() + +/obj/item/mod/control/wrench_act(mob/living/user, obj/item/wrench) + if(..()) + return TRUE + if(seconds_electrified && get_charge() && shock(user)) + return TRUE + if(open) + if(!core) + balloon_alert(user, "no core!") + return TRUE + balloon_alert(user, "removing core...") + wrench.play_tool_sound(src, 100) + if(!wrench.use_tool(src, user, 3 SECONDS) || !open) + balloon_alert(user, "interrupted!") + return TRUE + wrench.play_tool_sound(src, 100) + balloon_alert(user, "core removed") + core.forceMove(drop_location()) + update_charge_alert() + return TRUE + return ..() + +/obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver) + if(..()) + return TRUE + if(active || activating)// || ai_controller) + balloon_alert(user, "deactivate suit first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + balloon_alert(user, "[open ? "closing" : "opening"] cover...") + screwdriver.play_tool_sound(src, 100) + if(screwdriver.use_tool(src, user, 1 SECONDS)) + if(active || activating) + balloon_alert(user, "deactivate suit first!") + screwdriver.play_tool_sound(src, 100) + balloon_alert(user, "cover [open ? "closed" : "opened"]") + open = !open + else + balloon_alert(user, "interrupted!") + return TRUE + +/obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar) + . = ..() + if(!open) + balloon_alert(user, "open the cover first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(!allowed(user)) + balloon_alert(user, "insufficient access!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL) + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(length(modules)) + var/list/removable_modules = list() + for(var/obj/item/mod/module/module as anything in modules) + if(!module.removable) + continue + removable_modules += module + var/obj/item/mod/module/module_to_remove = tgui_input_list(user, "Which module to remove?", "Module Removal", removable_modules) + if(!module_to_remove?.mod) + return FALSE + uninstall(module_to_remove) + module_to_remove.forceMove(drop_location()) + crowbar.play_tool_sound(src, 100) + return TRUE + balloon_alert(user, "no modules!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + +/obj/item/mod/control/attackby(obj/item/attacking_item, mob/living/user, params) + if(istype(attacking_item, /obj/item/mod/module)) + if(!open) + balloon_alert(user, "open the cover first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + install(attacking_item, user) + return TRUE + else if(istype(attacking_item, /obj/item/mod/core)) + if(!open) + balloon_alert(user, "open the cover first!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + if(core) + balloon_alert(user, "core already installed!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE + var/obj/item/mod/core/attacking_core = attacking_item + 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) + return TRUE + else if(open && attacking_item.GetID()) + update_access(user, attacking_item.GetID()) + return TRUE + else if(open && istype(attacking_item, /obj/item/stock_parts/cell) && istype(core, /obj/item/mod/core/standard)) + var/obj/item/mod/core/standard/attacked_core = core + attacked_core.on_attackby(src, attacking_item, wearer) + return TRUE + return ..() + +/obj/item/mod/control/get_cell() + if(!open) + return + var/obj/item/stock_parts/cell/cell = get_charge_source() + if(!istype(cell)) + return + return cell + +/obj/item/mod/control/GetAccess() + /*if(ai_controller) + return req_access.Copy() + else */ + return ..() + +/obj/item/mod/control/emag_act(mob/user) + locked = !locked + balloon_alert(user, "suit access [locked ? "locked" : "unlocked"]") + +/obj/item/mod/control/emp_act(severity) + . = ..() + if(!active || !wearer) + return + to_chat(wearer, span_notice("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!")) + if(. & EMP_PROTECT_CONTENTS) + return + selected_module?.on_deactivation(display_message = TRUE) + wearer.apply_damage(10 / severity, BURN, spread_damage=TRUE) + to_chat(wearer, span_danger("You feel [src] heat up from the EMP, burning you slightly.")) + if(wearer.stat < UNCONSCIOUS && prob(10)) + wearer.emote("scream") + +/*obj/item/mod/control/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot) + if(visuals_only) + set_wearer(outfit_wearer) //we need to set wearer manually since it doesnt call equipped + quick_activation()*/ + +/obj/item/mod/control/doStrip(mob/stripper, mob/owner) + if(active && !toggle_activate(stripper, force_deactivate = TRUE)) + return + for(var/obj/item/part as anything in mod_parts) + if(part.loc == src) + continue + retract(null, part) + return ..() + +/obj/item/mod/control/worn_overlays(isinhands = FALSE, icon_file) + . = ..() + for(var/obj/item/mod/module/module as anything in modules) + var/list/module_icons = module.generate_worn_overlay(src.layer) + if(!length(module_icons)) + continue + . += module_icons + +/obj/item/mod/control/update_icon_state() + item_state = "[skin]-control[active ? "-sealed" : ""]" + return ..() + +/obj/item/mod/control/proc/set_wearer(mob/user) + wearer = user + SEND_SIGNAL(src, COMSIG_MOD_WEARER_SET, wearer) + RegisterSignal(wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit)) + RegisterSignal(wearer, COMSIG_SPECIES_GAIN, PROC_REF(on_species_gain)) + update_charge_alert() + for(var/obj/item/mod/module/module as anything in modules) + module.on_equip() + +/obj/item/mod/control/proc/unset_wearer() + 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("mod_charge") + SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer) + wearer = null + +/obj/item/mod/control/proc/clean_up() + if(active || activating) + for(var/obj/item/mod/module/module as anything in modules) + if(!module.active) + continue + module.on_deactivation(display_message = FALSE) + for(var/obj/item/part as anything in mod_parts) + seal_part(part, seal = FALSE) + for(var/obj/item/part as anything in mod_parts) + retract(null, part) + if(active) + finish_activation(on = FALSE) + var/mob/old_wearer = wearer + unset_wearer() + old_wearer.temporarilyRemoveItemFromInventory(src) + +/obj/item/mod/control/proc/on_species_gain(datum/source, datum/species/new_species, datum/species/old_species) + SIGNAL_HANDLER + + var/list/all_parts = mod_parts + src + for(var/obj/item/part in all_parts) + if(!(part.slot_flags in new_species.no_equip) || is_type_in_list(new_species, part.species_exception)) + continue + forceMove(drop_location()) + return + +/obj/item/mod/control/proc/quick_module(mob/user) + if(!length(modules)) + return + var/list/display_names = list() + var/list/items = list() + for(var/obj/item/mod/module/module as anything in modules) + if(module.module_type == MODULE_PASSIVE) + continue + display_names[module.name] = REF(module) + var/image/module_image = image(icon = module.icon, icon_state = module.icon_state) + if(module == selected_module) + module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected") + else if(module.active) + module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active") + if(!COOLDOWN_FINISHED(module, cooldown_timer)) + module_image.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown")) + items += list(module.name = module_image) + if(!length(items)) + return + var/radial_anchor = src + if(istype(user.loc, /obj/effect/dummy/phased_mob)) + radial_anchor = get_turf(user.loc) //they're phased out via some module, anchor the radial on the turf so it may still display + var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = TRUE, tooltips = TRUE) + if(!pick) + return + var/module_reference = display_names[pick] + var/obj/item/mod/module/picked_module = locate(module_reference) in modules + if(!istype(picked_module)) + return + picked_module.on_select() + +/obj/item/mod/control/proc/shock(mob/living/user) + if(!istype(user) || get_charge() < 1) + return FALSE + do_sparks(5, TRUE, src) + var/check_range = TRUE + return electrocute_mob(user, get_charge_source(), src, 0.7, check_range) + +/obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user) + for(var/obj/item/mod/module/old_module as anything in modules) + if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules)) + if(user) + balloon_alert(user, "[new_module] incompatible with [old_module]!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + if(is_type_in_list(new_module, theme.module_blacklist)) + if(user) + balloon_alert(user, "[src] doesn't accept [new_module]!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + var/complexity_with_module = complexity + complexity_with_module += new_module.complexity + if(complexity_with_module > complexity_max) + if(user) + balloon_alert(user, "[new_module] would make [src] too complex!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + new_module.forceMove(src) + modules += new_module + complexity += new_module.complexity + new_module.mod = src + new_module.on_install() + if(wearer) + new_module.on_equip() + + if(user) + balloon_alert(user, "[new_module] added") + playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) + +/obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE) + modules -= old_module + complexity -= old_module.complexity + if(active) + old_module.on_suit_deactivation(deleting = deleting) + if(old_module.active) + old_module.on_deactivation(display_message = !deleting, deleting = deleting) + old_module.on_uninstall(deleting = deleting) + QDEL_LIST_ASSOC_VAL(old_module.pinned_to) + old_module.mod = null + +/// Intended for callbacks, don't use normally, just get wearer by itself. +/obj/item/mod/control/proc/get_wearer() + return wearer + +/obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card) + if(!allowed(user)) + balloon_alert(user, "insufficient access!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + req_access = card.access.Copy() + balloon_alert(user, "access updated") + +/obj/item/mod/control/proc/get_charge_source() + return core?.charge_source() + +/obj/item/mod/control/proc/get_charge() + return core?.charge_amount() || 0 + +/obj/item/mod/control/proc/get_max_charge() + return core?.max_charge_amount() || 1 //avoid dividing by 0 + +/obj/item/mod/control/proc/get_charge_percent() + return ROUND_UP((get_charge() / get_max_charge()) * 100) + +/obj/item/mod/control/proc/add_charge(amount) + return core?.add_charge(amount) || FALSE + +/obj/item/mod/control/proc/subtract_charge(amount) + return core?.subtract_charge(amount) || FALSE + +/obj/item/mod/control/proc/check_charge(amount) + return core?.check_charge(amount) || FALSE + +/obj/item/mod/control/proc/update_charge_alert() + if(!wearer) + return + if(!core) + wearer.throw_alert("mod_charge", /atom/movable/screen/alert/nocore) + return + core.update_charge_alert() + +/obj/item/mod/control/proc/update_speed() + var/list/all_parts = mod_parts + src + for(var/obj/item/part as anything in all_parts) + part.slowdown = (active ? slowdown_active : slowdown_inactive) / length(all_parts) + wearer?.update_equipment_speed_mods() + +/obj/item/mod/control/proc/power_off() + balloon_alert(wearer, "Нет энергии!") + toggle_activate(wearer, force_deactivate = TRUE) + +/obj/item/mod/control/proc/set_mod_color(new_color) + var/list/all_parts = mod_parts + src + for(var/obj/item/part as anything in all_parts) + part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + part.add_atom_colour(new_color, FIXED_COLOUR_PRIORITY) + wearer?.regenerate_icons() + +/obj/item/mod/control/proc/set_mod_skin(new_skin) + if(active) + CRASH("[src] tried to set skin while active!") + skin = new_skin + var/list/used_skin = theme.skins[new_skin] + if(used_skin[CONTROL_LAYER]) + alternate_worn_layer = used_skin[CONTROL_LAYER] + var/list/skin_updating = mod_parts + src + for(var/obj/item/part as anything in skin_updating) + part.icon = used_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi' + //part.mob_overlay_icon = used_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi' + part.icon_state = "[skin]-[part.base_icon_state]" + for(var/obj/item/clothing/part as anything in mod_parts) + var/used_category + if(part == helmet) + used_category = HELMET_FLAGS + if(part == chestplate) + used_category = CHESTPLATE_FLAGS + if(part == gauntlets) + used_category = GAUNTLETS_FLAGS + if(part == boots) + used_category = BOOTS_FLAGS + var/list/category = used_skin[used_category] + part.clothing_flags = category[UNSEALED_CLOTHING] || NONE + part.visor_flags = category[SEALED_CLOTHING] || NONE + part.flags_inv = category[UNSEALED_INVISIBILITY] || NONE + part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE + part.flags_cover = category[UNSEALED_COVER] || NONE + part.visor_flags_cover = category[SEALED_COVER] || NONE + part.alternate_worn_layer = category[UNSEALED_LAYER] + mod_parts[part] = part.alternate_worn_layer + if(!category[CAN_OVERSLOT]) + if(overslotting_parts[part]) + var/obj/item/overslot = overslotting_parts[part] + overslot.forceMove(drop_location()) + overslotting_parts -= part + continue + overslotting_parts |= part + wearer?.regenerate_icons() + +/obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction) + SIGNAL_HANDLER + + if(part.loc == src) + return + if(part == core) + core.uninstall() + update_charge_alert() + return + if(part.loc == wearer) + return + if(part in modules) + uninstall(part) + return + if(part in mod_parts) + if(!wearer) + part.forceMove(src) + return + retract(wearer, part) + if(active) + INVOKE_ASYNC(src, PROC_REF(toggle_activate), wearer, TRUE) + +/obj/item/mod/control/proc/on_part_destruction(obj/item/part, damage_flag) + SIGNAL_HANDLER + + if(overslotting_parts[part]) + var/obj/item/overslot = overslotting_parts[part] + overslot.forceMove(drop_location()) + overslotting_parts[part] = null + if(QDELETED(src)) + return + obj_destruction(damage_flag) + +/obj/item/mod/control/proc/on_part_deletion(obj/item/part) + SIGNAL_HANDLER + + if(QDELETED(src)) + return + qdel(src) + +/obj/item/mod/control/proc/on_overslot_exit(datum/source, atom/movable/overslot, direction) + SIGNAL_HANDLER + + if(overslot != overslotting_parts[source]) + return + overslotting_parts[source] = null diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm new file mode 100644 index 000000000000..4c9d16ef7b7d --- /dev/null +++ b/code/modules/mod/mod_core.dm @@ -0,0 +1,357 @@ +/obj/item/mod/core + name = "MOD core" + desc = "A non-functional MOD core. Inform the admins if you see this." + icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' + icon_state = "mod-core" + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + /// MOD unit we are powering. + var/obj/item/mod/control/mod + +/obj/item/mod/core/Destroy() + if(mod) + uninstall() + return ..() + +/obj/item/mod/core/proc/install(obj/item/mod/control/mod_unit) + mod = mod_unit + mod.core = src + forceMove(mod) + +/obj/item/mod/core/proc/uninstall() + mod.core = null + mod = null + +/obj/item/mod/core/proc/charge_source() + return + +/obj/item/mod/core/proc/charge_amount() + return 0 + +/obj/item/mod/core/proc/max_charge_amount() + return 1 + +/obj/item/mod/core/proc/add_charge(amount) + return FALSE + +/obj/item/mod/core/proc/subtract_charge(amount) + return FALSE + +/obj/item/mod/core/proc/check_charge(amount) + return FALSE + +/obj/item/mod/core/proc/update_charge_alert() + mod.wearer.clear_alert("mod_charge") + +/obj/item/mod/core/infinite + name = "MOD infinite core" + icon_state = "mod-core-infinite" + desc = "A fusion core using the rare Fixium to sustain enough energy for the lifetime of the MOD's user. \ + This might be because of the slowly killing poison inside, but those are just rumors." + +/obj/item/mod/core/infinite/charge_source() + return src + +/obj/item/mod/core/infinite/charge_amount() + return INFINITY + +/obj/item/mod/core/infinite/max_charge_amount() + return INFINITY + +/obj/item/mod/core/infinite/add_charge(amount) + return TRUE + +/obj/item/mod/core/infinite/subtract_charge(amount) + return TRUE + +/obj/item/mod/core/infinite/check_charge(amount) + return TRUE + +/obj/item/mod/core/standard + name = "MOD standard core" + icon_state = "mod-core-standard" + desc = "Growing in the most lush, fertile areas of the planet Sprout, there is a crystal known as the Heartbloom. \ + These rare, organic piezoelectric crystals are of incredible cultural significance to the artist castes of the \ + Ethereals, owing to their appearance; which is exactly similar to that of an Ethereal's heart.\n\ + Which one you have in your suit is unclear, but either way, \ + it's been repurposed to be an internal power source for a Modular Outerwear Device." + /// Installed cell. + var/obj/item/stock_parts/cell/cell + +/obj/item/mod/core/standard/Destroy() + if(cell) + QDEL_NULL(cell) + return ..() + +/obj/item/mod/core/standard/install(obj/item/mod/control/mod_unit) + . = ..() + if(cell) + install_cell(cell) + RegisterSignal(mod, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(mod, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand)) + RegisterSignal(mod, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) + RegisterSignal(mod, COMSIG_MOD_WEARER_SET, PROC_REF(on_wearer_set)) + if(mod.wearer) + on_wearer_set(mod, mod.wearer) + +/obj/item/mod/core/standard/uninstall() + if(!QDELETED(cell)) + cell.forceMove(drop_location()) + UnregisterSignal(mod, list(COMSIG_PARENT_EXAMINE, COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_ATTACKBY, COMSIG_MOD_WEARER_SET)) + if(mod.wearer) + on_wearer_unset(mod, mod.wearer) + return ..() + +/obj/item/mod/core/standard/charge_source() + return cell + +/obj/item/mod/core/standard/charge_amount() + var/obj/item/stock_parts/cell/charge_source = charge_source() + return charge_source?.charge || 0 + +/obj/item/mod/core/standard/max_charge_amount(amount) + var/obj/item/stock_parts/cell/charge_source = charge_source() + return charge_source?.maxcharge || 1 + +/obj/item/mod/core/standard/add_charge(amount) + var/obj/item/stock_parts/cell/charge_source = charge_source() + if(!charge_source) + return FALSE + return charge_source.give(amount) + +/obj/item/mod/core/standard/subtract_charge(amount) + var/obj/item/stock_parts/cell/charge_source = charge_source() + if(!charge_source) + return FALSE + return charge_source.use(amount, TRUE) + +/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("mod_charge", /atom/movable/screen/alert/nocell) + return + var/remaining_cell = charge_amount() / max_charge_amount() + switch(remaining_cell) + if(0.75 to INFINITY) + mod.wearer.clear_alert("mod_charge") + if(0.5 to 0.75) + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 1) + if(0.25 to 0.5) + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 2) + if(0.01 to 0.25) + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 3) + else + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/emptycell) + +/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)) + +/obj/item/mod/core/standard/proc/uninstall_cell() + if(!cell) + return + 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 + + if(!istype(cell) || cell.loc == src) + return + uninstall_cell() + +/obj/item/mod/core/standard/proc/on_examine(datum/source, mob/examiner, list/examine_text) + SIGNAL_HANDLER + + if(!mod.open) + return + examine_text += cell ? "You could remove the cell with an empty hand." : "You could use a cell on it to install one." + +/obj/item/mod/core/standard/proc/on_attack_hand(datum/source, mob/living/user) + SIGNAL_HANDLER + + if(mod.seconds_electrified && charge_amount() && mod.shock(user)) + return COMPONENT_CANCEL_ATTACK_CHAIN + if(mod.open && mod.loc == user) + INVOKE_ASYNC(src, PROC_REF(mod_uninstall_cell), user) + return COMPONENT_CANCEL_ATTACK_CHAIN + return NONE + +/obj/item/mod/core/standard/proc/mod_uninstall_cell(mob/living/user) + if(!cell) + mod.balloon_alert(user, "no cell!") + return + mod.balloon_alert(user, "removing cell...") + if(!do_after(user, 1.5 SECONDS, target = mod)) + mod.balloon_alert(user, "interrupted!") + return + mod.balloon_alert(user, "cell removed") + playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) + 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 + + if(istype(attacking_item, /obj/item/stock_parts/cell)) + if(!mod.open) + mod.balloon_alert(user, "open the cover first!") + playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return NONE + if(cell) + mod.balloon_alert(user, "cell already installed!") + playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return COMPONENT_NO_AFTERATTACK + 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 + +/obj/item/mod/core/standard/proc/on_wearer_set(datum/source, mob/user) + SIGNAL_HANDLER + + RegisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge)) + RegisterSignal(mod, COMSIG_MOD_WEARER_UNSET, PROC_REF(on_wearer_unset)) + +/obj/item/mod/core/standard/proc/on_wearer_unset(datum/source, mob/user) + SIGNAL_HANDLER + + UnregisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) + UnregisterSignal(mod, COMSIG_MOD_WEARER_UNSET) + +/obj/item/mod/core/standard/proc/on_borg_charge(datum/source, amount) + SIGNAL_HANDLER + + add_charge(amount) + mod.update_charge_alert() + +/obj/item/mod/core/ethereal + name = "MOD ethereal core" + icon_state = "mod-core-ethereal" + desc = "A reverse engineered core of a Modular Outerwear Device. Using natural liquid electricity from Ethereals, \ + preventing the need to use external sources to convert electric charge." + /// A modifier to all charge we use, ethereals don't need to spend as much energy as normal suits. + var/charge_modifier = 0.1 + +/obj/item/mod/core/ethereal/charge_source() + var/obj/item/organ/stomach/ethereal/ethereal_stomach = mod.wearer.getorganslot(ORGAN_SLOT_STOMACH) + if(!istype(ethereal_stomach)) + return + return ethereal_stomach + +/obj/item/mod/core/ethereal/charge_amount() + var/obj/item/organ/stomach/ethereal/charge_source = charge_source() + return charge_source?.crystal_charge || ELZUOSE_CHARGE_NONE + +/obj/item/mod/core/ethereal/max_charge_amount() + return ELZUOSE_CHARGE_FULL + +/obj/item/mod/core/ethereal/add_charge(amount) + var/obj/item/organ/stomach/ethereal/charge_source = charge_source() + if(!charge_source) + return FALSE + charge_source.adjust_charge(amount*charge_modifier) + return TRUE + +/obj/item/mod/core/ethereal/subtract_charge(amount) + var/obj/item/organ/stomach/ethereal/charge_source = charge_source() + if(!charge_source) + return FALSE + charge_source.adjust_charge(-amount*charge_modifier) + return TRUE + +/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/stomach/ethereal/charge_source = charge_source() + if(charge_source) + mod.wearer.clear_alert("mod_charge") + return + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/nocell) + +/obj/item/mod/core/plasma + name = "MOD plasma core" + icon_state = "mod-core-plasma" + desc = "Nanotrasen's attempt at capitalizing on their plasma research. These plasma cores are refueled \ + through plasma ore, allowing for easy continued use by their mining squads." + /// How much charge we can store. + var/maxcharge = 10000 + /// How much charge we are currently storing. + var/charge = 10000 + /// Associated list of charge sources and how much they charge, only stacks allowed. + var/list/charger_list = list(/obj/item/stack/ore/plasma = 1500, /obj/item/stack/sheet/mineral/plasma = 2000) + +/obj/item/mod/core/plasma/install(obj/item/mod/control/mod_unit) + . = ..() + RegisterSignal(mod, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) + +/obj/item/mod/core/plasma/uninstall() + UnregisterSignal(mod, COMSIG_PARENT_ATTACKBY) + return ..() + +/obj/item/mod/core/plasma/attackby(obj/item/attacking_item, mob/user, params) + if(charge_plasma(attacking_item, user)) + return TRUE + return ..() + +/obj/item/mod/core/plasma/charge_source() + return src + +/obj/item/mod/core/plasma/charge_amount() + return charge + +/obj/item/mod/core/plasma/max_charge_amount() + return maxcharge + +/obj/item/mod/core/plasma/add_charge(amount) + charge = min(maxcharge, charge + amount) + return TRUE + +/obj/item/mod/core/plasma/subtract_charge(amount) + charge = max(0, charge - amount) + 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) + if(0.75 to INFINITY) + mod.wearer.clear_alert("mod_charge") + if(0.5 to 0.75) + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell/plasma, 1) + if(0.25 to 0.5) + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell/plasma, 2) + if(0.01 to 0.25) + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell/plasma, 3) + else + mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/emptycell/plasma) + +/obj/item/mod/core/plasma/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user) + SIGNAL_HANDLER + + if(charge_plasma(attacking_item, user)) + return COMPONENT_NO_AFTERATTACK + return NONE + +/obj/item/mod/core/plasma/proc/charge_plasma(obj/item/stack/plasma, mob/user) + var/charge_given = is_type_in_list(plasma, charger_list, zebra = TRUE) + if(!charge_given) + return FALSE + var/uses_needed = min(plasma.amount, ROUND_UP((max_charge_amount() - charge_amount()) / charge_given)) + if(!plasma.use(uses_needed)) + return FALSE + add_charge(uses_needed * charge_given) + balloon_alert(user, "core refueled") + return TRUE diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm new file mode 100644 index 000000000000..aead577224bc --- /dev/null +++ b/code/modules/mod/mod_paint.dm @@ -0,0 +1,192 @@ +#define MODPAINT_MAX_COLOR_VALUE 1.25 +#define MODPAINT_MIN_COLOR_VALUE 0 +#define MODPAINT_MAX_SECTION_COLORS 2 +#define MODPAINT_MIN_SECTION_COLORS 0.25 +#define MODPAINT_MAX_OVERALL_COLORS 4 +#define MODPAINT_MIN_OVERALL_COLORS 1.5 + +/obj/item/mod/paint + name = "MOD paint kit" + desc = "This kit will repaint your MODsuit to something unique." + icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' + icon_state = "paintkit" + var/obj/item/mod/control/editing_mod + var/atom/movable/screen/map_view/proxy_view + var/list/current_color + +/obj/item/mod/paint/Initialize(mapload) + . = ..() + current_color = color_matrix_identity() + +/obj/item/mod/paint/examine(mob/user) + . = ..() + . += span_notice("Left-click a MODsuit to change skin.") + //. += span_notice("Right-click a MODsuit to recolor.") + +/obj/item/mod/paint/pre_attack(atom/attacked_atom, mob/living/user, params) + if(!istype(attacked_atom, /obj/item/mod/control)) + return ..() + var/obj/item/mod/control/mod = attacked_atom + if(mod.active || mod.activating) + balloon_alert(user, "suit is active!") + return TRUE + paint_skin(mod, user) + +/*obj/item/mod/paint/pre_attack_secondary(atom/attacked_atom, mob/living/user, params) + if(!istype(attacked_atom, /obj/item/mod/control)) + return .() + var/obj/item/mod/control/mod = attacked_atom + if(mod.active || mod.activating) + balloon_alert(user, "suit is active!") + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(editing_mod) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + editing_mod = mod + proxy_view = new() + proxy_view.generate_view("color_matrix_proxy_[REF(user.client)]") + + proxy_view.appearance = editing_mod.appearance + proxy_view.color = null + proxy_view.display_to(user) + ui_interact(user) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN*/ + +/obj/item/mod/paint/ui_interact(mob/user, datum/tgui/ui) + if(!editing_mod) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MODpaint", name) + ui.open() + +/obj/item/mod/paint/ui_host() + return editing_mod + +/obj/item/mod/paint/ui_close(mob/user) + . = ..() + editing_mod = null + QDEL_NULL(proxy_view) + current_color = color_matrix_identity() + +/obj/item/mod/paint/ui_status(mob/user) + if(check_menu(editing_mod, user)) + return ..() + return UI_CLOSE + +/obj/item/mod/paint/ui_static_data(mob/user) + var/list/data = list() + data["mapRef"] = proxy_view.assigned_map + return data + +/obj/item/mod/paint/ui_data(mob/user) + var/list/data = list() + data["currentColor"] = current_color + return data + +/obj/item/mod/paint/ui_act(action, list/params) + . = ..() + if(.) + return + switch(action) + if("transition_color") + current_color = params["color"] + animate(proxy_view, time = 0.5 SECONDS, color = current_color) + if("confirm") + if(length(current_color) != 20) //20 is the length of a matrix identity list + return + for(var/color_value in current_color) + if(isnum(color_value)) + continue + return + var/total_color_value = 0 + var/list/total_colors = current_color.Copy() + total_colors.Cut(13, length(total_colors)) // 13 to 20 are just a and c, dont want to count them + var/red_value = current_color[1] + current_color[5] + current_color[9] //rr + gr + br + var/green_value = current_color[2] + current_color[6] + current_color[10] //rg + gg + bg + var/blue_value = current_color[3] + current_color[7] + current_color[11] //rb + gb + bb + if(red_value > MODPAINT_MAX_SECTION_COLORS) + balloon_alert(usr, "total red too high! ([red_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)") + return + else if(red_value < MODPAINT_MIN_SECTION_COLORS) + balloon_alert(usr, "total red too low! ([red_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)") + return + if(green_value > MODPAINT_MAX_SECTION_COLORS) + balloon_alert(usr, "total green too high! ([green_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)") + return + else if(green_value < MODPAINT_MIN_SECTION_COLORS) + balloon_alert(usr, "total green too low! ([green_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)") + return + if(blue_value > MODPAINT_MAX_SECTION_COLORS) + balloon_alert(usr, "total blue too high! ([blue_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)") + return + else if(blue_value < MODPAINT_MIN_SECTION_COLORS) + balloon_alert(usr, "total blue too low! ([blue_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)") + return + for(var/color_value in total_colors) + total_color_value += color_value + if(color_value > MODPAINT_MAX_COLOR_VALUE) + balloon_alert(usr, "one of colors too high! ([color_value*100]%/[MODPAINT_MAX_COLOR_VALUE*100]%") + return + else if(color_value < MODPAINT_MIN_COLOR_VALUE) + balloon_alert(usr, "one of colors too low! ([color_value*100]%/[MODPAINT_MIN_COLOR_VALUE*100]%") + return + if(total_color_value > MODPAINT_MAX_OVERALL_COLORS) + balloon_alert(usr, "total colors too high! ([total_color_value*100]%/[MODPAINT_MAX_OVERALL_COLORS*100]%)") + return + else if(total_color_value < MODPAINT_MIN_OVERALL_COLORS) + balloon_alert(usr, "total colors too low! ([total_color_value*100]%/[MODPAINT_MIN_OVERALL_COLORS*100]%)") + return + editing_mod.set_mod_color(current_color) + SStgui.close_uis(src) + +/obj/item/mod/paint/proc/paint_skin(obj/item/mod/control/mod, mob/user) + if(length(mod.theme.skins) <= 1) + balloon_alert(user, "no alternate skins!") + return + var/list/skins = list() + for(var/mod_skin in mod.theme.skins) + skins[mod_skin] = image(icon = mod.icon, icon_state = "[mod_skin]-control") + var/pick = show_radial_menu(user, mod, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), mod, user), require_near = TRUE) + if(!pick) + balloon_alert(user, "no skin picked!") + return + mod.set_mod_skin(pick) + +/obj/item/mod/paint/proc/check_menu(obj/item/mod/control/mod, mob/user) + if(user.incapacitated() || !user.is_holding(src) || !mod || mod.active || mod.activating) + return FALSE + return TRUE + +#undef MODPAINT_MAX_COLOR_VALUE +#undef MODPAINT_MIN_COLOR_VALUE +#undef MODPAINT_MAX_SECTION_COLORS +#undef MODPAINT_MIN_SECTION_COLORS +#undef MODPAINT_MAX_OVERALL_COLORS +#undef MODPAINT_MIN_OVERALL_COLORS + +/obj/item/mod/skin_applier + name = "MOD skin applier" + desc = "This one-use skin applier will add a skin to MODsuits of a specific type." + icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' + icon_state = "skinapplier" + var/skin = "civilian" + var/compatible_theme = /datum/mod_theme + +/obj/item/mod/skin_applier/Initialize(mapload) + . = ..() + name = "MOD [skin] skin applier" + +/obj/item/mod/skin_applier/pre_attack(atom/attacked_atom, mob/living/user, params) + if(!istype(attacked_atom, /obj/item/mod/control)) + return ..() + var/obj/item/mod/control/mod = attacked_atom + if(mod.active || mod.activating) + balloon_alert(user, "suit is active!") + return TRUE + if(!istype(mod.theme, compatible_theme)) + balloon_alert(user, "incompatible theme!") + return TRUE + mod.set_mod_skin(skin) + balloon_alert(user, "skin applied") + qdel(src) + return TRUE diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm new file mode 100644 index 000000000000..2e6325df919f --- /dev/null +++ b/code/modules/mod/mod_theme.dm @@ -0,0 +1,1154 @@ +/// Global proc that sets up all MOD themes as singletons in a list and returns it. +/proc/setup_mod_themes() + . = list() + for(var/path in typesof(/datum/mod_theme)) + var/datum/mod_theme/new_theme = new path() + .[path] = new_theme + +/// MODsuit theme, instanced once and then used by MODsuits to grab various statistics. +/datum/mod_theme + /// Theme name for the MOD. + var/name = "standard" + /// Description added to the MOD. + var/desc = "A MOD suit. Placeholder Desc" + /// Extended description on examine_more + var/extended_desc = "Placeholder Desc" + /// Default skin of the MOD. + var/default_skin = "standard" + /// The slot this mod theme fits on + var/slot_flags = ITEM_SLOT_BACK + /// Armor shared across the MOD parts. + var/armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 100, "fire" = 25, "acid" = 25) + /// Resistance flags shared across the MOD parts. + var/resistance_flags = NONE + /// Atom flags shared across the MOD parts. + var/atom_flags = NONE + /// Max heat protection shared across the MOD parts. + var/max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + /// Max cold protection shared across the MOD parts. + var/min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + /// Siemens shared across the MOD parts. + var/siemens_coefficient = 0.5 + /// How much modules can the MOD carry without malfunctioning. + var/complexity_max = DEFAULT_MAX_COMPLEXITY + /// How much battery power the MOD uses by just being on + var/charge_drain = DEFAULT_CHARGE_DRAIN + /// Slowdown of the MOD when not active. + var/slowdown_inactive = 1.25 + /// Slowdown of the MOD when active. + var/slowdown_active = 0.75 + /// Theme used by the MOD TGUI. + var/ui_theme = "ntos" + /// List of inbuilt modules. These are different from the pre-equipped suits, you should mainly use these for unremovable modules with 0 complexity. + var/list/inbuilt_modules = list() + /// Modules blacklisted from the MOD. + var/list/module_blacklist = list() + /// Allowed items in the chestplate's suit storage. + var/list/allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + ) + /// List of skins with their appropriate clothing flags. + var/list/skins = list( + "standard" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + "civilian" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/engineering + name = "engineering" + default_skin = "engineering" + armor = list("melee" = 10, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 10, "bio" = 100, "fire" = 100, "acid" = 25) + resistance_flags = FIRE_PROOF + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + siemens_coefficient = 0 + slowdown_inactive = 1.5 + slowdown_active = 1 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/construction/rcd, + /obj/item/storage/bag/construction, + ) + skins = list( + "engineering" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/atmospheric + name = "atmospheric" + default_skin = "atmospheric" + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 15, "bomb" = 10, "bio" = 100, "fire" = 100, "acid" = 75) + resistance_flags = FIRE_PROOF + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + slowdown_inactive = 1.5 + slowdown_active = 1 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/analyzer, + /obj/item/t_scanner, + /obj/item/pipe_dispenser, + ) + skins = list( + "atmospheric" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR, + UNSEALED_COVER = HEADCOVERSMOUTH, + SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/advanced + name = "advanced" + default_skin = "advanced" + armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 15, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 90) + resistance_flags = FIRE_PROOF + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + siemens_coefficient = 0 + slowdown_inactive = 1 + slowdown_active = 0.5 + inbuilt_modules = list(/obj/item/mod/module/magboot/advanced) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/analyzer, + /obj/item/t_scanner, + /obj/item/pipe_dispenser, + /obj/item/construction/rcd, + /obj/item/storage/bag/construction, + /obj/item/melee/classic_baton/telescopic, + ) + skins = list( + "advanced" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/mining + name = "mining" + default_skin = "mining" + armor = list("melee" = 15, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 30, "bio" = 100, "fire" = 100, "acid" = 75) + resistance_flags = FIRE_PROOF|LAVA_PROOF + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + complexity_max = DEFAULT_MAX_COMPLEXITY - 5 + charge_drain = DEFAULT_CHARGE_DRAIN * 2 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/resonator, + /obj/item/mining_scanner, + /obj/item/t_scanner/adv_mining_scanner, + /obj/item/pickaxe, + /obj/item/kinetic_crusher, + /obj/item/stack/ore/plasma, + /obj/item/storage/bag/ore, + ) + inbuilt_modules = list() + skins = list( + "mining" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + "asteroid" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/loader + name = "loader" + default_skin = "loader" + armor = list("melee" = 15, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 10, "bio" = 10, "fire" = 25, "acid" = 25) + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + siemens_coefficient = 0.25 + complexity_max = DEFAULT_MAX_COMPLEXITY - 5 + slowdown_inactive = 0.5 + slowdown_active = 0 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/paper + ) + inbuilt_modules = list(/obj/item/mod/module/clamp/loader, /obj/item/mod/module/magnet) + skins = list( + "loader" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, + SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + ), + GAUNTLETS_FLAGS = list( + SEALED_CLOTHING = THICKMATERIAL, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + SEALED_CLOTHING = THICKMATERIAL, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/medical + name = "medical" + default_skin = "medical" + armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 10, "bio" = 100, "fire" = 60, "acid" = 75) + charge_drain = DEFAULT_CHARGE_DRAIN * 1.5 + slowdown_inactive = 1 + slowdown_active = 0.5 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/healthanalyzer, + /obj/item/reagent_containers/dropper, + /obj/item/reagent_containers/glass/beaker, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/hypospray, + /obj/item/reagent_containers/pill, + /obj/item/reagent_containers/syringe, + /obj/item/stack/medical, + /obj/item/sensor_device, + /obj/item/storage/pill_bottle, + /obj/item/storage/bag/chemistry, + /obj/item/storage/bag/bio, + ) + skins = list( + "medical" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + "corpsman" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/rescue + name = "rescue" + default_skin = "rescue" + armor = list("melee" = 10, "bullet" = 10, "laser" = 5, "energy" = 5, "bomb" = 10, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + charge_drain = DEFAULT_CHARGE_DRAIN * 1.5 + slowdown_inactive = 0.75 + slowdown_active = 0.25 + inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/healthanalyzer, + /obj/item/reagent_containers/dropper, + /obj/item/reagent_containers/glass/beaker, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/hypospray, + /obj/item/reagent_containers/pill, + /obj/item/reagent_containers/syringe, + /obj/item/stack/medical, + /obj/item/sensor_device, + /obj/item/storage/pill_bottle, + /obj/item/storage/bag/chemistry, + /obj/item/storage/bag/bio, + /obj/item/melee/classic_baton/telescopic, + ) + skins = list( + "rescue" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/research + name = "research" + default_skin = "research" + armor = list("melee" = 20, "bullet" = 15, "laser" = 5, "energy" = 5, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + complexity_max = DEFAULT_MAX_COMPLEXITY + 5 + slowdown_inactive = 1.75 + slowdown_active = 1.25 + inbuilt_modules = list(/obj/item/mod/module/reagent_scanner/advanced) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/analyzer, + /obj/item/dnainjector, + /obj/item/storage/bag/bio, + /obj/item/melee/classic_baton/telescopic, + ) + skins = list( + "research" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/security + name = "security" + default_skin = "security" + armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 25, "bio" = 100, "fire" = 75, "acid" = 75) + complexity_max = DEFAULT_MAX_COMPLEXITY - 3 + slowdown_inactive = 1 + slowdown_active = 0.5 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/reagent_containers/spray/pepper, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + ) + skins = list( + "security" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, + UNSEALED_COVER = HEADCOVERSMOUTH, + SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/safeguard + name = "safeguard" + default_skin = "safeguard" + armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 40, "bio" = 100, "fire" = 100, "acid" = 95) + resistance_flags = FIRE_PROOF + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + slowdown_inactive = 0.75 + slowdown_active = 0.25 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/reagent_containers/spray/pepper, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + ) + skins = list( + "safeguard" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/magnate + name = "magnate" + default_skin = "magnate" + armor = list("melee" = 20, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + siemens_coefficient = 0 + complexity_max = DEFAULT_MAX_COMPLEXITY + 5 + slowdown_inactive = 0.75 + slowdown_active = 0.25 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + ) + skins = list( + "magnate" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/syndicate + name = "syndicate" + default_skin = "syndicate" + armor = list("melee" = 15, "bullet" = 20, "laser" = 15, "energy" = 15, "bomb" = 35, "bio" = 100, "fire" = 50, "acid" = 90) + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + siemens_coefficient = 0 + slowdown_inactive = 1 + slowdown_active = 0.5 + ui_theme = "syndicate" + inbuilt_modules = list(/obj/item/mod/module/armor_booster) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/energy, + ) + skins = list( + "syndicate" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/elite + name = "elite" + default_skin = "elite" + armor = list("melee" = 35, "bullet" = 30, "laser" = 35, "energy" = 35, "bomb" = 55, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + siemens_coefficient = 0 + slowdown_inactive = 1 + slowdown_active = 0.5 + ui_theme = "syndicate" + inbuilt_modules = list(/obj/item/mod/module/armor_booster) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/energy, + ) + skins = list( + "elite" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/ninja + name = "ninja" + default_skin = "ninja" + armor = list("melee" = 40, "bullet" = 30, "laser" = 20, "energy" = 30, "bomb" = 30, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = LAVA_PROOF|FIRE_PROOF|ACID_PROOF + charge_drain = DEFAULT_CHARGE_DRAIN * 0.5 + siemens_coefficient = 0 + slowdown_inactive = 0.5 + slowdown_active = 0 + ui_theme = "hackerman" + inbuilt_modules = list(/obj/item/mod/module/welding/camera_vision, /obj/item/mod/module/hacker, /obj/item/mod/module/weapon_recall, /obj/item/mod/module/adrenaline_boost, /obj/item/mod/module/energy_net) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/gun, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/melee/baton, + /obj/item/restraints/handcuffs, + ) + skins = list( + "ninja" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, + SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/prototype + name = "prototype" + default_skin = "prototype" + armor = list("melee" = 20, "bullet" = 5, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 75) + resistance_flags = FIRE_PROOF + siemens_coefficient = 0 + complexity_max = DEFAULT_MAX_COMPLEXITY + 5 + charge_drain = DEFAULT_CHARGE_DRAIN * 2 + slowdown_inactive = 2 + slowdown_active = 1.5 + ui_theme = "hackerman" + //inbuilt_modules = list(/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype) + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/analyzer, + /obj/item/t_scanner, + /obj/item/pipe_dispenser, + /obj/item/construction/rcd, + ) + skins = list( + "prototype" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/responsory + name = "responsory" + default_skin = "responsory" + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 90) + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + resistance_flags = FIRE_PROOF + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + siemens_coefficient = 0 + slowdown_inactive = 0.5 + slowdown_active = 0 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + ) + skins = list( + "responsory" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + "inquisitory" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/apocryphal + name = "apocryphal" + default_skin = "apocryphal" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 60, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + siemens_coefficient = 0 + complexity_max = DEFAULT_MAX_COMPLEXITY + 10 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/energy, + ) + skins = list( + "apocryphal" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, + SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/corporate + name = "corporate" + default_skin = "corporate" + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + siemens_coefficient = 0 + slowdown_inactive = 0.5 + slowdown_active = 0 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + /obj/item/melee/baton, + ) + skins = list( + "corporate" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/chrono + name = "chrono" + default_skin = "chrono" + armor = list("melee" = 60, "bullet" = 60, "laser" = 60, "energy" = 60, "bomb" = 30, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + complexity_max = DEFAULT_MAX_COMPLEXITY - 10 + slowdown_inactive = 0 + slowdown_active = 0 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/restraints/handcuffs, + ) + skins = list( + "chrono" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = NECK_LAYER, + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/debug + name = "debug" + default_skin = "debug" + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + complexity_max = 50 + siemens_coefficient = 0 + slowdown_inactive = 0.5 + slowdown_active = 0 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/gun, + ) + skins = list( + "debug" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE, + UNSEALED_COVER = HEADCOVERSMOUTH, + SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) + +/datum/mod_theme/administrative + name = "administrative" + default_skin = "debug" + armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE|LAVA_PROOF|FIRE_PROOF|UNACIDABLE|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + complexity_max = 1000 + charge_drain = DEFAULT_CHARGE_DRAIN * 0 + siemens_coefficient = 0 + slowdown_inactive = 0 + slowdown_active = 0 + allowed_suit_storage = list( + /obj/item/flashlight, + /obj/item/tank/internals, + /obj/item/gun, + ) + skins = list( + "debug" = list( + HELMET_FLAGS = list( + UNSEALED_LAYER = null, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + ), + CHESTPLATE_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCKS_SHOVE_KNOCKDOWN, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + ), + GAUNTLETS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + BOOTS_FLAGS = list( + UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + ), + ), + ) diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm new file mode 100644 index 000000000000..6cd33197e423 --- /dev/null +++ b/code/modules/mod/mod_types.dm @@ -0,0 +1,331 @@ +/obj/item/mod/control/pre_equipped + /// The skin we apply to the suit, defaults to the default_skin of the theme. + var/applied_skin + /// The MOD core we apply to the suit. + var/applied_core = /obj/item/mod/core/standard + /// The cell we apply to the core. Only applies to standard core suits. + var/applied_cell = /obj/item/stock_parts/cell/high + +/obj/item/mod/control/pre_equipped/Initialize(mapload, new_theme, new_skin, new_core) + new_skin = applied_skin + new_core = new applied_core(src) + if(istype(new_core, /obj/item/mod/core/standard)) + var/obj/item/mod/core/standard/cell_core = new_core + cell_core.cell = new applied_cell() + return ..() + +/obj/item/mod/control/pre_equipped/standard + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + ) + +/obj/item/mod/control/pre_equipped/engineering + theme = /datum/mod_theme/engineering + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/magboot, + ) + +/obj/item/mod/control/pre_equipped/atmospheric + theme = /datum/mod_theme/atmospheric + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/t_ray, + ) + +/obj/item/mod/control/pre_equipped/advanced + theme = /datum/mod_theme/advanced + applied_cell = /obj/item/stock_parts/cell/super + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/jetpack, + ) + +/obj/item/mod/control/pre_equipped/loader + theme = /datum/mod_theme/loader + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/paper_dispenser, + /obj/item/mod/module/stamp, + ) + +/obj/item/mod/control/pre_equipped/mining + theme = /datum/mod_theme/mining + applied_core = /obj/item/mod/core/plasma + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/gps, + /obj/item/mod/module/orebag, + /obj/item/mod/module/clamp, + /obj/item/mod/module/drill, + ) + +/obj/item/mod/control/pre_equipped/medical + theme = /datum/mod_theme/medical + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/health_analyzer, + /obj/item/mod/module/quick_carry, + ) + +/obj/item/mod/control/pre_equipped/rescue + theme = /datum/mod_theme/rescue + applied_cell = /obj/item/stock_parts/cell/super + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/health_analyzer, + /obj/item/mod/module/injector, + ) + +/obj/item/mod/control/pre_equipped/research + theme = /datum/mod_theme/research + applied_cell = /obj/item/stock_parts/cell/super + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + //obj/item/mod/module/circuit, + /obj/item/mod/module/t_ray, + ) + +/obj/item/mod/control/pre_equipped/security + theme = /datum/mod_theme/security + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/flashlight, + ) + +/obj/item/mod/control/pre_equipped/safeguard + theme = /datum/mod_theme/safeguard + applied_cell = /obj/item/stock_parts/cell/super + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/jetpack, + /obj/item/mod/module/megaphone, + ) + +/obj/item/mod/control/pre_equipped/magnate + theme = /datum/mod_theme/magnate + applied_cell = /obj/item/stock_parts/cell/hyper + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/hat_stabilizer, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/jetpack/advanced, + ) + +/obj/item/mod/control/pre_equipped/traitor + theme = /datum/mod_theme/syndicate + applied_cell = /obj/item/stock_parts/cell/super + initial_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/flashlight, + /obj/item/mod/module/dna_lock, + ) + +/obj/item/mod/control/pre_equipped/traitor_elite + theme = /datum/mod_theme/elite + applied_cell = /obj/item/stock_parts/cell/bluespace + initial_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/advanced, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/dna_lock, + ) + +/obj/item/mod/control/pre_equipped/nuclear + theme = /datum/mod_theme/syndicate + applied_cell = /obj/item/stock_parts/cell/hyper + req_access = list(ACCESS_SYNDICATE) + initial_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/advanced, + /obj/item/mod/module/flashlight, + ) + +/obj/item/mod/control/pre_equipped/elite + theme = /datum/mod_theme/elite + applied_cell = /obj/item/stock_parts/cell/bluespace + req_access = list(ACCESS_SYNDICATE) + initial_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/advanced, + /obj/item/mod/module/flashlight, + ) + +/obj/item/mod/control/pre_equipped/elite/flamethrower + initial_modules = list( + /obj/item/mod/module/storage/syndicate, + /obj/item/mod/module/emp_shield, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/thermal_regulator, + /obj/item/mod/module/jetpack/advanced, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/flamethrower, + ) + +/obj/item/mod/control/pre_equipped/ninja + theme = /datum/mod_theme/ninja + applied_cell = /obj/item/stock_parts/cell/super + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/noslip, + /obj/item/mod/module/status_readout, + /obj/item/mod/module/stealth/ninja, + /obj/item/mod/module/dispenser/ninja, + /obj/item/mod/module/dna_lock/reinforced, + /obj/item/mod/module/emp_shield/pulse, + ) + +/obj/item/mod/control/pre_equipped/prototype + theme = /datum/mod_theme/prototype + req_access = list(ACCESS_AWAY_GENERAL) + initial_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/tether, + ) + +/obj/item/mod/control/pre_equipped/responsory + theme = /datum/mod_theme/responsory + applied_cell = /obj/item/stock_parts/cell/hyper + req_access = list(ACCESS_CENT_GENERAL) + initial_modules = list( + /obj/item/mod/module/storage/large_capacity, + /obj/item/mod/module/welding, + /obj/item/mod/module/emp_shield, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/flashlight, + ) + /// The insignia type, insignias show what sort of member of the ERT you're dealing with. + var/insignia_type = /obj/item/mod/module/insignia + /// Additional module we add, as a treat. + var/additional_module = /obj/item/mod/module + +/obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin, new_core) + initial_modules.Insert(1, insignia_type) + initial_modules.Add(additional_module) + return ..() + +/obj/item/mod/control/pre_equipped/responsory/commander + insignia_type = /obj/item/mod/module/insignia/commander + additional_module = /obj/item/mod/module/power_kick + +/obj/item/mod/control/pre_equipped/responsory/security + insignia_type = /obj/item/mod/module/insignia/security + additional_module = /obj/item/mod/module/megaphone + +/obj/item/mod/control/pre_equipped/responsory/engineer + insignia_type = /obj/item/mod/module/insignia/engineer + additional_module = /obj/item/mod/module/magboot + +/obj/item/mod/control/pre_equipped/responsory/medic + insignia_type = /obj/item/mod/module/insignia/medic + additional_module = /obj/item/mod/module/quick_carry + +/obj/item/mod/control/pre_equipped/responsory/janitor + insignia_type = /obj/item/mod/module/insignia/janitor + additional_module = /obj/item/mod/module/clamp + +/obj/item/mod/control/pre_equipped/responsory/chaplain + insignia_type = /obj/item/mod/module/insignia/chaplain + additional_module = /obj/item/mod/module/injector + +/obj/item/mod/control/pre_equipped/apocryphal + theme = /datum/mod_theme/apocryphal + applied_cell = /obj/item/stock_parts/cell/bluespace + req_access = list(ACCESS_CENT_SPECOPS) + initial_modules = list( + /obj/item/mod/module/storage/bluespace, + /obj/item/mod/module/welding, + /obj/item/mod/module/emp_shield/advanced, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/jetpack, + ) + +/obj/item/mod/control/pre_equipped/corporate + theme = /datum/mod_theme/corporate + applied_core = /obj/item/mod/core/infinite + req_access = list(ACCESS_CENT_SPECOPS) + initial_modules = list( + /obj/item/mod/module/storage/bluespace, + /obj/item/mod/module/hat_stabilizer, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/emp_shield/advanced, + ) + +/*obj/item/mod/control/pre_equipped/chrono + theme = /datum/mod_theme/chrono + applied_core = /obj/item/mod/core/infinite + initial_modules = list( + /obj/item/mod/module/eradication_lock, + /obj/item/mod/module/emp_shield, + /obj/item/mod/module/timeline_jumper, + /obj/item/mod/module/timestopper, + /obj/item/mod/module/rewinder, + /obj/item/mod/module/tem, + /obj/item/mod/module/anomaly_locked/kinesis/plus, + )*/ + +/obj/item/mod/control/pre_equipped/debug + theme = /datum/mod_theme/debug + applied_core = /obj/item/mod/core/infinite + initial_modules = list( + /obj/item/mod/module/storage/bluespace, + /obj/item/mod/module/welding, + /obj/item/mod/module/flashlight, + /obj/item/mod/module/tether, + /obj/item/mod/module/injector, + ) + +/obj/item/mod/control/pre_equipped/administrative + theme = /datum/mod_theme/administrative + applied_core = /obj/item/mod/core/infinite + initial_modules = list( + /obj/item/mod/module/storage/bluespace, + /obj/item/mod/module/emp_shield/advanced, + /obj/item/mod/module/welding, + /obj/item/mod/module/stealth/ninja, + /obj/item/mod/module/quick_carry/advanced, + /obj/item/mod/module/magboot/advanced, + /obj/item/mod/module/jetpack/advanced, + //obj/item/mod/module/anomaly_locked/kinesis/plus, + ) + +//these exist for the prefs menu +/obj/item/mod/control/pre_equipped/empty + +/obj/item/mod/control/pre_equipped/empty/syndicate + theme = /datum/mod_theme/syndicate + +/obj/item/mod/control/pre_equipped/empty/elite + theme = /datum/mod_theme/elite + +/obj/item/mod/control/pre_equipped/empty/ninja + theme = /datum/mod_theme/ninja + +INITIALIZE_IMMEDIATE(/obj/item/mod/control/pre_equipped/empty) diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm new file mode 100644 index 000000000000..3bfa930dea7e --- /dev/null +++ b/code/modules/mod/mod_ui.dm @@ -0,0 +1,86 @@ +/obj/item/mod/control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MODsuit", name) + ui.open() + +/obj/item/mod/control/ui_data(mob/user) + var/data = list() + data["interface_break"] = interface_break + data["malfunctioning"] = malfunctioning + data["open"] = open + data["active"] = active + data["locked"] = locked + data["complexity"] = complexity + data["selected_module"] = selected_module?.name + data["wearer_name"] = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "No Occupant" + data["wearer_job"] = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "No Job" + //data[JOB_AI] = ai?.name + data["core"] = core?.name + data["charge"] = get_charge_percent() + data["modules"] = list() + for(var/obj/item/mod/module/module as anything in modules) + var/list/module_data = list( + "module_name" = module.name, + "description" = module.desc, + "module_type" = module.module_type, + "module_active" = module.active, + "pinned" = module.pinned_to[user], + "idle_power" = module.idle_power_cost, + "active_power" = module.active_power_cost, + "use_power" = module.use_power_cost, + "module_complexity" = module.complexity, + "cooldown_time" = module.cooldown_time, + "cooldown" = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS), + "id" = module.tgui_id, + "ref" = REF(module), + "configuration_data" = module.get_configuration() + ) + module_data += module.add_ui_data() + data["modules"] += list(module_data) + return data + +/obj/item/mod/control/ui_static_data(mob/user) + var/data = list() + data["ui_theme"] = ui_theme + data["control"] = name + data["complexity_max"] = complexity_max + data["helmet"] = helmet?.name + data["chestplate"] = chestplate?.name + data["gauntlets"] = gauntlets?.name + data["boots"] = boots?.name + return data + +/obj/item/mod/control/ui_act(action, params) + . = ..() + if(.) + return + if(locked && !allowed(usr)) + balloon_alert(usr, "insufficient access!") + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return + if(malfunctioning && prob(75)) + balloon_alert(usr, "button malfunctions!") + return + switch(action) + if("lock") + locked = !locked + balloon_alert(usr, "[locked ? "locked" : "unlocked"]!") + if("activate") + toggle_activate(usr) + if("select") + var/obj/item/mod/module/module = locate(params["ref"]) in modules + if(!module) + return + module.on_select() + if("configure") + var/obj/item/mod/module/module = locate(params["ref"]) in modules + if(!module) + return + module.configure_edit(params["key"], params["value"]) + if("pin") + var/obj/item/mod/module/module = locate(params["ref"]) in modules + if(!module) + return + module.pin(usr) + return TRUE diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm new file mode 100644 index 000000000000..7264066e5d1a --- /dev/null +++ b/code/modules/mod/modules/_module.dm @@ -0,0 +1,399 @@ +///MOD Module - A special device installed in a MODsuit allowing the suit to do new stuff. +/obj/item/mod/module + name = "MOD module" + icon = 'icons/obj/clothing/modsuit/mod_modules.dmi' + icon_state = "module" + /// If it can be removed + var/removable = TRUE + /// If it's passive, togglable, usable or active + var/module_type = MODULE_PASSIVE + /// Is the module active + var/active = FALSE + /// How much space it takes up in the MOD + var/complexity = 0 + /// Power use when idle + var/idle_power_cost = DEFAULT_CHARGE_DRAIN * 0 + /// Power use when active + var/active_power_cost = DEFAULT_CHARGE_DRAIN * 0 + /// Power use when used, we call it manually + var/use_power_cost = DEFAULT_CHARGE_DRAIN * 0 + /// ID used by their TGUI + var/tgui_id + /// Linked MODsuit + var/obj/item/mod/control/mod + /// If we're an active module, what item are we? + var/obj/item/device + /// Overlay given to the user when the module is inactive + var/overlay_state_inactive + /// Overlay given to the user when the module is active + var/overlay_state_active + /// Overlay given to the user when the module is used, lasts until cooldown finishes + var/overlay_state_use + /// Icon file for the overlay. + var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi' + /// Does the overlay use the control unit's colors? + var/use_mod_colors = FALSE + /// What modules are we incompatible with? + var/list/incompatible_modules = list() + /// Cooldown after use + var/cooldown_time = 0 + /// The mouse button needed to use this module + var/used_signal + /// List of REF()s mobs we are pinned to, linked with their action buttons + var/list/pinned_to = list() + /// If we're allowed to use this module while phased out. + var/allowed_in_phaseout = FALSE + /// If we're allowed to use this module while the suit is disabled. + var/allowed_inactive = FALSE + /// Timer for the cooldown + COOLDOWN_DECLARE(cooldown_timer) + +/obj/item/mod/module/Initialize(mapload) + . = ..() + if(module_type != MODULE_ACTIVE) + return + if(ispath(device)) + device = new device(src) + ADD_TRAIT(device, TRAIT_NODROP, MOD_TRAIT) + RegisterSignal(device, COMSIG_PARENT_QDELETING, PROC_REF(on_device_deletion)) + RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit)) + +/obj/item/mod/module/Destroy() + mod?.uninstall(src) + if(device) + UnregisterSignal(device, COMSIG_PARENT_QDELETING) + QDEL_NULL(device) + return ..() + +/obj/item/mod/module/examine(mob/user) + . = ..() + if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + . += span_notice("Complexity level: [complexity]") + + +/// Called when the module is selected from the TGUI, radial or the action button +/obj/item/mod/module/proc/on_select() + if(((!mod.active || mod.activating) && !allowed_inactive) || module_type == MODULE_PASSIVE) + if(mod.wearer) + balloon_alert(mod.wearer, "not active!") + return + if(module_type != MODULE_USABLE) + if(active) + on_deactivation() + else + on_activation() + else + on_use() + SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src) + +/// Called when the module is activated +/obj/item/mod/module/proc/on_activation() + if(!COOLDOWN_FINISHED(src, cooldown_timer)) + balloon_alert(mod.wearer, "on cooldown!") + return FALSE + if(!mod.active || mod.activating || !mod.get_charge()) + balloon_alert(mod.wearer, "unpowered!") + return FALSE + if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob)) + //specifically a to_chat because the user is phased out. + to_chat(mod.wearer, span_warning("You cannot activate this right now.")) + return FALSE + if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE) + return FALSE + if(module_type == MODULE_ACTIVE) + if(mod.selected_module && !mod.selected_module.on_deactivation(display_message = FALSE)) + return FALSE + mod.selected_module = src + if(device) + if(mod.wearer.put_in_hands(device)) + balloon_alert(mod.wearer, "[device] extended") + RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit)) + RegisterSignal(mod.wearer, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey)) + else + balloon_alert(mod.wearer, "can't extend [device]!") + mod.wearer.transferItemToLoc(device, src, force = TRUE) + return FALSE + else + var/used_button = MIDDLE_CLICK + update_signal(used_button) + balloon_alert(mod.wearer, "[src] activated, [used_button]-click to use") + active = TRUE + COOLDOWN_START(src, cooldown_timer, cooldown_time) + mod.wearer.update_inv_back(mod.slot_flags) + SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED) + return TRUE + +/// Called when the module is deactivated +/obj/item/mod/module/proc/on_deactivation(display_message = TRUE, deleting = FALSE) + active = FALSE + if(module_type == MODULE_ACTIVE) + mod.selected_module = null + if(display_message) + balloon_alert(mod.wearer, device ? "[device] retracted" : "[src] deactivated") + if(device) + mod.wearer.transferItemToLoc(device, src, force = TRUE) + UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED) + UnregisterSignal(mod.wearer, COMSIG_KB_MOB_DROPITEM_DOWN) + else + UnregisterSignal(mod.wearer, used_signal) + used_signal = null + mod.wearer.update_inv_back(mod.slot_flags) + SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED) + return TRUE + +/// Called when the module is used +/obj/item/mod/module/proc/on_use() + if(!COOLDOWN_FINISHED(src, cooldown_timer)) + balloon_alert(mod.wearer, "on cooldown!") + return FALSE + if(!check_power(use_power_cost)) + balloon_alert(mod.wearer, "not enough charge!") + return FALSE + if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob)) + //specifically a to_chat because the user is phased out. + to_chat(mod.wearer, span_warning("You cannot activate this right now.")) + return FALSE + if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE) + return FALSE + COOLDOWN_START(src, cooldown_timer, cooldown_time) + addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_inv_back), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts + mod.wearer.update_inv_back(mod.slot_flags) + SEND_SIGNAL(src, COMSIG_MODULE_USED) + return TRUE + +/// Called when an activated module without a device is used +/obj/item/mod/module/proc/on_select_use(atom/target) + if(mod.wearer.incapacitated(IGNORE_GRAB)) + return FALSE + mod.wearer.face_atom(target) + if(!on_use()) + return FALSE + return TRUE + +/// Called when an activated module without a device is active and the user alt/middle-clicks +/obj/item/mod/module/proc/on_special_click(mob/source, atom/target) + SIGNAL_HANDLER + on_select_use(target) + return COMSIG_MOB_CANCEL_CLICKON + +/// Called on the MODsuit's process +/obj/item/mod/module/proc/on_process(delta_time) + if(active) + if(!drain_power(active_power_cost * delta_time)) + on_deactivation() + return FALSE + on_active_process(delta_time) + else + drain_power(idle_power_cost * delta_time) + return TRUE + +/// Called on the MODsuit's process if it is an active module +/obj/item/mod/module/proc/on_active_process(delta_time) + return + +/// Called from MODsuit's install() proc, so when the module is installed. +/obj/item/mod/module/proc/on_install() + return + +/// Called from MODsuit's uninstall() proc, so when the module is uninstalled. +/obj/item/mod/module/proc/on_uninstall(deleting = FALSE) + return + +/// Called when the MODsuit is activated +/obj/item/mod/module/proc/on_suit_activation() + return + +/// Called when the MODsuit is deactivated +/obj/item/mod/module/proc/on_suit_deactivation(deleting = FALSE) + return + +/// Called when the MODsuit is equipped +/obj/item/mod/module/proc/on_equip() + return + +/// Called when the MODsuit is unequipped +/obj/item/mod/module/proc/on_unequip() + return + +/// Drains power from the suit charge +/obj/item/mod/module/proc/drain_power(amount) + 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 +/obj/item/mod/module/proc/check_power(amount) + return mod.check_charge(amount) + +/// Adds additional things to the MODsuit ui_data() +/obj/item/mod/module/proc/add_ui_data() + return list() + +/// Creates a list of configuring options for this module +/obj/item/mod/module/proc/get_configuration() + return list() + +/// Generates an element of the get_configuration list with a display name, type and value +/obj/item/mod/module/proc/add_ui_configuration(display_name, type, value, list/values) + return list("display_name" = display_name, "type" = type, "value" = value, "values" = values) + +/// Receives configure edits from the TGUI and edits the vars +/obj/item/mod/module/proc/configure_edit(key, value) + return + +/// Called when the device moves to a different place on active modules +/obj/item/mod/module/proc/on_exit(datum/source, atom/movable/part, direction) + SIGNAL_HANDLER + + if(!active) + return + if(part.loc == src) + return + if(part.loc == mod.wearer) + return + if(part == device) + on_deactivation(display_message = FALSE) + +/// Called when the device gets deleted on active modules +/obj/item/mod/module/proc/on_device_deletion(datum/source) + SIGNAL_HANDLER + + if(source == device) + device = null + qdel(src) + +/// Generates an icon to be used for the suit's worn overlays +/obj/item/mod/module/proc/generate_worn_overlay(mod_layer) + . = list() + if(!mod.active) + return + var/used_overlay + if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer)) + used_overlay = overlay_state_use + else if(overlay_state_active && active) + used_overlay = overlay_state_active + else if(overlay_state_inactive) + used_overlay = overlay_state_inactive + else + return + var/mutable_appearance/module_icon = mutable_appearance(overlay_icon_file, used_overlay, layer = mod_layer + 0.1) + if(!use_mod_colors) + module_icon.appearance_flags |= RESET_COLOR + . += module_icon + +/// Updates the signal used by active modules to be activated +/obj/item/mod/module/proc/update_signal(value) + switch(value) + if(MIDDLE_CLICK) + mod.selected_module.used_signal = COMSIG_MOB_MIDDLECLICKON + if(ALT_CLICK) + mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON + RegisterSignal(mod.wearer, mod.selected_module.used_signal, TYPE_PROC_REF(/obj/item/mod/module, on_special_click)) + +/// Pins the module to the user's action buttons +/obj/item/mod/module/proc/pin(mob/user) + var/datum/action/item_action/mod/pinned_module/existing_action = pinned_to[REF(user)] + if(existing_action) + mod.remove_item_action(existing_action) + return + + var/datum/action/item_action/mod/pinned_module/new_action = new(mod, src, user) + mod.add_item_action(new_action) + +/// On drop key, concels a device item. +/obj/item/mod/module/proc/dropkey(mob/living/user) + SIGNAL_HANDLER + + if(user.get_active_held_item() != device) + return + on_deactivation() + return COMSIG_KB_ACTIVATED + +///Anomaly Locked - Causes the module to not function without an anomaly. +/obj/item/mod/module/anomaly_locked + name = "MOD anomaly locked module" + desc = "A form of a module, locked behind an anomalous core to function." + incompatible_modules = list(/obj/item/mod/module/anomaly_locked) + /// The core item the module runs off. + var/obj/item/assembly/signaler/anomaly/core + /// Accepted types of anomaly cores. + var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly) + /// If this one starts with a core in. + var/prebuilt = FALSE + +/obj/item/mod/module/anomaly_locked/Initialize(mapload) + . = ..() + if(!prebuilt || !length(accepted_anomalies)) + return + var/core_path = pick(accepted_anomalies) + core = new core_path(src) + update_icon_state() + +/obj/item/mod/module/anomaly_locked/Destroy() + QDEL_NULL(core) + return ..() + +/obj/item/mod/module/anomaly_locked/examine(mob/user) + . = ..() + if(!length(accepted_anomalies)) + return + if(core) + . += span_notice("There is a [core.name] installed in it. You could remove it with a screwdriver...") + else + var/list/core_list = list() + for(var/path in accepted_anomalies) + var/atom/core_path = path + core_list += initial(core_path.name) + . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.") + +/obj/item/mod/module/anomaly_locked/on_select() + if(!core) + balloon_alert(mod.wearer, "no core!") + return + return ..() + +/obj/item/mod/module/anomaly_locked/on_process(delta_time) + . = ..() + if(!core) + return FALSE + +/obj/item/mod/module/anomaly_locked/on_active_process(delta_time) + if(!core) + return FALSE + return TRUE + +/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params) + if(item.type in accepted_anomalies) + if(core) + balloon_alert(user, "core already in!") + return + if(!user.transferItemToLoc(item, src)) + return + core = item + balloon_alert(user, "core installed") + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + update_icon_state() + else + return ..() + +/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(!core) + balloon_alert(user, "no core!") + return + balloon_alert(user, "removing core...") + if(!do_after(user, 3 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return + balloon_alert(user, "core removed") + core.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(core) + core = null + update_icon_state() + +/obj/item/mod/module/anomaly_locked/update_icon_state() + icon_state = initial(icon_state) + (core ? "-core" : "") + return ..() diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm new file mode 100644 index 000000000000..33edd75e173f --- /dev/null +++ b/code/modules/mod/modules/modules_antag.dm @@ -0,0 +1,398 @@ +//Antag modules for MODsuits + +///Armor Booster - Grants your suit more armor and speed in exchange for EVA protection. Also acts as a welding screen. +/obj/item/mod/module/armor_booster + name = "MOD armor booster module" + desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \ + giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \ + However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \ + so this extra armor provides zero ability for extravehicular activity while deployed." + icon_state = "armor_booster" + module_type = MODULE_TOGGLE + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + removable = TRUE + incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/welding) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_armorbooster_off" + overlay_state_active = "module_armorbooster_on" + use_mod_colors = TRUE + /// Whether or not this module removes pressure protection. + var/remove_pressure_protection = TRUE + /// Speed added to the control unit. + var/speed_added = 0.5 + /// Speed that we actually added. + var/actual_speed_added = 0 + /// Armor values added to the suit parts. + var/list/armor_values = list("melee" = 25, "bullet" = 30, "laser" = 15, "energy" = 15) + /// List of parts of the suit that are spaceproofed, for giving them back the pressure protection. + var/list/spaceproofed = list() + +/obj/item/mod/module/armor_booster/on_suit_activation() + mod.helmet.flash_protect = FLASH_PROTECTION_WELDER + +/obj/item/mod/module/armor_booster/on_suit_deactivation(deleting = FALSE) + if(deleting) + return + mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + +/obj/item/mod/module/armor_booster/on_activation() + . = ..() + if(!.) + return + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + actual_speed_added = max(0, min(mod.slowdown_active, speed_added)) + mod.slowdown -= actual_speed_added + mod.wearer.update_equipment_speed_mods() + var/list/parts = mod.mod_parts + mod + for(var/obj/item/part as anything in parts) + part.armor = part.armor.modifyRating(arglist(armor_values)) + if(!remove_pressure_protection || !isclothing(part)) + continue + var/obj/item/clothing/clothing_part = part + if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE) + clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE + spaceproofed[clothing_part] = TRUE + +/obj/item/mod/module/armor_booster/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + if(!deleting) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + mod.slowdown += actual_speed_added + mod.wearer.update_equipment_speed_mods() + var/list/parts = mod.mod_parts + mod + var/list/removed_armor = armor_values.Copy() + for(var/armor_type in removed_armor) + removed_armor[armor_type] = -removed_armor[armor_type] + for(var/obj/item/part as anything in parts) + part.armor = part.armor.modifyRating(arglist(removed_armor)) + if(!remove_pressure_protection || !isclothing(part)) + continue + var/obj/item/clothing/clothing_part = part + if(spaceproofed[clothing_part]) + clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE + spaceproofed = list() + +/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing) + overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" + overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]" + return ..() + +///Energy Shield - Gives you a rechargeable energy shield that nullifies attacks. +/obj/item/mod/module/energy_shield + name = "MOD energy shield module" + desc = "A personal, protective forcefield typically seen in military applications. \ + This advanced deflector shield is essentially a scaled down version of those seen on starships, \ + and the power cost can be an easy indicator of this. However, it is capable of blocking nearly any incoming attack, \ + though with its' low amount of separate charges, the user remains mortal." + icon_state = "energy_shield" + complexity = 3 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + use_power_cost = DEFAULT_CHARGE_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/energy_shield) + /// Max charges of the shield. + var/max_charges = 3 + /// The time it takes for the first charge to recover. + var/recharge_start_delay = 20 SECONDS + /// How much time it takes for charges to recover after they started recharging. + var/charge_increment_delay = 1 SECONDS + /// How much charge is recovered per recovery. + var/charge_recovery = 1 + /// Whether or not this shield can lose multiple charges. + var/lose_multiple_charges = FALSE + /// The item path to recharge this shielkd. + var/recharge_path = null + /// The icon file of the shield. + var/shield_icon_file = 'icons/effects/effects.dmi' + /// The icon_state of the shield. + var/shield_icon = "shield-red" + /// Charges the shield should start with. + var/charges + +/obj/item/mod/module/energy_shield/Initialize(mapload) + . = ..() + charges = max_charges + +/obj/item/mod/module/energy_shield/on_suit_activation() + mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \ + charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon) + RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(shield_reaction)) + +/obj/item/mod/module/energy_shield/on_suit_deactivation(deleting = FALSE) + var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded) + charges = shield.current_charges + qdel(shield) + UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS) + +/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner, atom/movable/hitby, damage = 0, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) + if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type) & COMPONENT_HIT_REACTION_BLOCK) + drain_power(use_power_cost) + return SHIELD_BLOCK + return NONE + +///Insignia - Gives you a skin specific stripe. +/obj/item/mod/module/insignia + name = "MOD insignia module" + desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \ + the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \ + they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \ + color patterns to and from the suit." + icon_state = "insignia" + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/insignia) + overlay_state_inactive = "module_insignia" + +/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing) + overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" + . = ..() + for(var/mutable_appearance/appearance as anything in .) + appearance.color = color + +/obj/item/mod/module/insignia/commander + color = "#4980a5" + +/obj/item/mod/module/insignia/security + color = "#b30d1e" + +/obj/item/mod/module/insignia/engineer + color = "#e9c80e" + +/obj/item/mod/module/insignia/medic + color = "#ebebf5" + +/obj/item/mod/module/insignia/janitor + color = "#7925c7" + +/obj/item/mod/module/insignia/chaplain + color = "#f0a00c" + +///Anti Slip - Prevents you from slipping on water. +/obj/item/mod/module/noslip + name = "MOD anti slip module" + desc = "These are a modified variant of standard magnetic boots, utilizing piezoelectric crystals on the soles. \ + The two plates on the bottom of the boots automatically extend and magnetize as the user steps; \ + a pull that's too weak to offer them the ability to affix to a hull, but just strong enough to \ + protect against the fact that you didn't read the wet floor sign. Honk Co. has come out numerous times \ + in protest of these modules being legal." + icon_state = "noslip" + complexity = 1 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1 + incompatible_modules = list(/obj/item/mod/module/noslip) + +/obj/item/mod/module/noslip/on_suit_activation() + mod.boots.clothing_flags |= NOSLIP + +/obj/item/mod/module/noslip/on_suit_deactivation(deleting = FALSE) + mod.boots.clothing_flags &= ~NOSLIP + +//Bite of 87 Springlock - Equips faster, disguised as DNA lock. +/obj/item/mod/module/springlock/bite_of_87 + +/obj/item/mod/module/springlock/bite_of_87/Initialize(mapload) + . = ..() + var/obj/item/mod/module/dna_lock/the_dna_lock_behind_the_slaughter = /obj/item/mod/module/dna_lock + name = initial(the_dna_lock_behind_the_slaughter.name) + desc = initial(the_dna_lock_behind_the_slaughter.desc) + icon_state = initial(the_dna_lock_behind_the_slaughter.icon_state) + complexity = initial(the_dna_lock_behind_the_slaughter.complexity) + use_power_cost = initial(the_dna_lock_behind_the_slaughter.use_power_cost) + +/obj/item/mod/module/springlock/bite_of_87/on_install() + mod.activation_step_time *= 0.1 + +/obj/item/mod/module/springlock/bite_of_87/on_uninstall(deleting = FALSE) + mod.activation_step_time *= 10 + +/obj/item/mod/module/springlock/bite_of_87/on_suit_activation() + ..() + if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS] || prob(1)) + mod.set_mod_color("#b17f00") + mod.wearer.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) // turns purple guy purple + mod.wearer.add_atom_colour("#704b96", FIXED_COLOUR_PRIORITY) + +///Flamethrower - Launches fire across the area. +/obj/item/mod/module/flamethrower + name = "MOD flamethrower module" + desc = "A custom-manufactured flamethrower, used to burn through your path. Burn well." + icon_state = "flamethrower" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN * 3 + incompatible_modules = list(/obj/item/mod/module/flamethrower) + cooldown_time = 2.5 SECONDS + overlay_state_inactive = "module_flamethrower" + overlay_state_active = "module_flamethrower_on" + +/obj/item/mod/module/flamethrower/on_select_use(atom/target) + . = ..() + if(!.) + return + var/obj/projectile/flame = new /obj/projectile/bullet/incendiary(mod.wearer.loc) + flame.preparePixelProjectile(target, mod.wearer) + flame.firer = mod.wearer + playsound(src, 'sound/items/modsuit/flamethrower.ogg', 75, TRUE) + INVOKE_ASYNC(flame, TYPE_PROC_REF(/obj/projectile, fire)) + drain_power(use_power_cost) + +///Power kick - Lets the user launch themselves at someone to kick them. +/obj/item/mod/module/power_kick + name = "MOD power kick module" + desc = "This module uses high-power myomer to generate an incredible amount of energy, transferred into the power of a kick." + icon_state = "power_kick" + module_type = MODULE_ACTIVE + removable = FALSE + use_power_cost = DEFAULT_CHARGE_DRAIN*5 + incompatible_modules = list(/obj/item/mod/module/power_kick) + cooldown_time = 5 SECONDS + /// Damage on kick. + var/damage = 20 + /// The wound bonus of the kick. + var/wounding_power = 35 + /// How long we knockdown for on the kick. + var/knockdown_time = 2 SECONDS + +/obj/item/mod/module/power_kick/on_select_use(atom/target) + . = ..() + if(!.) + return + mod.wearer.visible_message(span_warning("[mod.wearer] starts charging a kick!"), \ + blind_message = span_hear("You hear a charging sound.")) + playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE) + balloon_alert(mod.wearer, "you start charging...") + animate(mod.wearer, 0.3 SECONDS, pixel_z = 16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT) + addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, SpinAnimation), 3, 2), 0.3 SECONDS) + if(!do_after(mod.wearer, 1 SECONDS, target = mod)) + animate(mod.wearer, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT) + return + animate(mod.wearer) + drain_power(use_power_cost) + playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE) + var/angle = get_angle(mod.wearer, target) + 180 + mod.wearer.transform = mod.wearer.transform.Turn(angle) + RegisterSignal(mod.wearer, COMSIG_MOVABLE_IMPACT, PROC_REF(on_throw_impact)) + mod.wearer.throw_at(target, range = 7, speed = 2, thrower = mod.wearer, spin = FALSE, gentle = TRUE, callback = CALLBACK(src, PROC_REF(on_throw_end), mod.wearer, -angle)) + +/obj/item/mod/module/power_kick/proc/on_throw_end(mob/user, angle) + if(!user) + return + user.transform = user.transform.Turn(angle) + animate(user, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT) + +/obj/item/mod/module/power_kick/proc/on_throw_impact(mob/living/source, obj/target, datum/thrownthing/thrownthing) + SIGNAL_HANDLER + + UnregisterSignal(source, COMSIG_MOVABLE_IMPACT) + if(!mod?.wearer) + return + if(isliving(target)) + var/mob/living/living_target = target + living_target.apply_damage(damage, BRUTE, mod.wearer.zone_selected) + living_target.Knockdown(knockdown_time) + else if(target.obj_integrity) + target.take_damage(damage, BRUTE) + else + return + mod.wearer.do_attack_animation(target, ATTACK_EFFECT_SMASH) + +///Chameleon - lets the suit disguise as any item that would fit on that slot. +/obj/item/mod/module/chameleon + name = "MOD chameleon module" + desc = "A module using chameleon technology to disguise the suit as another object." + icon_state = "chameleon" + module_type = MODULE_USABLE + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/chameleon) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// A list of all the items the suit can disguise as. + var/list/possible_disguises = list() + /// The path of the item we're disguised as. + var/obj/item/current_disguise + +/obj/item/mod/module/chameleon/on_install() + var/list/all_disguises = sortList(subtypesof(get_path_by_slot(mod.slot_flags)), GLOBAL_PROC_REF(cmp_typepaths_asc)) + for(var/clothing_path in all_disguises) + var/obj/item/clothing = clothing_path + if(!initial(clothing.icon_state)) + continue + var/chameleon_item_name = "[initial(clothing.name)] ([initial(clothing.icon_state)])" + possible_disguises[chameleon_item_name] = clothing_path + +/obj/item/mod/module/chameleon/on_uninstall(deleting = FALSE) + if(current_disguise) + return_look() + possible_disguises = null + +/obj/item/mod/module/chameleon/on_use() + if(mod.active || mod.activating) + balloon_alert(mod.wearer, "suit active!") + return + . = ..() + if(!.) + return + if(current_disguise) + return_look() + return + var/picked_name = tgui_input_list(mod.wearer, "Select look to change into", "Chameleon Settings", possible_disguises) + if(!possible_disguises[picked_name] || mod.active || mod.activating) + return + current_disguise = possible_disguises[picked_name] + update_look() + +/obj/item/mod/module/chameleon/proc/update_look() + mod.name = initial(current_disguise.name) + mod.desc = initial(current_disguise.desc) + mod.icon_state = initial(current_disguise.icon_state) + mod.icon = initial(current_disguise.icon) + mod.mob_overlay_icon = initial(current_disguise.mob_overlay_icon) + mod.alternate_worn_layer = initial(current_disguise.alternate_worn_layer) + mod.lefthand_file = initial(current_disguise.lefthand_file) + mod.righthand_file = initial(current_disguise.righthand_file) + //mod.mob_overlay_state = initial(current_disguise.mob_overlay_state) + mod.item_state = initial(current_disguise.item_state) + mod.wearer.update_inv_back(mod.slot_flags) + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(return_look)) + +/obj/item/mod/module/chameleon/proc/return_look() + mod.name = "[mod.theme.name] [initial(mod.name)]" + mod.desc = "[initial(mod.desc)] [mod.theme.desc]" + mod.icon_state = "[mod.skin]-[initial(mod.icon_state)]" + var/list/mod_skin = mod.theme.skins[mod.skin] + mod.icon = mod_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mod.mob_overlay_icon = mod_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi' + mod.alternate_worn_layer = mod_skin[CONTROL_LAYER] + mod.lefthand_file = initial(mod.lefthand_file) + mod.righthand_file = initial(mod.righthand_file) + //___callbacknewmod.worn_icon_state = null + mod.item_state = null + mod.wearer.update_inv_back(mod.slot_flags) + current_disguise = null + UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) + +///Plate Compression - Compresses the suit to normal size +/obj/item/mod/module/plate_compression + name = "MOD plate compression module" + desc = "A module that keeps the suit in a very tightly fit state, lowering the overall size. \ + Due to the pressure on all the parts, typical storage modules do not fit." + icon_state = "plate_compression" + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/plate_compression, /obj/item/mod/module/storage) + /// The size we set the suit to. + var/new_size = WEIGHT_CLASS_NORMAL + /// The suit's size before the module is installed. + var/old_size + +/obj/item/mod/module/plate_compression/on_install() + old_size = mod.w_class + mod.w_class = new_size + +/obj/item/mod/module/plate_compression/on_uninstall(deleting = FALSE) + mod.w_class = old_size + old_size = null + if(!mod.loc) + return + var/datum/component/storage/concrete/holding_storage = mod.GetComponent(/datum/component/storage/concrete) + if(!holding_storage || holding_storage.max_combined_w_class >= mod.w_class) + return + mod.forceMove(drop_location()) diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm new file mode 100644 index 000000000000..4905b3ae691f --- /dev/null +++ b/code/modules/mod/modules/modules_engineering.dm @@ -0,0 +1,169 @@ +//Engineering modules for MODsuits + +///Welding Protection - Makes the helmet protect from flashes and welding. +/obj/item/mod/module/welding + name = "MOD welding protection module" + desc = "A module installed into the visor of the suit, this projects a \ + polarized, holographic overlay in front of the user's eyes. It's rated high enough for \ + immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights." + icon_state = "welding" + complexity = 1 + incompatible_modules = list(/obj/item/mod/module/welding, /obj/item/mod/module/armor_booster) + overlay_state_inactive = "module_welding" + +/obj/item/mod/module/welding/on_suit_activation() + mod.helmet.flash_protect = FLASH_PROTECTION_WELDER + +/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE) + if(deleting) + return + mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + +///T-Ray Scan - Scans the terrain for undertile objects. +/obj/item/mod/module/t_ray + name = "MOD t-ray scan module" + desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \ + to essentially echolocate things beneath the floor, mostly cables and pipes. \ + A staple of atmospherics work, and counter-smuggling work." + icon_state = "tray" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/t_ray) + cooldown_time = 0.5 SECONDS + /// T-ray scan range. + var/range = 4 + +/obj/item/mod/module/t_ray/on_active_process(delta_time) + t_ray_scan(mod.wearer, 0.8 SECONDS, range) + +///Magnetic Stability - Gives the user a slowdown but makes them negate gravity and be immune to slips. +/obj/item/mod/module/magboot + name = "MOD magnetic stability module" + desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \ + excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \ + However, these basic models do not feature computerized systems to automatically toggle them on and off, \ + so numerous users report a certain stickiness to their steps." + icon_state = "magnet" + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/magboot) + cooldown_time = 0.5 SECONDS + /// Slowdown added onto the suit. + var/slowdown_active = 0.5 + +/obj/item/mod/module/magboot/on_activation() + . = ..() + if(!.) + return + ADD_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) + mod.slowdown += slowdown_active + mod.wearer.update_gravity(mod.wearer.has_gravity()) + mod.wearer.update_equipment_speed_mods() + +/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) + mod.slowdown -= slowdown_active + mod.wearer.update_gravity(mod.wearer.has_gravity()) + mod.wearer.update_equipment_speed_mods() + +/obj/item/mod/module/magboot/advanced + name = "MOD advanced magnetic stability module" + removable = FALSE + complexity = 0 + slowdown_active = 0 + +///Emergency Tether - Shoots a grappling hook projectile in 0g that throws the user towards it. +/obj/item/mod/module/tether + name = "MOD emergency tether module" + desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \ + While some older models of cargo-oriented grapples have capacities of a few tons, \ + these are only capable of working in zero-gravity environments, a blessing to some Engineers." + icon_state = "tether" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/tether) + cooldown_time = 1.5 SECONDS + +/obj/item/mod/module/tether/on_use() + if(mod.wearer.has_gravity(get_turf(src))) + balloon_alert(mod.wearer, "too much gravity!!") + playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE) + return FALSE + return ..() + +/obj/item/mod/module/tether/on_select_use(atom/target) + . = ..() + if(!.) + return + var/obj/projectile/tether = new /obj/projectile/tether(mod.wearer.loc) + tether.preparePixelProjectile(target, mod.wearer) + tether.firer = mod.wearer + playsound(src, 'sound/weapons/batonextend.ogg', 25, TRUE) + INVOKE_ASYNC(tether, TYPE_PROC_REF(/obj/projectile, fire)) + drain_power(use_power_cost) + +/obj/projectile/tether + name = "tether" + icon_state = "tether_projectile" + icon = 'icons/obj/clothing/modsuit/mod_modules.dmi' + damage = 0 + nodamage = TRUE + range = 10 + hitsound = 'sound/weapons/batonextend.ogg' + suppressed = SUPPRESSED_VERY + //hit_threshhold = LATTICE_LAYER + /// Reference to the beam following the projectile. + var/line + +/obj/projectile/tether/fire(setAngle) + if(firer) + line = firer.Beam(src, "line", 'icons/obj/clothing/modsuit/mod_modules.dmi') + ..() + +/obj/projectile/tether/on_hit(atom/target) + . = ..() + if(firer) + firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE) + +/obj/projectile/tether/Destroy() + QDEL_NULL(line) + return ..() + +///Mister - Sprays water over an area. +/obj/item/mod/module/mister + name = "MOD water mister module" + desc = "A module containing a mister, able to spray it over areas." + icon_state = "mister" + module_type = MODULE_ACTIVE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + device = /obj/item/reagent_containers/spray/mister + incompatible_modules = list(/obj/item/mod/module/mister) + cooldown_time = 0.5 SECONDS + /// Volume of our reagent holder. + var/volume = 500 + +/obj/item/mod/module/mister/Initialize(mapload) + create_reagents(volume, OPENCONTAINER) + return ..() + +///Resin Mister - Sprays resin over an area. +/obj/item/mod/module/mister/atmos + name = "MOD resin mister module" + desc = "An atmospheric resin mister, able to fix up areas quickly." + device = /obj/item/extinguisher/mini/nozzle/mod + volume = 250 + +/obj/item/mod/module/mister/atmos/Initialize(mapload) + . = ..() + reagents.add_reagent(/datum/reagent/water, volume) + +/obj/item/extinguisher/mini/nozzle/mod + name = "MOD atmospheric mister" + desc = "An atmospheric resin mister with three modes, mounted as a module." diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm new file mode 100644 index 000000000000..8c5f9e27cf55 --- /dev/null +++ b/code/modules/mod/modules/modules_general.dm @@ -0,0 +1,445 @@ +//General modules for MODsuits + +///Ion Jetpack - Lets the user fly freely through space using battery charge. +/obj/item/mod/module/jetpack + name = "MOD ion jetpack module" + desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \ + Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \ + charge from the suit's charge. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits." + icon_state = "jetpack" + module_type = MODULE_TOGGLE + complexity = 3 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/jetpack) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_jetpack" + overlay_state_active = "module_jetpack_on" + /// Do we stop the wearer from gliding in space. + var/stabilizers = FALSE + /// Do we give the wearer a speed buff. + var/full_speed = FALSE + var/datum/callback/get_mover + var/datum/callback/check_on_move + +/obj/item/mod/module/jetpack/Initialize(mapload) + . = ..() + get_mover = CALLBACK(src, PROC_REF(get_user)) + check_on_move = CALLBACK(src, PROC_REF(allow_thrust)) + refresh_jetpack() + +/obj/item/mod/module/jetpack/Destroy() + get_mover = null + check_on_move = null + return ..() + +/obj/item/mod/module/jetpack/proc/refresh_jetpack() + AddComponent(/datum/component/jetpack, stabilizers, COMSIG_MODULE_TRIGGERED, COMSIG_MODULE_DEACTIVATED, MOD_ABORT_USE, get_mover, check_on_move, /datum/effect_system/trail_follow/ion/grav_allowed) + +/obj/item/mod/module/jetpack/proc/set_stabilizers(new_stabilizers) + if(stabilizers == new_stabilizers) + return + stabilizers = new_stabilizers + refresh_jetpack() + +/obj/item/mod/module/jetpack/on_activation() + . = ..() + if(!.) + return + if(full_speed) + mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + +/obj/item/mod/module/jetpack/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(full_speed) + mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + +/obj/item/mod/module/jetpack/get_configuration() + . = ..() + .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers) + +/obj/item/mod/module/jetpack/configure_edit(key, value) + switch(key) + if("stabilizers") + set_stabilizers(text2num(value)) + +/obj/item/mod/module/jetpack/proc/allow_thrust(use_fuel = TRUE) + if(!use_fuel) + return check_power(use_power_cost) + if(!drain_power(use_power_cost)) + return FALSE + return TRUE + +/obj/item/mod/module/jetpack/proc/get_user() + return mod.wearer + +/obj/item/mod/module/jetpack/advanced + name = "MOD advanced ion jetpack module" + desc = "An improvement on the previous model of electric thrusters. This one achieves higher speeds through \ + mounting of more jets and a red paint applied on it." + icon_state = "jetpack_advanced" + overlay_state_inactive = "module_jetpackadv" + overlay_state_active = "module_jetpackadv_on" + full_speed = TRUE + +///Eating Apparatus - Lets the user eat/drink with the suit on. +/obj/item/mod/module/mouthhole + name = "MOD eating apparatus module" + desc = "A favorite by Miners, this modification to the helmet utilizes a nanotechnology barrier infront of the mouth \ + to allow eating and drinking while retaining protection and atmosphere. However, it won't free you from masks, \ + and it will do nothing to improve the taste of a goliath steak." + icon_state = "apparatus" + complexity = 1 + incompatible_modules = list(/obj/item/mod/module/mouthhole) + overlay_state_inactive = "module_apparatus" + /// Former flags of the helmet. + var/former_flags = NONE + /// Former visor flags of the helmet. + var/former_visor_flags = NONE + +/obj/item/mod/module/mouthhole/on_install() + former_flags = mod.helmet.flags_cover + former_visor_flags = mod.helmet.visor_flags_cover + mod.helmet.flags_cover &= ~HEADCOVERSMOUTH|PEPPERPROOF + mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH|PEPPERPROOF + +/obj/item/mod/module/mouthhole/on_uninstall(deleting = FALSE) + if(deleting) + return + mod.helmet.flags_cover |= former_flags + mod.helmet.visor_flags_cover |= former_visor_flags + +///EMP Shield - Protects the suit from EMPs. +/obj/item/mod/module/emp_shield + name = "MOD EMP shield module" + desc = "A field inhibitor installed into the suit, protecting it against feedback such as \ + electromagnetic pulses that would otherwise damage the electronic systems of the suit or devices on the wearer. \ + However, it will take from the suit's power to do so. Luckily, your PDA already has one of these." + icon_state = "empshield" + complexity = 1 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/emp_shield) + +/obj/item/mod/module/emp_shield/on_install() + mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + +/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE) + mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + +/obj/item/mod/module/emp_shield/advanced + name = "MOD advanced EMP shield module" + desc = "An enhnanced field inhibitor installed into the suit, protecting it against feedback such as \ + electromagnetic pulses that would otherwise damage the electronic systems of the suit or devices on the wearer \ + including augmentations. However, it will take from the suit's power to do so. Luckily, your PDA already has one of these." + complexity = 2 + +/obj/item/mod/module/emp_shield/advanced/on_suit_activation() + mod.wearer.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) + +/obj/item/mod/module/emp_shield/advanced/on_suit_deactivation(deleting) + mod.wearer.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) + +///Flashlight - Gives the suit a customizable flashlight. +/obj/item/mod/module/flashlight + name = "MOD flashlight module" + desc = "A simple pair of flashlights installed on the left and right sides of the helmet." + icon_state = "flashlight" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/flashlight) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_light" + light_system = MOVABLE_LIGHT_DIRECTIONAL + light_color = COLOR_WHITE + light_range = 4 + light_power = 1 + light_on = FALSE + /// Charge drain per range amount. + var/base_power = DEFAULT_CHARGE_DRAIN * 0.1 + +/obj/item/mod/module/flashlight/on_activation() + . = ..() + if(!.) + return + set_light_flags(light_flags | LIGHT_ATTACHED) + set_light_on(active) + active_power_cost = base_power * light_range + +/obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + set_light_flags(light_flags & ~LIGHT_ATTACHED) + set_light_on(active) + +/obj/item/mod/module/flashlight/on_process(delta_time) + active_power_cost = base_power * light_range + return ..() + +/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing) + . = ..() + if(!active) + return + var/mutable_appearance/light_icon = mutable_appearance(overlay_icon_file, "module_light_on", layer = standing + 0.2) + light_icon.appearance_flags = RESET_COLOR + light_icon.color = light_color + . += light_icon + +///Dispenser - Dispenses an item after a time passes. +/obj/item/mod/module/dispenser + name = "MOD burger dispenser module" + desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \ + This can draw incredible amounts of power from the suit's charge to create edible organic matter in the \ + palm of the wearer's glove; however, research seemed to have entirely stopped at burgers. \ + Notably, all attempts to get it to dispense Earl Grey tea have failed." + icon_state = "dispenser" + module_type = MODULE_USABLE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/dispenser) + cooldown_time = 5 SECONDS + /// Path we dispense. + var/dispense_type = /obj/item/reagent_containers/food/snacks/burger + /// Time it takes for us to dispense. + var/dispense_time = 0 SECONDS + +/obj/item/mod/module/dispenser/on_use() + . = ..() + if(!.) + return + if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod)) + balloon_alert(mod.wearer, "interrupted!") + return FALSE + var/obj/item/dispensed = new dispense_type(mod.wearer.loc) + mod.wearer.put_in_hands(dispensed) + balloon_alert(mod.wearer, "[dispensed] dispensed") + playsound(src, 'sound/machines/click.ogg', 100, TRUE) + drain_power(use_power_cost) + return dispensed + +///Thermal Regulator - Regulates the wearer's core temperature. +/obj/item/mod/module/thermal_regulator + name = "MOD thermal regulator module" + desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \ + flexible cooling lines. This circulates coolant at various user-controlled temperatures, \ + ensuring they're comfortable; even if they're some that like it hot." + icon_state = "regulator" + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/thermal_regulator) + cooldown_time = 0.5 SECONDS + /// The temperature we are regulating to. + var/temperature_setting = BODYTEMP_NORMAL + /// Minimum temperature we can set. + var/min_temp = 293.15 + /// Maximum temperature we can set. + var/max_temp = 318.15 + +/obj/item/mod/module/thermal_regulator/get_configuration() + . = ..() + .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C) + +/obj/item/mod/module/thermal_regulator/configure_edit(key, value) + switch(key) + if("temperature_setting") + temperature_setting = clamp(value + T0C, min_temp, max_temp) + +/obj/item/mod/module/thermal_regulator/on_active_process(delta_time) + mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time)) + +///DNA Lock - Prevents people without the set DNA from activating the suit. +/obj/item/mod/module/dna_lock + name = "MOD DNA lock module" + desc = "A module which engages with the various locks and seals tied to the suit's systems, \ + enabling it to only be worn by someone corresponding with the user's exact DNA profile; \ + however, this incredibly sensitive module is shorted out by EMPs. Luckily, cloning has been outlawed." + icon_state = "dnalock" + module_type = MODULE_USABLE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 3 + incompatible_modules = list(/obj/item/mod/module/dna_lock/*, obj/item/mod/module/eradication_lock*/) + cooldown_time = 0.5 SECONDS + /// The DNA we lock with. + var/dna = null + +/obj/item/mod/module/dna_lock/on_install() + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_mod_activation)) + RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, PROC_REF(on_mod_removal)) + RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp)) + RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag)) + +/obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE) + UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) + UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL) + UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT) + UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT) + +/obj/item/mod/module/dna_lock/on_use() + . = ..() + if(!.) + return + dna = mod.wearer.dna.unique_enzymes + balloon_alert(mod.wearer, "dna updated") + drain_power(use_power_cost) + +/obj/item/mod/module/dna_lock/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + on_emp(src, severity) + +/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + on_emag(src, user, emag_card) + +/obj/item/mod/module/dna_lock/proc/dna_check(mob/user) + if(!iscarbon(user)) + return FALSE + var/mob/living/carbon/carbon_user = user + if(!dna || (carbon_user.has_dna() && carbon_user.dna.unique_enzymes == dna)) + return TRUE + balloon_alert(user, "dna locked!") + return FALSE + +/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity) + SIGNAL_HANDLER + + dna = null + +/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card) + SIGNAL_HANDLER + + dna = null + +/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source, mob/user) + SIGNAL_HANDLER + + if(!dna_check(user)) + return MOD_CANCEL_ACTIVATE + +/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source, mob/user) + SIGNAL_HANDLER + + if(!dna_check(user)) + return MOD_CANCEL_REMOVAL + +///Plasma Stabilizer - Prevents plasmamen from igniting in the suit +/obj/item/mod/module/plasma_stabilizer + name = "MOD plasma stabilizer module" + desc = "This system essentially forms an atmosphere of its' own inside the suit, \ + safely ejecting oxygen from the inside and allowing the wearer, a plasmaman, \ + to have their internal plasma circulate around them somewhat like a sauna. \ + This prevents them from self-igniting, and leads to greater comfort overall. \ + The purple glass of the visor seems to be constructed for nostalgic purposes." + icon_state = "plasma_stabilizer" + complexity = 1 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer) + overlay_state_inactive = "module_plasma" + +/obj/item/mod/module/plasma_stabilizer/on_equip() + ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MOD_TRAIT) + +/obj/item/mod/module/plasma_stabilizer/on_unequip() + REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MOD_TRAIT) + + +//Finally, https://pipe.miroware.io/5b52ba1d94357d5d623f74aa/mspfa/Nuke%20Ops/Panels/0648.gif can be real: +///Hat Stabilizer - Allows displaying a hat over the MOD-helmet, à la plasmamen helmets. +/obj/item/mod/module/hat_stabilizer + name = "MOD hat stabilizer module" + desc = "A simple set of deployable stands, directly atop one's head; \ + these will deploy under a select few hats to keep them from falling off, allowing them to be worn atop the sealed helmet. \ + You still need to take the hat off your head while the helmet deploys, though. \ + This is a must-have for Nanotrasen Captains, enabling them to show off their authoritative hat even while in their MODsuit." + icon_state = "hat_holder" + incompatible_modules = list(/obj/item/mod/module/hat_stabilizer) + /*Intentionally left inheriting 0 complexity and removable = TRUE; + even though it comes inbuilt into the Magnate/Corporate MODS and spawns in maints, I like the idea of stealing them*/ + /// Currently "stored" hat. No armor or function will be inherited, ONLY the icon. + var/obj/item/clothing/head/attached_hat + /// Whitelist of attachable hats, read note in Initialize() below this line + var/static/list/attachable_hats_list + +/obj/item/mod/module/hat_stabilizer/Initialize(mapload) + . = ..() + attachable_hats_list = typecacheof( + //List of attachable hats. Make sure these and their subtypes are all tested, so they dont appear janky. + //This list should also be gimmicky, so captains can have fun. I.E. the Santahat, Pirate hat, Tophat, Chefhat... + //Yes, I said it, the captain should have fun. + list( + /obj/item/clothing/head/caphat, + /obj/item/clothing/head/crown, + /obj/item/clothing/head/centhat, + /obj/item/clothing/head/pirate, + /obj/item/clothing/head/santa, + /obj/item/clothing/head/hardhat/reindeer, + /obj/item/clothing/head/sombrero, + /obj/item/clothing/head/kitty, + /obj/item/clothing/head/rabbitears, + /obj/item/clothing/head/festive, + /obj/item/clothing/head/powdered_wig, + /obj/item/clothing/head/that, + /obj/item/clothing/head/nursehat, + /obj/item/clothing/head/chefhat, + /obj/item/clothing/head/papersack, + )) + +/obj/item/mod/module/hat_stabilizer/on_suit_activation() + RegisterSignal(mod.helmet, COMSIG_PARENT_EXAMINE, PROC_REF(add_examine)) + RegisterSignal(mod.helmet, COMSIG_PARENT_ATTACKBY, PROC_REF(place_hat)) + RegisterSignal(mod.helmet, COMSIG_CLICK_ALT, PROC_REF(remove_hat)) + +/obj/item/mod/module/hat_stabilizer/on_suit_deactivation(deleting = FALSE) + if(deleting) + return + if(attached_hat) //knock off the helmet if its on their head. Or, technically, auto-rightclick it for them; that way it saves us code, AND gives them the bubble + remove_hat(src, mod.wearer) + UnregisterSignal(mod.helmet, COMSIG_PARENT_EXAMINE) + UnregisterSignal(mod.helmet, COMSIG_PARENT_ATTACKBY) + UnregisterSignal(mod.helmet, COMSIG_CLICK_ALT) + +/obj/item/mod/module/hat_stabilizer/proc/add_examine(datum/source, mob/user, list/base_examine) + SIGNAL_HANDLER + if(attached_hat) + base_examine += span_notice("There's \a [attached_hat] placed on the helmet. Alt-click to remove it.") + else + base_examine += span_notice("There's nothing placed on the helmet. Yet.") + +/obj/item/mod/module/hat_stabilizer/proc/place_hat(datum/source, obj/item/hitting_item, mob/user) + SIGNAL_HANDLER + if(!istype(hitting_item, /obj/item/clothing/head)) + return + if(!mod.active) + balloon_alert(user, "suit must be active!") + return + if(!is_type_in_typecache(hitting_item, attachable_hats_list)) + balloon_alert(user, "this hat won't fit!") + return + if(attached_hat) + balloon_alert(user, "hat already attached!") + return + if(mod.wearer.transferItemToLoc(hitting_item, src, force = FALSE, silent = TRUE)) + attached_hat = hitting_item + balloon_alert(user, "hat attached, alt-click to remove") + mod.wearer.update_inv_back(mod.slot_flags) + +/obj/item/mod/module/hat_stabilizer/generate_worn_overlay() + . = ..() + if(attached_hat) + . += attached_hat.build_worn_icon(default_layer = ABOVE_MOB_LAYER, default_icon_file = 'icons/mob/clothing/head.dmi') + +/obj/item/mod/module/hat_stabilizer/proc/remove_hat(datum/source, mob/user) + SIGNAL_HANDLER + . = SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(!attached_hat) + return + attached_hat.forceMove(drop_location()) + if(user.put_in_active_hand(attached_hat)) + balloon_alert(user, "hat removed") + else + balloon_alert_to_viewers("the hat falls to the floor!") + attached_hat = null + mod.wearer.update_inv_back(mod.slot_flags) diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm new file mode 100644 index 000000000000..e735654ef2c5 --- /dev/null +++ b/code/modules/mod/modules/modules_maint.dm @@ -0,0 +1,148 @@ +//Maint modules for MODsuits + +///Springlock Mechanism - allows your modsuit to activate faster, but reagents are very dangerous. +/obj/item/mod/module/springlock + name = "MOD springlock module" + desc = "A module that spans the entire size of the MOD unit, sitting under the outer shell. \ + This mechanical exoskeleton pushes out of the way when the user enters and it helps in booting \ + up, but was taken out of modern suits because of the springlock's tendency to \"snap\" back \ + into place when exposed to humidity. You know what it's like to have an entire exoskeleton enter you?" + icon_state = "springlock" + complexity = 3 // it is inside every part of your suit, so + incompatible_modules = list(/obj/item/mod/module/springlock) + +/obj/item/mod/module/springlock/on_install() + mod.activation_step_time *= 0.5 + +/obj/item/mod/module/springlock/on_uninstall(deleting = FALSE) + mod.activation_step_time *= 2 + +/obj/item/mod/module/springlock/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_wearer_exposed)) + +/obj/item/mod/module/springlock/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS) + +///Signal fired when wearer is exposed to reagents +/obj/item/mod/module/springlock/proc/on_wearer_exposed(atom/source, list/reagents, datum/reagents/source_reagents, methods, volume_modifier, show_message) + SIGNAL_HANDLER + + if(!(methods & (VAPOR|PATCH|TOUCH))) + return //remove non-touch reagent exposure + to_chat(mod.wearer, span_danger("[src] makes an ominous click sound...")) + playsound(src, 'sound/items/modsuit/springlock.ogg', 75, TRUE) + addtimer(CALLBACK(src, PROC_REF(snap_shut)), rand(3 SECONDS, 5 SECONDS)) + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block)) + +///Signal fired when wearer attempts to activate/deactivate suits +/obj/item/mod/module/springlock/proc/on_activate_spring_block(datum/source, user) + SIGNAL_HANDLER + + balloon_alert(user, "springlocks aren't responding...?") + return MOD_CANCEL_ACTIVATE + +///Delayed death proc of the suit after the wearer is exposed to reagents +/obj/item/mod/module/springlock/proc/snap_shut() + UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) + if(!mod.wearer) //while there is a guaranteed user when on_wearer_exposed() fires, that isn't the same case for this proc + return + mod.wearer.visible_message("[src] inside [mod.wearer]'s [mod.name] snaps shut, mutilating the user inside!", span_userdanger("*SNAP*")) + mod.wearer.emote("scream") + playsound(mod.wearer, 'sound/effects/snap.ogg', 75, TRUE, frequency = 0.5) + playsound(mod.wearer, 'sound/effects/splat.ogg', 50, TRUE, frequency = 0.5) + mod.wearer.apply_damage(500, BRUTE, forced = TRUE, spread_damage = TRUE) //boggers, bogchamp, etc + if(!HAS_TRAIT(mod.wearer, TRAIT_NODEATH)) + mod.wearer.death() //just in case, for some reason, they're still alive + flash_color(mod.wearer, flash_color = "#FF0000", flash_time = 10 SECONDS) + +///Balloon Blower - Blows a balloon. +/obj/item/mod/module/balloon + name = "MOD balloon blower module" + desc = "A strange module invented years ago by some ingenious mimes. It blows balloons." + icon_state = "bloon" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/balloon) + cooldown_time = 15 SECONDS + +/obj/item/mod/module/balloon/on_use() + . = ..() + if(!.) + return + if(!do_after(mod.wearer, 10 SECONDS, target = mod)) + return FALSE + mod.wearer.adjustOxyLoss(20) + playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE) + var/obj/item/toy/balloon/balloon = new(get_turf(src)) + mod.wearer.put_in_hands(balloon) + drain_power(use_power_cost) + +///Paper Dispenser - Dispenses (sometimes burning) paper sheets. +/obj/item/mod/module/paper_dispenser + name = "MOD paper dispenser module" + desc = "A simple module designed by the bureaucrats of Torch Bay. \ + It dispenses 'warm, clean, and crisp sheets of paper' onto a nearby table. Usually." + icon_state = "paper_maker" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/paper_dispenser) + cooldown_time = 5 SECONDS + /// The total number of sheets created by this MOD. The more sheets, them more likely they set on fire. + var/num_sheets_dispensed = 0 + +/obj/item/mod/module/paper_dispenser/on_use() + . = ..() + if(!.) + return + if(!do_after(mod.wearer, 1 SECONDS, target = mod)) + return FALSE + + var/obj/item/paper/crisp_paper = new(get_turf(src)) + crisp_paper.desc = "It's crisp and warm to the touch. Must be fresh." + + var/obj/structure/table/nearby_table = locate() in range(1, mod.wearer) + playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) + balloon_alert(mod.wearer, "dispensed paper[nearby_table ? " onto table":""]") + + mod.wearer.put_in_hands(crisp_paper) + if(nearby_table) + mod.wearer.transferItemToLoc(crisp_paper, nearby_table.drop_location(), silent = FALSE) + + // Up to a 30% chance to set the sheet on fire, +2% per sheet made + if(prob(min(num_sheets_dispensed * 2, 30))) + if(crisp_paper in mod.wearer.held_items) + mod.wearer.dropItemToGround(crisp_paper, force = TRUE) + crisp_paper.balloon_alert(mod.wearer, "pc load letter!") + crisp_paper.visible_message(span_warning("[crisp_paper] bursts into flames, it's too crisp!")) + crisp_paper.fire_act(1000, 100) + + drain_power(use_power_cost) + num_sheets_dispensed++ + + +///Stamper - Extends a stamp that can switch between accept/deny modes. +/obj/item/mod/module/stamp + name = "MOD stamper module" + desc = "A module installed into the wrist of the suit, this functions as a high-power stamp, \ + able to switch between accept and deny modes." + icon_state = "stamp" + module_type = MODULE_ACTIVE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + device = /obj/item/stamp/mod + incompatible_modules = list(/obj/item/mod/module/stamp) + cooldown_time = 0.5 SECONDS + +/obj/item/stamp/mod + name = "MOD electronic stamp" + desc = "A high-power stamp, able to switch between accept and deny mode when used." + +/obj/item/stamp/mod/attack_self(mob/user, modifiers) + . = ..() + if(icon_state == "stamp-ok") + icon_state = "stamp-deny" + else + icon_state = "stamp-ok" + balloon_alert(user, "switched mode") diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm new file mode 100644 index 000000000000..798f065ffe7f --- /dev/null +++ b/code/modules/mod/modules/modules_medical.dm @@ -0,0 +1,110 @@ +//Medical modules for MODsuits + +#define HEALTH_SCAN "Health" +#define WOUND_SCAN "Wound" +#define CHEM_SCAN "Chemical" + +///Health Analyzer - Gives the user a ranged health analyzer and their health status in the panel. +/obj/item/mod/module/health_analyzer + name = "MOD health analyzer module" + desc = "A module installed into the glove of the suit. This is a high-tech biological scanning suite, \ + allowing the user indepth information on the vitals and injuries of others even at a distance, \ + all with the flick of the wrist. Data is displayed in a convenient package on HUD in the helmet, \ + but it's up to you to do something with it." + icon_state = "health" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/health_analyzer) + cooldown_time = 0.5 SECONDS + tgui_id = "health_analyzer" + /// Scanning mode, changes how we scan something. + var/mode = HEALTH_SCAN + /// List of all scanning modes. + var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN) + +/obj/item/mod/module/health_analyzer/add_ui_data() + . = ..() + .["userhealth"] = mod.wearer?.health || 0 + .["usermaxhealth"] = mod.wearer?.getMaxHealth() || 0 + .["userbrute"] = mod.wearer?.getBruteLoss() || 0 + .["userburn"] = mod.wearer?.getFireLoss() || 0 + .["usertoxin"] = mod.wearer?.getToxLoss() || 0 + .["useroxy"] = mod.wearer?.getOxyLoss() || 0 + +/obj/item/mod/module/health_analyzer/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!isliving(target) || !mod.wearer.can_read(src)) + return + switch(mode) + if(HEALTH_SCAN) + healthscan(mod.wearer, target) + if(CHEM_SCAN) + chemscan(mod.wearer, target) + drain_power(use_power_cost) + +/obj/item/mod/module/health_analyzer/get_configuration() + . = ..() + .["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes) + +/obj/item/mod/module/health_analyzer/configure_edit(key, value) + switch(key) + if("mode") + mode = value + +#undef HEALTH_SCAN +#undef WOUND_SCAN +#undef CHEM_SCAN + +///Quick Carry - Lets the user carry bodies quicker. +/obj/item/mod/module/quick_carry + name = "MOD quick carry module" + desc = "A suite of advanced servos, redirecting power from the suit's arms to help carry the wounded; \ + or simply for fun. However, Nanotrasen has locked the module's ability to assist in hand-to-hand combat." + icon_state = "carry" + complexity = 1 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/quick_carry) + +/obj/item/mod/module/quick_carry/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/on_suit_deactivation(deleting = FALSE) + REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/advanced + name = "MOD advanced quick carry module" + removable = FALSE + complexity = 0 + +/obj/item/mod/module/quick_carry/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/on_suit_deactivation(deleting = FALSE) + REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) + +///Injector - Gives the suit an extendable large-capacity piercing syringe. +/obj/item/mod/module/injector + name = "MOD injector module" + desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \ + with a tip fine enough to locate the emergency injection ports on any suit of armor, \ + penetrating it with ease. Even yours." + icon_state = "injector" + module_type = MODULE_ACTIVE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + device = /obj/item/reagent_containers/syringe/mod + incompatible_modules = list(/obj/item/mod/module/injector) + cooldown_time = 0.5 SECONDS + +/obj/item/reagent_containers/syringe/mod + name = "MOD injector syringe" + desc = "A high-capacity syringe, with a tip fine enough to locate \ + the emergency injection ports on any suit of armor, penetrating it with ease. Even yours." + amount_per_transfer_from_this = 30 + possible_transfer_amounts = list(5, 10, 15, 20, 30) + volume = 30 diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm new file mode 100644 index 000000000000..69da2287eb72 --- /dev/null +++ b/code/modules/mod/modules/modules_ninja.dm @@ -0,0 +1,446 @@ +//Ninja modules for MODsuits + +///Cloaking - Lowers the user's visibility, can be interrupted by being touched or attacked. +/obj/item/mod/module/stealth + name = "MOD prototype cloaking module" + desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \ + to bend light around the user, as well as mimetic materials to make the surface of the suit match the \ + surroundings based off sensor data. For some reason, this tech is rarely seen." + icon_state = "cloak" + module_type = MODULE_TOGGLE + complexity = 4 + active_power_cost = DEFAULT_CHARGE_DRAIN * 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 10 + incompatible_modules = list(/obj/item/mod/module/stealth) + cooldown_time = 5 SECONDS + /// Whether or not the cloak turns off on bumping. + var/bumpoff = TRUE + /// The alpha applied when the cloak is on. + var/stealth_alpha = 50 + +/obj/item/mod/module/stealth/on_activation() + . = ..() + if(!.) + return + if(bumpoff) + RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, PROC_REF(unstealth)) + RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, PROC_REF(on_unarmed_attack)) + RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) + RegisterSignal(mod.wearer, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND/*, COMSIG_ATOM_HITBY*/, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED), PROC_REF(unstealth)) + animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS) + drain_power(use_power_cost) + +/obj/item/mod/module/stealth/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + if(bumpoff) + UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP) + UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT/*, COMSIG_ATOM_HITBY*/, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED)) + animate(mod.wearer, alpha = 255, time = 1.5 SECONDS) + +/obj/item/mod/module/stealth/proc/unstealth(datum/source) + SIGNAL_HANDLER + + to_chat(mod.wearer, span_warning("[src] gets discharged from contact!")) + do_sparks(2, TRUE, src) + drain_power(use_power_cost) + on_deactivation(display_message = TRUE, deleting = FALSE) + +/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target) + SIGNAL_HANDLER + + if(!isliving(target)) + return + unstealth(source) + +/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/projectile/projectile) + SIGNAL_HANDLER + + if(projectile.nodamage) + return + unstealth(source) + +//Advanced Cloaking - Doesn't turf off on bump, less power drain, more stealthy. +/obj/item/mod/module/stealth/ninja + name = "MOD advanced cloaking module" + desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \ + The field has been tuned to be even more responsive and fast-acting, with enough stability to \ + continue operation of the field even if the user bumps into others. \ + The power draw has been reduced drastically, making this perfect for activities like \ + standing near sentry turrets for extended periods of time." + icon_state = "cloak_ninja" + bumpoff = FALSE + stealth_alpha = 20 + active_power_cost = DEFAULT_CHARGE_DRAIN + use_power_cost = DEFAULT_CHARGE_DRAIN * 5 + cooldown_time = 3 SECONDS + +///Camera Vision - Prevents flashes, blocks tracking. +/obj/item/mod/module/welding/camera_vision + name = "MOD camera vision module" + desc = "A module installed into the suit's helmet. This specialized piece of technology is built for subterfuge, \ + replacing the standard visor with a nanotech display; capable of displaying specialized imagery at \ + just the right frequency to jam all known forms of camera tracking and facial recognition, \ + as well as automatically dimming incoming flashes of light to protect the user's eyes. Become the unseen." + icon_state = "welding_camera" + removable = FALSE + complexity = 0 + overlay_state_inactive = null + +/obj/item/mod/module/welding/camera_vision/on_suit_activation() + . = ..() + RegisterSignal(mod.wearer, COMSIG_LIVING_CAN_TRACK, PROC_REF(can_track)) + +/obj/item/mod/module/welding/camera_vision/on_suit_deactivation(deleting = FALSE) + . = ..() + UnregisterSignal(mod.wearer, COMSIG_LIVING_CAN_TRACK) + +/obj/item/mod/module/welding/camera_vision/proc/can_track(datum/source, mob/user) + SIGNAL_HANDLER + + return COMPONENT_CANT_TRACK + +//Ninja Star Dispenser - Dispenses ninja stars. +/obj/item/mod/module/dispenser/ninja + name = "MOD ninja star dispenser module" + desc = "This piece of Spider Clan technology can exploit known energy-matter equivalence principles, \ + using the nanites already hosted in the wearer's suit to transmute into monomolecular shuriken. \ + While these lack the intense bleeding edge of conventional throwing stars, \ + they have been set to electrify fleeing targets; and branded with the Spider Clan symbol." + dispense_type = /obj/item/throwing_star/stamina + cooldown_time = 0.5 SECONDS + +///Hacker - This module hooks onto your right-clicks with empty hands and causes ninja actions. +/obj/item/mod/module/hacker + name = "MOD hacker module" + desc = "Built for one purpose, electronic warfare, this module is built into the hands. \ + Using near-field communication alongside precise electro-stimulation of the wires in machines, \ + this decker's dream is normally used to pass through doors like a phantom. \ + It's also capable of non-precise electro-stimulation of an assassin-saboteur's opponents on disarming attacks." + icon_state = "hacker" + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/hacker) + /// Minimum amount of power we can drain in a single drain action + var/mindrain = 200 + /// Maximum amount of power we can drain in a single drain action + var/maxdrain = 400 + /// Whether or not the communication console hack was used to summon another antagonist. + var/communication_console_hack_success = FALSE + /// How many times the module has been used to force open doors. + var/door_hack_counter = 0 + ///Used for the research objective (see antagonist file) + var/datum/techweb/stored_research + +/obj/item/mod/module/hacker/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, PROC_REF(hack)) + +/obj/item/mod/module/hacker/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) + +/obj/item/mod/module/hacker/proc/hack(mob/living/carbon/human/source, atom/target, proximity, modifiers) + SIGNAL_HANDLER + + if(!LAZYACCESS(modifiers, RIGHT_CLICK) || !proximity) + return NONE + target.add_fingerprint(mod.wearer) + return target.ninjadrain_act(mod.wearer, src) + +/obj/item/mod/module/hacker/proc/charge_message(atom/drained_atom, drain_amount) + if(drain_amount) + to_chat(mod.wearer, span_notice("Получено [drain_amount] единиц энергии с [drained_atom].")) + else + to_chat(mod.wearer, span_warning("[drained_atom] истощен, необходимо найти другой источник питания!")) + +///Weapon Recall - Teleports your katana to you, prevents gun use. +/obj/item/mod/module/weapon_recall + name = "MOD weapon recall module" + desc = "The cornerstone of a clanmember's life as a blademaster, and a module symbolizing their eternal bond with their weapon. \ + This hooks to the micro bluespace drive inside an energy katana's handle, capable of recalling it to the user's \ + skilled hands wherever they are. However, those that make such a bond with their weapon are cursed to \ + fusing their existence with acts of combat, with a singular purpose; Cutting Down Their Opponent. \ + Their hand a hand that is cutting, their body a body that is cutting, their mind, a mind that is cutting. \ + Ranged weapons are forbidden." + icon_state = "recall" + removable = FALSE + module_type = MODULE_USABLE + use_power_cost = DEFAULT_CHARGE_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/weapon_recall) + cooldown_time = 0.5 SECONDS + /// The item linked to the module that will get recalled. + var/obj/item/linked_weapon + /// The accepted typepath we can link to. + var/accepted_type = /obj/item/energy_katana + +/obj/item/mod/module/weapon_recall/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_NOGUNS, MOD_TRAIT) + +/obj/item/mod/module/weapon_recall/on_suit_deactivation(deleting = FALSE) + REMOVE_TRAIT(mod.wearer, TRAIT_NOGUNS, MOD_TRAIT) + +/obj/item/mod/module/weapon_recall/on_use() + . = ..() + if(!.) + return + if(!linked_weapon) + var/obj/item/weapon_to_link = mod.wearer.is_holding_item_of_type(accepted_type) + if(!weapon_to_link) + balloon_alert(mod.wearer, "can't locate weapon!") + return + set_weapon(weapon_to_link) + balloon_alert(mod.wearer, "[linked_weapon.name] linked") + return + if(linked_weapon in mod.wearer.get_all_contents()) + balloon_alert(mod.wearer, "already on self!") + return + var/distance = get_dist(mod.wearer, linked_weapon) + var/in_view = (linked_weapon in view(mod.wearer)) + if(!in_view && !drain_power(use_power_cost * distance)) + balloon_alert(mod.wearer, "not enough charge!") + return + linked_weapon.forceMove(linked_weapon.drop_location()) + if(in_view) + do_sparks(5, FALSE, linked_weapon) + mod.wearer.visible_message(span_danger("[linked_weapon] flies towards [mod.wearer]!"),span_warning("You hold out your hand and [linked_weapon] flies towards you!")) + linked_weapon.throw_at(mod.wearer, distance+1, linked_weapon.throw_speed, mod.wearer) + else + recall_weapon() + +/obj/item/mod/module/weapon_recall/proc/set_weapon(obj/item/weapon) + linked_weapon = weapon + RegisterSignal(linked_weapon, COMSIG_MOVABLE_IMPACT, PROC_REF(catch_weapon)) + RegisterSignal(linked_weapon, COMSIG_PARENT_QDELETING, PROC_REF(deleted_weapon)) + +/obj/item/mod/module/weapon_recall/proc/recall_weapon(caught = FALSE) + linked_weapon.forceMove(get_turf(src)) + var/alert = "" + if(mod.wearer.put_in_hands(linked_weapon)) + alert = "[linked_weapon.name] teleports to your hand" + else if(mod.wearer.equip_to_slot_if_possible(linked_weapon, ITEM_SLOT_BELT, disable_warning = TRUE)) + alert = "[linked_weapon.name] sheathes itself in your belt" + else + alert = "[linked_weapon.name] teleports under you" + if(caught) + if(mod.wearer.is_holding(linked_weapon)) + alert = "you catch [linked_weapon.name]" + else + alert = "[linked_weapon.name] lands under you" + else + do_sparks(5, FALSE, linked_weapon) + if(alert) + balloon_alert(mod.wearer, alert) + +/obj/item/mod/module/weapon_recall/proc/catch_weapon(obj/item/source, atom/hit_atom, datum/thrownthing/thrownthing) + SIGNAL_HANDLER + + if(!mod) + return + if(hit_atom != mod.wearer) + return + INVOKE_ASYNC(src, PROC_REF(recall_weapon), TRUE) + return COMPONENT_MOVABLE_IMPACT_NEVERMIND + +/obj/item/mod/module/weapon_recall/proc/deleted_weapon(obj/item/source) + SIGNAL_HANDLER + + linked_weapon = null + +//Reinforced DNA Lock - Gibs if wrong DNA, emp-proof. +/obj/item/mod/module/dna_lock/reinforced + name = "MOD reinforced DNA lock module" + desc = "A module which engages with the various locks and seals tied to the suit's systems, \ + enabling it to only be worn by someone corresponding with the user's exact DNA profile. \ + Due to utilizing a skintight dampening shield, this one is entirely sealed against electromagnetic interference; \ + it also dutifully protects the secrets of the Spider Clan from unknowing outsiders." + icon_state = "dnalock_ninja" + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + +/obj/item/mod/module/dna_lock/reinforced/on_mod_activation(datum/source, mob/user) + . = ..() + if(. != MOD_CANCEL_ACTIVATE || !isliving(user)) + return + var/mob/living/living_user = user + to_chat(living_user, span_danger("fATaL EERRoR: 382200-*#00CODE RED\nUNAUTHORIZED USE DETECteD\nCoMMENCING SUB-R0UTIN3 13...\nTERMInATING U-U-USER...")) + living_user.gib() + +/obj/item/mod/module/dna_lock/reinforced/on_emp(datum/source, severity) + return + +//EMP Pulse - In addition to normal shielding, can also launch an EMP itself. +/obj/item/mod/module/emp_shield/pulse + name = "MOD EMP pulse module" + desc = "This module is normally set to activate on dramatic gestures, inverting and expanding the suit's \ + EMP dampening shield to cause an electromagnetic pulse of its own. While this won't interfere with the wearer, \ + it will piss off everyone around them." + icon_state = "emp_pulse" + module_type = MODULE_USABLE + use_power_cost = DEFAULT_CHARGE_DRAIN * 10 + cooldown_time = 8 SECONDS + +/obj/item/mod/module/emp_shield/pulse/on_use() + . = ..() + if(!.) + return + playsound(src, 'sound/effects/empulse.ogg', 60, TRUE) + empulse(src, heavy_range = 4, light_range = 6) + drain_power(use_power_cost) + +///Status Readout - Puts a lot of information including health, nutrition, fingerprints, temperature to the suit TGUI. +/obj/item/mod/module/status_readout + name = "MOD status readout module" + desc = "A once-common module, this technology went unfortunately out of fashion; \ + and right into the arachnid grip of the Spider Clan. This hooks into the suit's spine, \ + capable of capturing and displaying all possible biometric data of the wearer; sleep, nutrition, fitness, fingerprints, \ + and even useful information such as their overall health and wellness." + icon_state = "status" + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.1 + incompatible_modules = list(/obj/item/mod/module/status_readout) + tgui_id = "status_readout" + +/obj/item/mod/module/status_readout/add_ui_data() + . = ..() + .["statustime"] = station_time_timestamp() + .["statusid"] = GLOB.round_id + .["statushealth"] = mod.wearer?.health || 0 + .["statusmaxhealth"] = mod.wearer?.getMaxHealth() || 0 + .["statusbrute"] = mod.wearer?.getBruteLoss() || 0 + .["statusburn"] = mod.wearer?.getFireLoss() || 0 + .["statustoxin"] = mod.wearer?.getToxLoss() || 0 + .["statusoxy"] = mod.wearer?.getOxyLoss() || 0 + .["statustemp"] = mod.wearer?.bodytemperature || 0 + .["statusnutrition"] = mod.wearer?.nutrition || 0 + //.["statusfingerprints"] = mod.wearer ? md5(mod.wearer.dna.unique_identity) : null + .["statusdna"] = mod.wearer?.dna.unique_enzymes + .["statusviruses"] = null + if(!length(mod.wearer?.diseases)) + return + var/list/viruses = list() + for(var/datum/disease/virus as anything in mod.wearer.diseases) + var/list/virus_data = list() + virus_data["name"] = virus.name + virus_data["type"] = virus.spread_text + virus_data["stage"] = virus.stage + virus_data["maxstage"] = virus.max_stages + virus_data["cure"] = virus.cure_text + viruses += list(virus_data) + .["statusviruses"] = viruses + +///Energy Net - Ensnares enemies in a net that prevents movement. +/obj/item/mod/module/energy_net + name = "MOD energy net module" + desc = "A custom-built net-thrower. While conventional implementations of this capturing device \ + tilize monomolecular fibers or cutting razorwire, this uses hardlight technology to deploy a \ + trapping field capable of immobilizing even the strongest opponents." + icon_state = "energy_net" + removable = FALSE + module_type = MODULE_ACTIVE + use_power_cost = DEFAULT_CHARGE_DRAIN * 6 + incompatible_modules = list(/obj/item/mod/module/energy_net) + cooldown_time = 1.5 SECONDS + +/obj/item/mod/module/energy_net/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!isliving(target)) + balloon_alert(mod.wearer, "invalid target!") + return + var/mob/living/living_target = target + if(locate(/obj/structure/energy_net) in get_turf(living_target)) + balloon_alert(mod.wearer, "already trapped!") + return + for(var/turf/between_turf as anything in get_line(get_turf(mod.wearer), get_turf(living_target))) + if(between_turf.density) + balloon_alert(mod.wearer, "not through obstacles!") + return + //if(IS_SPACE_NINJA(mod.wearer)) + // mod.wearer.say("Get over here!", forced = type) + mod.wearer.Beam(living_target, "n_beam", time = 1.5 SECONDS) + var/obj/structure/energy_net/net = new /obj/structure/energy_net(living_target.drop_location()) + net.affecting = living_target + mod.wearer.visible_message(span_danger("[mod.wearer] caught [living_target] with an energy net!"), span_notice("You caught [living_target] with an energy net!")) + if(living_target.buckled) + living_target.buckled.unbuckle_mob(living_target, force = TRUE) + net.buckle_mob(living_target, force = TRUE) + drain_power(use_power_cost) + +///Adrenaline Boost - Stops all stuns the ninja is affected with, increases his speed. +/obj/item/mod/module/adrenaline_boost + name = "MOD adrenaline boost module" + desc = "The secrets of the Spider Clan are many. The exact specifications of their suits, \ + the techniques they use to make every singular cut make their enemies weep with admiration, \ + but one of their greatest mysteries is the chemical compound their assassin-saboteurs use in times of need. \ + It's capable of clearing any fatigue whatsoever from the user, any immobilizing effect, and can even \ + cure total paralysis. All that's known is that the fluid requires radiation to properly 'cook,' \ + so this module demands radium to be refilled with." + icon_state = "adrenaline_boost" + removable = FALSE + module_type = MODULE_USABLE + incompatible_modules = list(/obj/item/mod/module/adrenaline_boost) + cooldown_time = 12 SECONDS + /// What reagent we need to refill? + var/reagent_required = /datum/reagent/uranium/radium + /// How much of a reagent we need to refill the boost. + var/reagent_required_amount = 20 + +/obj/item/mod/module/adrenaline_boost/Initialize(mapload) + . = ..() + create_reagents(reagent_required_amount) + reagents.add_reagent(reagent_required, reagent_required_amount) + +/obj/item/mod/module/adrenaline_boost/on_use() + if(!reagents.has_reagent(reagent_required, reagent_required_amount)) + balloon_alert(mod.wearer, "no charge!") + return + . = ..() + if(!.) + return + //if(IS_SPACE_NINJA(mod.wearer)) + // mod.wearer.say(pick_list_replacements(NINJA_FILE, "lines"), forced = type) + to_chat(mod.wearer, span_notice("You have used the adrenaline boost.")) + mod.wearer.SetUnconscious(0) + mod.wearer.SetStun(0) + mod.wearer.SetKnockdown(0) + mod.wearer.SetImmobilized(0) + mod.wearer.SetParalyzed(0) + mod.wearer.adjustStaminaLoss(-200) + mod.wearer.stuttering = 0 + mod.wearer.reagents.add_reagent(/datum/reagent/medicine/stimulants, 5) + reagents.remove_reagent(reagent_required, reagents.total_volume * 0.75) + addtimer(CALLBACK(src, PROC_REF(boost_aftereffects), mod.wearer), 7 SECONDS) + +/obj/item/mod/module/adrenaline_boost/on_install() + RegisterSignal(mod, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) + +/obj/item/mod/module/adrenaline_boost/on_uninstall(deleting) + UnregisterSignal(mod, COMSIG_PARENT_ATTACKBY) + +/obj/item/mod/module/adrenaline_boost/attackby(obj/item/attacking_item, mob/user, params) + if(charge_boost(attacking_item, user)) + return TRUE + return ..() + +/obj/item/mod/module/adrenaline_boost/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user) + SIGNAL_HANDLER + + if(charge_boost(attacking_item, user)) + return COMPONENT_NO_AFTERATTACK + return NONE + +/obj/item/mod/module/adrenaline_boost/proc/charge_boost(obj/item/attacking_item, mob/user) + if(!attacking_item.is_open_container()) + return FALSE + if(reagents.has_reagent(reagent_required, reagent_required_amount)) + balloon_alert(mod.wearer, "already charged!") + return FALSE + if(!attacking_item.reagents.trans_id_to(src, reagent_required, reagent_required_amount)) + return FALSE + balloon_alert(mod.wearer, "charge [reagents.has_reagent(reagent_required, reagent_required_amount) ? "fully" : "partially"] reloaded") + return TRUE + +/obj/item/mod/module/adrenaline_boost/proc/boost_aftereffects(mob/affected_mob) + if(!affected_mob) + return + reagents.trans_to(affected_mob, reagents.total_volume) + to_chat(affected_mob, span_danger("You are beginning to feel the after-effect of the injection.")) diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm new file mode 100644 index 000000000000..02025ea1b420 --- /dev/null +++ b/code/modules/mod/modules/modules_science.dm @@ -0,0 +1,132 @@ +//Science modules for MODsuits + +///Reagent Scanner - Lets the user scan reagents. +/obj/item/mod/module/reagent_scanner + name = "MOD reagent scanner module" + desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \ + containers and projecting the information in an easy-to-read format on the wearer's display. \ + It cannot detect flavors, so that's up to you." + icon_state = "scanner" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/reagent_scanner) + cooldown_time = 0.5 SECONDS + +/obj/item/mod/module/reagent_scanner/on_activation() + . = ..() + if(!.) + return + mod.helmet.clothing_flags |= SCAN_REAGENTS + +/obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + mod.helmet.clothing_flags &= ~SCAN_REAGENTS + +/obj/item/mod/module/reagent_scanner/advanced + name = "MOD advanced reagent scanner module" + complexity = 0 + removable = FALSE + var/explosion_detection_dist = 21 + +/obj/item/mod/module/reagent_scanner/advanced/on_activation() + . = ..() + if(!.) + return + mod.helmet.clothing_flags |= SCAN_REAGENTS + RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, PROC_REF(sense_explosion)) + +/obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + mod.helmet.clothing_flags |= SCAN_REAGENTS + UnregisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION) + +/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter, + devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) + SIGNAL_HANDLER + var/turf/wearer_turf = get_turf(mod.wearer) + if(wearer_turf.z != epicenter.z) + return + if(get_dist(epicenter, wearer_turf) > explosion_detection_dist) + return + to_chat(mod.wearer, span_notice("Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]")) + +///Anti-Gravity - Makes the user weightless. +/obj/item/mod/module/anomaly_locked/antigrav + name = "MOD anti-gravity module" + desc = "A module that uses a gravitational core to make the user completely weightless." + icon_state = "antigrav" + module_type = MODULE_TOGGLE + complexity = 3 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.7 + incompatible_modules = list(/obj/item/mod/module/anomaly_locked) + cooldown_time = 0.5 SECONDS + accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) + +/obj/item/mod/module/anomaly_locked/antigrav/on_activation() + . = ..() + if(!.) + return + if(mod.wearer.has_gravity()) + new /obj/effect/temp_visual/mook_dust(get_turf(src)) + mod.wearer.AddElement(/datum/element/forced_gravity, 0) + mod.wearer.update_gravity(mod.wearer.has_gravity()) + playsound(src, 'sound/effects/gravhit.ogg', 50) + +/obj/item/mod/module/anomaly_locked/antigrav/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + mod.wearer.RemoveElement(/datum/element/forced_gravity, 0) + mod.wearer.update_gravity(mod.wearer.has_gravity()) + if(deleting) + return + if(mod.wearer.has_gravity()) + new /obj/effect/temp_visual/mook_dust(get_turf(src)) + playsound(src, 'sound/effects/gravhit.ogg', 50) + +/obj/item/mod/module/anomaly_locked/antigrav/prebuilt + prebuilt = TRUE + +///Teleporter - Lets the user teleport to a nearby location. +/obj/item/mod/module/anomaly_locked/teleporter + name = "MOD teleporter module" + desc = "A module that uses a bluespace core to let the user transport their particles elsewhere." + icon_state = "teleporter" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN * 5 + cooldown_time = 5 SECONDS + accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace) + /// Time it takes to teleport + var/teleport_time = 3 SECONDS + +/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target) + . = ..() + if(!.) + return + var/turf/open/target_turf = get_turf(target) + if(!istype(target_turf) || target_turf.is_blocked_turf() || !(target_turf in view(mod.wearer))) + balloon_alert(mod.wearer, "invalid target!") + return + balloon_alert(mod.wearer, "teleporting...") + var/matrix/pre_matrix = matrix() + pre_matrix.Scale(4, 0.25) + var/matrix/post_matrix = matrix() + post_matrix.Scale(0.25, 4) + animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = pre_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT) + if(!do_after(mod.wearer, teleport_time, target = mod)) + balloon_alert(mod.wearer, "interrupted!") + animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT) + return + animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT) + if(!do_teleport(mod.wearer, target_turf, asoundin = 'sound/effects/phasein.ogg')) + return + drain_power(use_power_cost) + +/obj/item/mod/module/anomaly_locked/teleporter/prebuilt + prebuilt = TRUE diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm new file mode 100644 index 000000000000..d3ac53846461 --- /dev/null +++ b/code/modules/mod/modules/modules_security.dm @@ -0,0 +1,136 @@ +//Security modules for MODsuits + +///Magnetic Harness - Automatically puts guns in your suit storage when you drop them. +/obj/item/mod/module/magnetic_harness + name = "MOD magnetic harness module" + desc = "Based off old TerraGov harness kits, this magnetic harness automatically attaches dropped guns back to the wearer." + icon_state = "mag_harness" + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/magnetic_harness) + /// Time before we activate the magnet. + var/magnet_delay = 0.8 SECONDS + /// The typecache of all guns we allow. + var/static/list/guns_typecache + /// The guns already allowed by the modsuit chestplate. + var/list/already_allowed_guns = list() + +/obj/item/mod/module/magnetic_harness/Initialize(mapload) + . = ..() + if(!guns_typecache) + guns_typecache = typecacheof(list(/obj/item/gun/ballistic, /obj/item/gun/energy, /obj/item/gun/grenadelauncher, /obj/item/gun/chem, /obj/item/gun/syringe)) + +/obj/item/mod/module/magnetic_harness/on_install() + already_allowed_guns = guns_typecache & mod.chestplate.allowed + mod.chestplate.allowed |= guns_typecache + +/obj/item/mod/module/magnetic_harness/on_uninstall(deleting = FALSE) + if(deleting) + return + mod.chestplate.allowed -= (guns_typecache - already_allowed_guns) + +/obj/item/mod/module/magnetic_harness/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(check_dropped_item)) + +/obj/item/mod/module/magnetic_harness/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM) + +/obj/item/mod/module/magnetic_harness/proc/check_dropped_item(datum/source, obj/item/dropped_item, force, new_location) + SIGNAL_HANDLER + + if(!is_type_in_typecache(dropped_item, guns_typecache)) + return + if(new_location != get_turf(src)) + return + addtimer(CALLBACK(src, PROC_REF(pick_up_item), dropped_item), magnet_delay) + +/obj/item/mod/module/magnetic_harness/proc/pick_up_item(obj/item/item) + if(!isturf(item.loc) || !item.Adjacent(mod.wearer)) + return + if(!mod.wearer.equip_to_slot_if_possible(item, ITEM_SLOT_SUITSTORE, qdel_on_fail = FALSE, disable_warning = TRUE)) + return + playsound(src, 'sound/items/modsuit/magnetic_harness.ogg', 50, TRUE) + balloon_alert(mod.wearer, "[item] reattached") + drain_power(use_power_cost) + +///Holster - Instantly holsters any not huge gun. +/obj/item/mod/module/holster + name = "MOD holster module" + desc = "Based off typical storage compartments, this system allows the suit to holster a \ + standard firearm across its surface and allow for extremely quick retrieval. \ + While some users prefer the chest, others the forearm for quick deployment, \ + some law enforcement prefer the holster to extend from the thigh." + icon_state = "holster" + module_type = MODULE_USABLE + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/holster) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// Gun we have holstered. + var/obj/item/gun/holstered + +/obj/item/mod/module/holster/on_use() + . = ..() + if(!.) + return + if(!holstered) + var/obj/item/gun/holding = mod.wearer.get_active_held_item() + if(!holding) + balloon_alert(mod.wearer, "nothing to holster!") + return + if(!istype(holding) || holding.w_class > WEIGHT_CLASS_BULKY) + balloon_alert(mod.wearer, "it doesn't fit!") + return + if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE)) + holstered = holding + balloon_alert(mod.wearer, "weapon holstered") + playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) + else if(mod.wearer.put_in_active_hand(holstered, forced = FALSE, ignore_animation = TRUE)) + balloon_alert(mod.wearer, "weapon drawn") + playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) + else + balloon_alert(mod.wearer, "holster full!") + +/obj/item/mod/module/holster/on_uninstall(deleting = FALSE) + if(holstered) + holstered.forceMove(drop_location()) + +/obj/item/mod/module/holster/Exited(atom/movable/gone, direction) + . = ..() + if(gone == holstered) + holstered = null + +/obj/item/mod/module/holster/Destroy() + QDEL_NULL(holstered) + return ..() + +///Megaphone - Lets you speak loud. +/obj/item/mod/module/megaphone + name = "MOD megaphone module" + desc = "A microchip megaphone linked to a MODsuit, for very important purposes, like: loudness." + icon_state = "megaphone" + module_type = MODULE_TOGGLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/megaphone) + cooldown_time = 0.5 SECONDS + /// List of spans we add to the speaker. + var/list/voicespan = list(SPAN_COMMAND) + +/obj/item/mod/module/megaphone/on_activation() + . = ..() + if(!.) + return + RegisterSignal(mod.wearer, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/obj/item/mod/module/megaphone/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + UnregisterSignal(mod.wearer, COMSIG_MOB_SAY) + +/obj/item/mod/module/megaphone/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + speech_args[SPEECH_SPANS] |= voicespan + drain_power(use_power_cost) diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm new file mode 100644 index 000000000000..e983bbc3dbc0 --- /dev/null +++ b/code/modules/mod/modules/modules_service.dm @@ -0,0 +1,56 @@ +//Service modules for MODsuits + +///Bike Horn - Plays a bike horn sound. +/obj/item/mod/module/bikehorn + name = "MOD bike horn module" + desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \ + precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound." + icon_state = "bikehorn" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/bikehorn) + cooldown_time = 1 SECONDS + +/obj/item/mod/module/bikehorn/on_use() + . = ..() + if(!.) + return + playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE) + drain_power(use_power_cost) + +///Microwave Beam - Microwaves items instantly. +/obj/item/mod/module/microwave_beam + name = "MOD microwave beam module" + desc = "An oddly domestic device, this module is installed into the user's palm, \ + hooking up with culinary scanners located in the helmet to blast food with precise microwave radiation, \ + allowing them to cook food from a distance, with the greatest of ease. Not recommended for use against grapes." + icon_state = "microwave_beam" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 5 + incompatible_modules = list(/obj/item/mod/module/microwave_beam) + cooldown_time = 10 SECONDS + +/obj/item/mod/module/microwave_beam/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!istype(target, /obj/item)) + return + if(!isturf(target.loc)) + balloon_alert(mod.wearer, "must be on the floor!") + return + var/obj/item/microwave_target = target + var/datum/effect_system/spark_spread/spark_effect = new() + spark_effect.set_up(2, 1, mod.wearer) + spark_effect.start() + mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5) + if(microwave_target.microwave_act()) + playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE) + else + balloon_alert(mod.wearer, "can't be microwaved!") + var/datum/effect_system/spark_spread/spark_effect_two = new() + spark_effect_two.set_up(2, 1, microwave_target) + spark_effect_two.start() + drain_power(use_power_cost) diff --git a/code/modules/mod/modules/modules_storage.dm b/code/modules/mod/modules/modules_storage.dm new file mode 100644 index 000000000000..25caad6806f7 --- /dev/null +++ b/code/modules/mod/modules/modules_storage.dm @@ -0,0 +1,60 @@ +/obj/item/mod/module/storage + name = "MOD storage module" + desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \ + the surface of the suit, useful for storing various bits, and or bobs." + icon_state = "storage" + complexity = 3 + incompatible_modules = list(/obj/item/mod/module/storage) + var/datum/component/storage/concrete/storage + var/max_w_class = WEIGHT_CLASS_NORMAL + var/max_combined_w_class = 15 + var/max_items = 7 + +/obj/item/mod/module/storage/Initialize(mapload) + . = ..() + storage = AddComponent(/datum/component/storage/concrete) + storage.max_w_class = max_w_class + storage.max_combined_w_class = max_combined_w_class + storage.max_items = max_items + storage.allow_big_nesting = TRUE + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) + +/obj/item/mod/module/storage/on_install() + var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage) + modstorage.max_w_class = max_w_class + modstorage.max_combined_w_class = max_combined_w_class + modstorage.max_items = max_items + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE) + +/obj/item/mod/module/storage/on_uninstall(deleting = FALSE) + var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage) + storage.slaves -= modstorage + qdel(modstorage) + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) + +/obj/item/mod/module/storage/large_capacity + name = "MOD expanded storage module" + desc = "Reverse engineered by Nakamura Engineering from Donk Corporation designs, this system of hidden compartments \ + is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \ + whether smuggling, or simply hauling." + icon_state = "storage_large" + max_combined_w_class = 21 + max_items = 14 + +/obj/item/mod/module/storage/syndicate + name = "MOD syndicate storage module" + desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \ + esoteric technology to compress the physical matter of items put inside of them, \ + essentially shrinking items for much easier and more portable storage." + icon_state = "storage_syndi" + max_combined_w_class = 30 + max_items = 21 + +/obj/item/mod/module/storage/bluespace + name = "MOD bluespace storage module" + desc = "A storage system developed by Nanotrasen, these compartments employ \ + miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside." + icon_state = "storage_large" + max_w_class = WEIGHT_CLASS_GIGANTIC + max_combined_w_class = 60 + max_items = 21 diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm new file mode 100644 index 000000000000..04f0aaf73ad1 --- /dev/null +++ b/code/modules/mod/modules/modules_supply.dm @@ -0,0 +1,306 @@ +//Supply modules for MODsuits + +///Internal GPS - Extends a GPS you can use. +/obj/item/mod/module/gps + name = "MOD internal GPS module" + desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \ + down to the exact coordinates. This information is fed to a central database viewable from the device itself, \ + though using it to help people is up to you." + icon_state = "gps" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/gps) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + +/obj/item/mod/module/gps/Initialize(mapload) + . = ..() + AddComponent(/datum/component/gps/item, "MOD0") + +/obj/item/mod/module/gps/on_use() + . = ..() + if(!.) + return + attack_self(mod.wearer) + +///Hydraulic Clamp - Lets you pick up and drop crates. +/obj/item/mod/module/clamp + name = "MOD hydraulic clamp module" + desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \ + However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \ + A lot of people would say that loading cargo is a dull job, but you could not disagree more." + icon_state = "clamp" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/clamp) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_clamp" + overlay_state_active = "module_clamp_on" + /// Time it takes to load a crate. + var/load_time = 3 SECONDS + /// The max amount of crates you can carry. + var/max_crates = 3 + /// The crates stored in the module. + var/list/stored_crates = list() + +/obj/item/mod/module/clamp/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!mod.wearer.Adjacent(target)) + return + if(istype(target, /obj/structure/closet/crate))// || istype(target, /obj/item/delivery/big)) + var/atom/movable/picked_crate = target + if(!check_crate_pickup(picked_crate)) + return + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + if(!do_after(mod.wearer, load_time, target = target)) + balloon_alert(mod.wearer, "interrupted!") + return + if(!check_crate_pickup(picked_crate)) + return + stored_crates += picked_crate + picked_crate.forceMove(src) + balloon_alert(mod.wearer, "picked up [picked_crate]") + drain_power(use_power_cost) + mod.wearer.update_inv_back() + else if(length(stored_crates)) + var/turf/target_turf = get_turf(target) + if(target_turf.is_blocked_turf()) + return + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + if(!do_after(mod.wearer, load_time, target = target)) + balloon_alert(mod.wearer, "interrupted!") + return + if(target_turf.is_blocked_turf()) + return + var/atom/movable/dropped_crate = pop(stored_crates) + dropped_crate.forceMove(target_turf) + balloon_alert(mod.wearer, "dropped [dropped_crate]") + drain_power(use_power_cost) + mod.wearer.update_inv_back() + else + balloon_alert(mod.wearer, "invalid target!") + +/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE) + if(deleting) + return + for(var/atom/movable/crate as anything in stored_crates) + crate.forceMove(drop_location()) + stored_crates -= crate + +/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target) + if(length(stored_crates) >= max_crates) + balloon_alert(mod.wearer, "too many crates!") + return FALSE + for(var/mob/living/mob in target.get_all_contents()) + if(mob.mob_size < MOB_SIZE_HUMAN) + continue + balloon_alert(mod.wearer, "crate too heavy!") + return FALSE + return TRUE + +/obj/item/mod/module/clamp/loader + name = "MOD loader hydraulic clamp module" + icon_state = "clamp_loader" + complexity = 0 + removable = FALSE + overlay_state_inactive = null + overlay_state_active = "module_clamp_loader" + load_time = 1 SECONDS + max_crates = 5 + use_mod_colors = TRUE + +///Drill - Lets you dig through rock and basalt. +/obj/item/mod/module/drill + name = "MOD drill module" + desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \ + your drill is surely the one that both pierces and creates the heavens." + icon_state = "drill" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/drill) + cooldown_time = 0.5 SECONDS + overlay_state_active = "module_drill" + +/obj/item/mod/module/drill/on_activation() + . = ..() + if(!.) + return + RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine)) + +/obj/item/mod/module/drill/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP) + +/obj/item/mod/module/drill/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!mod.wearer.Adjacent(target)) + return + if(istype(target, /turf/closed/mineral)) + var/turf/closed/mineral/mineral_turf = target + mineral_turf.gets_drilled(mod.wearer) + drain_power(use_power_cost) + else if(istype(target, /turf/open/floor/plating/asteroid)) + var/turf/open/floor/plating/asteroid/sand_turf = target + if(!sand_turf.can_dig(mod.wearer)) + return + sand_turf.getDug() + drain_power(use_power_cost) + +/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity) + SIGNAL_HANDLER + if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost)) + return + var/turf/closed/mineral/mineral_turf = bumped_into + mineral_turf.gets_drilled(mod.wearer) + return COMPONENT_CANCEL_ATTACK_CHAIN + +///Ore Bag - Lets you pick up ores and drop them from the suit. +/obj/item/mod/module/orebag + name = "MOD ore bag module" + desc = "An integrated ore storage system installed into the suit, \ + this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \ + It's recommended by Nakamura Engineering to actually deposit that ore at local refineries." + icon_state = "ore" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/orebag) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// The ores stored in the bag. + var/list/ores = list() + +/obj/item/mod/module/orebag/on_equip() + RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(ore_pickup)) + +/obj/item/mod/module/orebag/on_unequip() + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) + +/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced) + SIGNAL_HANDLER + + for(var/obj/item/stack/ore/ore in get_turf(mod.wearer)) + INVOKE_ASYNC(src, PROC_REF(move_ore), ore) + playsound(src, SFX_RUSTLE, 50, TRUE) + +/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore) + for(var/obj/item/stack/stored_ore as anything in ores) + if(!ore.can_merge(stored_ore)) + continue + ore.merge(stored_ore) + if(QDELETED(ore)) + return + break + ore.forceMove(src) + ores += ore + +/obj/item/mod/module/orebag/on_use() + . = ..() + if(!.) + return + for(var/obj/item/ore as anything in ores) + ore.forceMove(drop_location()) + ores -= ore + drain_power(use_power_cost) + +/obj/item/mod/module/disposal_connector + name = "MOD disposal selector module" + desc = "A module that connects to the disposal pipeline, causing the user to go into their config selected disposal. \ + Only seems to work when the suit is on." + icon_state = "disposal" + complexity = 2 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/disposal_connector) + var/disposal_tag = NONE + +/obj/item/mod/module/disposal_connector/Initialize(mapload) + . = ..() + disposal_tag = pick(GLOB.TAGGERLOCATIONS) + +/obj/item/mod/module/disposal_connector/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_MOVABLE_DISPOSING, PROC_REF(disposal_handling)) + +/obj/item/mod/module/disposal_connector/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_DISPOSING) + +/obj/item/mod/module/disposal_connector/get_configuration() + . = ..() + .["disposal_tag"] = add_ui_configuration("Disposal Tag", "list", GLOB.TAGGERLOCATIONS[disposal_tag], GLOB.TAGGERLOCATIONS) + +/obj/item/mod/module/disposal_connector/configure_edit(key, value) + switch(key) + if("disposal_tag") + for(var/tag in 1 to length(GLOB.TAGGERLOCATIONS)) + if(GLOB.TAGGERLOCATIONS[tag] == value) + disposal_tag = tag + break + +/obj/item/mod/module/disposal_connector/proc/disposal_handling(datum/disposal_source, obj/structure/disposalholder/disposal_holder, obj/machinery/disposal/disposal_machine, hasmob) + SIGNAL_HANDLER + + disposal_holder.destinationTag = disposal_tag + +/obj/item/mod/module/magnet + name = "MOD loader hydraulic magnet module" + desc = "A powerful hydraulic electromagnet able to launch crates and lockers towards the user, and keep 'em attached." + icon_state = "magnet_loader" + module_type = MODULE_ACTIVE + removable = FALSE + use_power_cost = DEFAULT_CHARGE_DRAIN*3 + incompatible_modules = list(/obj/item/mod/module/magnet) + cooldown_time = 1.5 SECONDS + overlay_state_active = "module_magnet" + use_mod_colors = TRUE + +/obj/item/mod/module/magnet/on_select_use(atom/target) + . = ..() + if(!.) + return + if(istype(mod.wearer.pulling, /obj/structure/closet)) + var/obj/structure/closet/locker = mod.wearer.pulling + playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE) + locker.forceMove(mod.wearer.loc) + locker.throw_at(target, range = 7, speed = 4, thrower = mod.wearer) + return + if(!istype(target, /obj/structure/closet) || !(target in view(mod.wearer))) + balloon_alert(mod.wearer, "invalid target!") + return + var/obj/structure/closet/locker = target + if(locker.anchored || locker.move_resist >= MOVE_FORCE_OVERPOWERING) + balloon_alert(mod.wearer, "target anchored!") + return + new /obj/effect/temp_visual/mook_dust(get_turf(locker)) + playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE) + locker.throw_at(mod.wearer, range = 7, speed = 3, force = MOVE_FORCE_WEAK, \ + callback = CALLBACK(src, PROC_REF(check_locker), locker)) + +/obj/item/mod/module/magnet/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + if(istype(mod.wearer.pulling, /obj/structure/closet)) + mod.wearer.stop_pulling() + +/obj/item/mod/module/magnet/proc/check_locker(obj/structure/closet/locker) + if(!mod?.wearer) + return + if(!locker.Adjacent(mod.wearer) || !isturf(locker.loc) || !isturf(mod.wearer.loc)) + return + mod.wearer.start_pulling(locker) + //locker.strong_grab = TRUE + RegisterSignal(locker, COMSIG_ATOM_NO_LONGER_PULLED, PROC_REF(on_stop_pull)) + +/obj/item/mod/module/magnet/proc/on_stop_pull(obj/structure/closet/locker, atom/movable/last_puller) + SIGNAL_HANDLER + + //locker.strong_grab = FALSE + UnregisterSignal(locker, COMSIG_ATOM_NO_LONGER_PULLED) diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm new file mode 100644 index 000000000000..e1516c2aa0a1 --- /dev/null +++ b/code/modules/mod/modules/modules_visor.dm @@ -0,0 +1,85 @@ +//Visor modules for MODsuits + +///Base Visor - Adds a specific HUD and traits to you. +/obj/item/mod/module/visor + name = "MOD visor module" + desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you." + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/visor) + cooldown_time = 0.5 SECONDS + /// The HUD type given by the visor. + var/hud_type + /// The traits given by the visor. + var/list/visor_traits = list() + +/obj/item/mod/module/visor/on_activation() + . = ..() + if(!.) + return + if(hud_type) + var/datum/atom_hud/hud = GLOB.huds[hud_type] + hud.add_hud_to(mod.wearer) + for(var/trait in visor_traits) + ADD_TRAIT(mod.wearer, trait, MOD_TRAIT) + mod.wearer.update_sight() + +/obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + if(hud_type) + var/datum/atom_hud/hud = GLOB.huds[hud_type] + hud.remove_hud_from(mod.wearer) + for(var/trait in visor_traits) + REMOVE_TRAIT(mod.wearer, trait, MOD_TRAIT) + mod.wearer.update_sight() + +//Medical Visor - Gives you a medical HUD. +/obj/item/mod/module/visor/medhud + name = "MOD medical visor module" + desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \ + biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \ + access data such as patient files in a convenient readout. They say these also let you see behind you." + icon_state = "medhud_visor" + hud_type = DATA_HUD_MEDICAL_ADVANCED + visor_traits = list(TRAIT_MEDICAL_HUD) + +//Diagnostic Visor - Gives you a diagnostic HUD. +/obj/item/mod/module/visor/diaghud + name = "MOD diagnostic visor module" + desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \ + from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \ + and integrity of such. They say these also let you see behind you." + icon_state = "diaghud_visor" + hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED + visor_traits = list(TRAIT_DIAGNOSTIC_HUD) + +//Security Visor - Gives you a security HUD. +/obj/item/mod/module/visor/sechud + name = "MOD security visor module" + desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \ + plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \ + and generally know who to shoot. They say these also let you see behind you." + icon_state = "sechud_visor" + hud_type = DATA_HUD_SECURITY_ADVANCED + visor_traits = list(TRAIT_SECURITY_HUD) + +//Meson Visor - Gives you meson vision. +/obj/item/mod/module/visor/meson + name = "MOD meson visor module" + desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \ + technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \ + through walls, regardless of lighting conditions. They say these also let you see behind you." + icon_state = "meson_visor" + visor_traits = list(SEE_TURFS) + +//Thermal Visor - Gives you thermal vision. +/obj/item/mod/module/visor/thermal + name = "MOD thermal visor module" + desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \ + the thermal radiation output of objects near the user. While it can detect the heat output of even something as \ + small as a rodent, it still produces irritating red overlay. They say these also let you see behind you." + icon_state = "thermal_visor" + visor_traits = list(SEE_MOBS) diff --git a/code/modules/movespeed/modifiers/items.dm b/code/modules/movespeed/modifiers/items.dm index c858582af6a3..4c967a58a7ec 100644 --- a/code/modules/movespeed/modifiers/items.dm +++ b/code/modules/movespeed/modifiers/items.dm @@ -18,3 +18,5 @@ /datum/movespeed_modifier/berserk multiplicative_slowdown = -0.2 +/datum/movespeed_modifier/sphere + multiplicative_slowdown = -0.5 diff --git a/code/modules/overmap/objects/event_datum.dm b/code/modules/overmap/objects/event_datum.dm index bfed840a1acd..d798fd74778a 100644 --- a/code/modules/overmap/objects/event_datum.dm +++ b/code/modules/overmap/objects/event_datum.dm @@ -194,55 +194,25 @@ chance_to_affect = 100 ///The currently linked wormhole var/datum/overmap/event/wormhole/other_wormhole - ///Amount of times a ship can pass through before it collapses - var/stability /datum/overmap/event/wormhole/Initialize(position, _other_wormhole, ...) . = ..() - stability = rand(1, 5) if(_other_wormhole) other_wormhole = _other_wormhole if(!other_wormhole) other_wormhole = new(null, src) //Create a new wormhole at a random location - token.color = adjust_colors() - token.light_color = adjust_colors() + token.color = "#6d80c7" + token.light_color = "#6d80c7" token.update_appearance() /datum/overmap/event/wormhole/affect_ship(datum/overmap/ship/controlled/S) if(!other_wormhole) qdel(src) - if(--stability <= 0) - var/list/results = SSovermap.get_unused_overmap_square() - S.overmap_move(results["x"], results["y"]) - QDEL_NULL(other_wormhole) - for(var/MN in GLOB.player_list) - var/mob/M = MN - if(S.shuttle_port.is_in_shuttle_bounds(M)) - M.playsound_local(S.shuttle_port, 'sound/effects/explosionfar.ogg', 100) - shake_camera(M, 10, 10) - - return qdel(src) - other_wormhole.stability = stability + return S.overmap_move(other_wormhole.x, other_wormhole.y) S.overmap_step(S.get_heading()) - token.color = adjust_colors() - token.light_color = adjust_colors() - -/datum/overmap/event/wormhole/proc/adjust_colors() - switch(stability) - if(1) - return "#753214" - if(2) - return "#642f19" - if(3) - return"#654650" - if(4) - return"#5c5d8b" - if(5) - return"#6d80c7" - //Carp "meteors" - throws carp at the ship /datum/overmap/event/meteor/carp diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index 93b8319dbed7..e91a03e421f3 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -20,6 +20,7 @@ slot_flags = ITEM_SLOT_NECK custom_materials = list(/datum/material/iron = 50, /datum/material/glass = 150) custom_price = 120 + supports_variations = VOX_VARIATION var/flash_enabled = TRUE var/state_on = "camera" var/state_off = "camera_off" diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 8d711ad804fa..c9367ceecfbe 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -13,6 +13,7 @@ var/lastgen = 0 var/lastgenlev = -1 + var/max_efficiency = 0.45 /obj/machinery/power/generator/Initialize(mapload) . = ..() @@ -75,12 +76,13 @@ if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0) - var/efficiency = 0.45 //WS Edit - Nerfed down to Cit's efficiency + var/efficiency = LOGISTIC_FUNCTION(max_efficiency,0.0009,delta_temperature,10000) + //2nd (0.0009) effects how 'steep' the curve is, and 4th (10000) effects where the 'middle' is. var/energy_transfer = delta_temperature*hot_air_heat_capacity*cold_air_heat_capacity/(hot_air_heat_capacity+cold_air_heat_capacity) + lastgen += energy_transfer * efficiency var/heat = energy_transfer*(1-efficiency) - lastgen += LOGISTIC_FUNCTION(500000,0.0009,delta_temperature,10000) //WS Edit - Buries the 3x3 freezer heater TEG into the ground hot_air.set_temperature(hot_air.return_temperature() - energy_transfer/hot_air_heat_capacity) cold_air.set_temperature(cold_air.return_temperature() + heat/cold_air_heat_capacity) diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm index 154a6c77a7f3..786005aa5ca1 100644 --- a/code/modules/projectiles/ammunition/ballistic/rifle.dm +++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm @@ -39,8 +39,8 @@ // 5.56x39mm (M-90gl Carbine & P-16) /obj/item/ammo_casing/a556_39 - name = "5.56x39mm bullet casing" - desc = "A 5.56x39mm bullet casing." + name = "5.56x39mm CLIP bullet casing" + desc = "A 5.56x39mm CLIP bullet casing." icon_state = "rifle-brass" caliber = "5.56x45mm" projectile_type = /obj/projectile/bullet/a556_45 @@ -91,3 +91,10 @@ caliber = ".299 caseless" projectile_type = /obj/projectile/bullet/c299 bullet_per_box = 100 + +/obj/item/ammo_casing/a65clip + name = "6.5x57mm CLIP bullet casing" + desc = "A 6.5x57mm CLIP bullet casing." + icon_state = "big-brass" + caliber = "6.5CLIP" + projectile_type = /obj/projectile/bullet/a65clip diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index 8b10f0374eef..e8e5c59c9c65 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -398,6 +398,13 @@ /obj/item/ammo_box/a762_40/inteq icon_state = "a762_40box_big_inteq" +/obj/item/ammo_box/a556_39 + name = "ammo box (5.56x39mm CLIP)" + icon_state = "a556_39box_big" + ammo_type = /obj/item/ammo_casing/a556_39 + max_ammo = 120 + w_class = WEIGHT_CLASS_NORMAL + /obj/item/ammo_box/a308 name = "ammo box (.308)" icon_state = "a308box" diff --git a/code/modules/projectiles/boxes_magazines/external/rifle.dm b/code/modules/projectiles/boxes_magazines/external/rifle.dm index 9224c0db84af..781641a6eef1 100644 --- a/code/modules/projectiles/boxes_magazines/external/rifle.dm +++ b/code/modules/projectiles/boxes_magazines/external/rifle.dm @@ -85,9 +85,9 @@ . = ..() icon_state = "ebr_mag-[!!ammo_count()]" -/obj/item/ammo_box/magazine/gal - name = "\improper GAL Magazine (.308)" - desc = "A standard 10-round magazine for GAL platform DMRs. These rounds do good damage with significant armor penetration." +/obj/item/ammo_box/magazine/f4_308 + name = "\improper F4 Magazine (.308)" + desc = "A standard 10-round magazine for F4 platform DMRs. These rounds do good damage with significant armor penetration." icon_state = "gal_mag-1" base_icon_state = "gal_mag" ammo_type = /obj/item/ammo_casing/a308 @@ -95,7 +95,7 @@ max_ammo = 10 multiple_sprites = AMMO_BOX_FULL_EMPTY -/obj/item/ammo_box/magazine/p16 +/obj/item/ammo_box/magazine/p16 //repath to /obj/item/ammo_box/magazine/generic_556 sometime name = "assault rifle magazine (5.56x45mm)" desc = "A simple, 30-round magazine for 5.56x45mm assault rifles. These rounds do moderate damage with good armor penetration." icon_state = "p16_mag-1" diff --git a/code/modules/projectiles/boxes_magazines/external/shotgun.dm b/code/modules/projectiles/boxes_magazines/external/shotgun.dm index 3c9aaad1f6f6..b45051fd2644 100644 --- a/code/modules/projectiles/boxes_magazines/external/shotgun.dm +++ b/code/modules/projectiles/boxes_magazines/external/shotgun.dm @@ -37,7 +37,7 @@ max_ammo = 6 w_class = WEIGHT_CLASS_SMALL //Smaller, holds less -/obj/item/ammo_box/magazine/cm15_mag +/obj/item/ammo_box/magazine/cm15_12g name = "CM-15 magazine (12g buckshot)" desc = "An almost straight, 8-round magazine designed for the CM-15 shotgun." icon_state = "cm15_mag-1" diff --git a/code/modules/projectiles/boxes_magazines/external/smg.dm b/code/modules/projectiles/boxes_magazines/external/smg.dm index 4c464c0433d0..ca1355bab3a0 100644 --- a/code/modules/projectiles/boxes_magazines/external/smg.dm +++ b/code/modules/projectiles/boxes_magazines/external/smg.dm @@ -28,19 +28,6 @@ base_icon_state = "46x30mmtI" ammo_type = /obj/item/ammo_casing/c46x30mm/inc -/obj/item/ammo_box/magazine/uzim9mm - name = "long SMG magazine (9mm)" - desc = "A thin, 32-round magazine for the Uzi SMG. These rounds do okay damage, but struggle against armor." - icon_state = "uzi9mm-32" - base_icon_state = "uzi9mm" - ammo_type = /obj/item/ammo_casing/c9mm - caliber = "9mm" - max_ammo = 32 - -/obj/item/ammo_box/magazine/uzim9mm/update_icon_state() - . = ..() - icon_state = "[base_icon_state]-[round(ammo_count(),4)]" - /obj/item/ammo_box/magazine/smgm9mm name = "SMG magazine (9mm)" desc = "A 30-round magazine for 9mm submachine guns. These rounds do okay damage, but struggle against armor." @@ -106,7 +93,7 @@ /obj/item/ammo_box/magazine/c45_firestorm_mag name = "stick magazine (.45)" desc = "A 28-round stick magazine for the toploading Firestorm submachine gun. These rounds do moderate damage, but struggle against armor." - icon_state = "firestorm_mag" + icon_state = "firestorm_mag-1" base_icon_state = "firestorm_mag" ammo_type = /obj/item/ammo_casing/c45 caliber = ".45" diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index ca1a1a89b8c9..023f6212e06f 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -316,6 +316,9 @@ ///This prevents gun from firing until the coodown is done, affected by lag var/current_cooldown = 0 + var/gunslinger_recoil_bonus = 0 + var/gunslinger_spread_bonus = 0 + /obj/item/gun/Initialize() . = ..() RegisterSignal(src, COMSIG_TWOHANDED_WIELD, PROC_REF(on_wield)) @@ -802,61 +805,42 @@ /obj/item/gun/proc/before_firing(atom/target,mob/user) return -// We do it like this in case theres some specific gun behavior for adjusting recoil, like bipods or folded stocks /obj/item/gun/proc/calculate_recoil(mob/user, recoil_bonus = 0) - return recoil_bonus + if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) + recoil_bonus += gunslinger_recoil_bonus + return clamp(recoil_bonus, 0 , INFINITY) -// We do it like this in case theres some specific gun behavior for adjusting spread, like bipods or folded stocks /obj/item/gun/proc/calculate_spread(mob/user, bonus_spread) - ///our final spread value - var/sprd = 0 - ///our randomized value after checking if we are wielded or not + var/final_spread = 0 var/randomized_gun_spread = 0 - ///bonus - var/randomized_bonus_spread - // do we have poor aim - var/poor_aim = FALSE + var/randomized_bonus_spread = 0 - //do we have bonus_spread ? If so, set sprd to it because it means a subtype's proc messed with it - sprd += bonus_spread + final_spread += bonus_spread - //reset bonus_spread for poor aim... - bonus_spread = 0 + if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) + randomized_bonus_spread += rand(0, gunslinger_spread_bonus) - // if we have poor aim, we fuck the shooter over if(HAS_TRAIT(user, TRAIT_POOR_AIM)) - bonus_spread += 25 - poor_aim = TRUE - // then we randomize the bonus spread - randomized_bonus_spread = rand(poor_aim ? 10 : 0, bonus_spread) //poor aim is no longer just a nusiance - - //then, we mutiply previous bonus spread as it means dual wielding usually, it also means poor aim is also even more severe - randomized_bonus_spread *= DUALWIELD_PENALTY_EXTRA_MULTIPLIER + randomized_bonus_spread += rand(0, 25) - // we will then calculate gun spread depending on if we are fully wielding (after do_after) the gun or not + //We will then calculate gun spread depending on if we are fully wielding (after do_after) the gun or not randomized_gun_spread = rand(0, wielded_fully ? spread : spread_unwielded) - //finally, we put it all together including if sprd has a value - sprd += randomized_gun_spread + randomized_bonus_spread - - //clamp it down to avoid guns with negative spread to have worse recoil... - sprd = clamp(sprd, 0, INFINITY) + final_spread += randomized_gun_spread + randomized_bonus_spread - // im not sure what this does, i beleive its meant to make it so bullet spread goes in the opposite direction? get back to me on this - update,i have commented it out, however it appears be dapening spread. weird. - //sprd *= (rand() - 0.5) + //Clamp it down to avoid guns with negative spread to have worse recoil... + final_spread = clamp(final_spread, 0, INFINITY) - //coin flip if we mutiply output by -1 so spread isn't JUST to the right + //So spread isn't JUST to the right if(prob(50)) - sprd *= -1 + final_spread *= -1 - // then we round it up and send it! - sprd = round(sprd) + final_spread = round(final_spread) - return sprd + return final_spread /obj/item/gun/proc/simulate_recoil(mob/living/user, recoil_bonus = 0, firing_angle) var/total_recoil = calculate_recoil(user, recoil_bonus) - total_recoil = clamp(total_recoil, 0 , INFINITY) var/actual_angle = firing_angle + rand(-recoil_deviation, recoil_deviation) + 180 if(actual_angle > 360) diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index dd71ff999fac..1790ba25a858 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -82,6 +82,8 @@ . += "[icon_state]_mag_[capacity_number]" if(!chambered && empty_indicator) . += "[icon_state]_empty" + if(chambered && mag_display_ammo) + . += "[icon_state]_chambered" /obj/item/gun/ballistic/process_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE, atom/shooter) if(!semi_auto && from_firing) @@ -107,7 +109,7 @@ chambered.forceMove(src) ///updates a bunch of racking related stuff and also handles the sound effects and the like -/obj/item/gun/ballistic/proc/rack(mob/user = null) +/obj/item/gun/ballistic/proc/rack(mob/user = null, chamber_new_round = TRUE) if (bolt_type == BOLT_TYPE_NO_BOLT) //If there's no bolt, nothing to rack return if (bolt_type == BOLT_TYPE_OPEN) @@ -118,21 +120,22 @@ bolt_locked = FALSE if (user) to_chat(user, "You rack the [bolt_wording] of \the [src].") - process_chamber(!chambered, FALSE, shooter = user) - if (bolt_type == BOLT_TYPE_LOCKING && !chambered) + process_chamber(!chambered, FALSE, chamber_new_round, user) + if ((bolt_type == BOLT_TYPE_LOCKING && !chambered) || bolt_type == BOLT_TYPE_CLIP) bolt_locked = TRUE playsound(src, lock_back_sound, lock_back_sound_volume, lock_back_sound_vary) else playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) - update_appearance() + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) ///Drops the bolt from a locked position -/obj/item/gun/ballistic/proc/drop_bolt(mob/user = null) +/obj/item/gun/ballistic/proc/drop_bolt(mob/user = null, chamber_new_round = TRUE) playsound(src, bolt_drop_sound, bolt_drop_sound_volume, FALSE) if (user) to_chat(user, "You drop the [bolt_wording] of \the [src].") - chamber_round() + if(chamber_new_round) + chamber_round() bolt_locked = FALSE update_appearance() @@ -246,6 +249,8 @@ if (last_shot_succeeded && bolt_type == BOLT_TYPE_LOCKING) bolt_locked = TRUE update_appearance() + if (last_shot_succeeded && bolt_type == BOLT_TYPE_CLIP) + update_appearance() /obj/item/gun/ballistic/pre_fire(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0, dual_wielded_gun = FALSE) prefire_empty_checks() @@ -281,13 +286,19 @@ else to_chat(user, "[src] is empty!") return - if(bolt_type == BOLT_TYPE_LOCKING && bolt_locked) + if((bolt_type == BOLT_TYPE_LOCKING || bolt_type == BOLT_TYPE_CLIP) && bolt_locked) drop_bolt(user) return + if (recent_rack > world.time) return recent_rack = world.time + rack_delay + if(bolt_type == BOLT_TYPE_CLIP) + rack(user, FALSE) + update_appearance() + return rack(user) + update_appearance() return diff --git a/code/modules/projectiles/guns/ballistic/assault.dm b/code/modules/projectiles/guns/ballistic/assault.dm index ce500cda930b..d5ed7a8225bf 100644 --- a/code/modules/projectiles/guns/ballistic/assault.dm +++ b/code/modules/projectiles/guns/ballistic/assault.dm @@ -14,23 +14,8 @@ rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg' spread_unwielded = 20 -/obj/item/gun/ballistic/automatic/assault/calculate_recoil(mob/user, recoil_bonus = 0) - var/gunslinger_bonus = 2 - var/total_recoil = recoil_bonus - - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty - total_recoil += gunslinger_bonus - - return ..(user, total_recoil) - -/obj/item/gun/ballistic/automatic/assault/calculate_spread(mob/user, bonus_spread) - var/gunslinger_bonus = 16 - var/total_spread = bonus_spread - - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty - total_spread += gunslinger_bonus - - return ..(user, total_spread) + gunslinger_recoil_bonus = 2 + gunslinger_spread_bonus = 16 /obj/item/gun/ballistic/automatic/assault/skm name = "\improper SKM-24" @@ -112,7 +97,7 @@ /obj/item/gun/ballistic/automatic/assault/p16/no_mag spawnwithmagazine = FALSE -/obj/item/gun/ballistic/automatic/assault/p16/minutemen +/obj/item/gun/ballistic/automatic/assault/cm82 name = "\improper CM-16" desc = "The standard-issue rifle of CLIP and an extensively modified reproduction of the P-16. Chambered in 5.56mm." icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' diff --git a/code/modules/projectiles/guns/ballistic/hmg.dm b/code/modules/projectiles/guns/ballistic/hmg.dm index 8a3e68b9a6ec..ccf21a2c7c0e 100644 --- a/code/modules/projectiles/guns/ballistic/hmg.dm +++ b/code/modules/projectiles/guns/ballistic/hmg.dm @@ -18,6 +18,9 @@ recoil_unwielded = 4 wield_slowdown = 3 + gunslinger_recoil_bonus = 2 + gunslinger_spread_bonus = 20 + ///does this have a bipod? var/has_bipod = FALSE ///is the bipod deployed? @@ -41,7 +44,7 @@ /obj/item/gun/ballistic/automatic/hmg/Initialize() . = ..() - for(var/datum/action/item_action/deploy_bipod/action as anything in actions_types) + for(var/datum/action/item_action/deploy_bipod/action as anything in actions) if(!has_bipod) qdel(action) @@ -116,29 +119,6 @@ . = ..() retract_bipod(user=user) -/obj/item/gun/ballistic/automatic/hmg/calculate_recoil(mob/user, recoil_bonus = 0) - var/gunslinger_bonus = 2 - var/total_recoil = recoil_bonus - - if(bipod_deployed) - total_recoil += deploy_recoil_bonus - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty - total_recoil += gunslinger_bonus - - return ..(user, total_recoil) - -/obj/item/gun/ballistic/automatic/hmg/calculate_spread(mob/user, bonus_spread) - var/gunslinger_bonus = 20 - var/total_spread = bonus_spread - - if(bipod_deployed) - total_spread += deploy_spread_bonus - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty - total_spread += gunslinger_bonus - - return ..(user, total_spread) - - /obj/item/gun/ballistic/automatic/hmg/update_icon_state() . = ..() item_state = "[initial(item_state)][bipod_deployed ? "_deployed" : ""]" diff --git a/code/modules/projectiles/guns/ballistic/marksman.dm b/code/modules/projectiles/guns/ballistic/marksman.dm index 46a9e2466290..0759de4f70e1 100644 --- a/code/modules/projectiles/guns/ballistic/marksman.dm +++ b/code/modules/projectiles/guns/ballistic/marksman.dm @@ -64,39 +64,3 @@ EMPTY_GUN_HELPER(automatic/marksman/sniper_rifle) spread = -4 EMPTY_GUN_HELPER(automatic/marksman/ebr) - -/obj/item/gun/ballistic/automatic/marksman/gal - name = "\improper CM-GAL-S" - desc = "The standard issue DMR of CLIP. Dates back to the Xenofauna War, this particular model is in a carbine configuration, and, as such, is shorter than the standard model. Chambered in .308." - - icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' - lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' - righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' - mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' - - fire_sound = 'sound/weapons/gun/rifle/shot.ogg' - icon_state = "gal" - item_state = "gal" - show_magazine_on_sprite = TRUE - weapon_weight = WEAPON_MEDIUM - w_class = WEIGHT_CLASS_BULKY - mag_type = /obj/item/ammo_box/magazine/gal - fire_sound = 'sound/weapons/gun/rifle/gal.ogg' - burst_size = 0 - actions_types = list() - manufacturer = MANUFACTURER_MINUTEMAN - - wield_slowdown = 2 - spread = -4 - fire_select_icon_state_prefix = "clip_" - adjust_fire_select_icon_state_on_safety = TRUE - -/obj/item/gun/ballistic/automatic/marksman/gal/inteq - name = "\improper SsG-04" - desc = "A marksman rifle purchased from CLIP and modified to suit IRMG's needs. Chambered in .308." - icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' - lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' - righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' - mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' - icon_state = "gal-inteq" - item_state = "gal-inteq" diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 545cbf8bf117..6a2e30c9cddb 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -40,6 +40,9 @@ safety_wording = "hammer" + gunslinger_recoil_bonus = -1 + gunslinger_spread_bonus = -8 + var/gate_loaded = FALSE //for stupid wild west shit var/gate_offset = 5 //for wild west shit 2: instead of ejecting the chambered round, eject the next round if 1 var/gate_load_direction = REVOLVER_AUTO_ROTATE_RIGHT_LOADING //when we load ammo with a box, which direction do we rotate the cylinder? unused with normal revolvers @@ -418,7 +421,7 @@ fire_delay = src::fire_delay if(fan) rack() - to_chat(user, "You fan the [bolt_wording] of \the [src]!") + to_chat(user, span_notice("You fan the [bolt_wording] of \the [src]!")) balloon_alert_to_viewers("fans revolver!") fire_delay = 0 SECONDS @@ -436,25 +439,6 @@ return to_chat(user, "The hammer is up on [src]! Pull it down to fire!") -/obj/item/gun/ballistic/revolver/calculate_recoil(mob/user, recoil_bonus = 0) - var/gunslinger_bonus = -1 - var/total_recoil = recoil_bonus - - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger bonus - total_recoil += gunslinger_bonus - total_recoil = clamp(total_recoil,0,INFINITY) - - return ..(user, total_recoil) - -/obj/item/gun/ballistic/revolver/calculate_spread(mob/user, bonus_spread) - var/gunslinger_bonus = -8 - var/total_spread = bonus_spread - - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger bonus - total_spread += gunslinger_bonus - - return ..(user, total_spread) - /obj/item/gun/ballistic/revolver/pickup(mob/user) . = ..() tryflip(user) diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 1f15f8d01689..f63328035959 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -35,6 +35,8 @@ recoil = 1 recoil_unwielded = 4 + gunslinger_recoil_bonus = -1 + /obj/item/gun/ballistic/shotgun/blow_up(mob/user) if(chambered && chambered.BB) process_fire(user, user, FALSE) @@ -46,15 +48,6 @@ return TRUE return FALSE -/obj/item/gun/ballistic/shotgun/calculate_recoil(mob/user, recoil_bonus = 0) - var/gunslinger_bonus = -1 - var/total_recoil = recoil_bonus - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger bonus - total_recoil += gunslinger_bonus - total_recoil = clamp(total_recoil,0,INFINITY) - - return ..(user, total_recoil) - // BRIMSTONE SHOTGUN // /obj/item/gun/ballistic/shotgun/brimstone @@ -276,24 +269,6 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) icon_state = "bulldog_suns" item_state = "bulldog_suns" -/obj/item/gun/ballistic/shotgun/bulldog/minutemen //TODO: REPATH - name = "\improper CM-15" - desc = "A standard-issue shotgun of CLIP, most often used by boarding crews. Only compatible with specialized 8-round magazines." - icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' - lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' - righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' - mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' - - mag_type = /obj/item/ammo_box/magazine/cm15_mag - icon_state = "cm15" - item_state = "cm15" - empty_alarm = FALSE - empty_indicator = FALSE - unique_mag_sprites_for_variants = FALSE - manufacturer = MANUFACTURER_MINUTEMAN - fire_select_icon_state_prefix = "clip_" - adjust_fire_select_icon_state_on_safety = TRUE - ///////////////////////////// // DOUBLE BARRELED SHOTGUN // ///////////////////////////// diff --git a/code/modules/projectiles/guns/ballistic/smg.dm b/code/modules/projectiles/guns/ballistic/smg.dm index 2cb68fc09176..d43e324cfacd 100644 --- a/code/modules/projectiles/guns/ballistic/smg.dm +++ b/code/modules/projectiles/guns/ballistic/smg.dm @@ -19,25 +19,8 @@ eject_sound = 'sound/weapons/gun/smg/smg_unload.ogg' eject_empty_sound = 'sound/weapons/gun/smg/smg_unload.ogg' -/obj/item/gun/ballistic/automatic/smg/calculate_recoil(mob/user, recoil_bonus = 0) - var/gunslinger_bonus = 2 - var/total_recoil - if(.) - total_recoil += . - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty - total_recoil += gunslinger_bonus - . = total_recoil - return ..() - -/obj/item/gun/ballistic/automatic/smg/calculate_spread(mob/user, bonus_spread) - var/gunslinger_bonus = 16 - var/total_spread = bonus_spread - if(.) - total_spread += . - if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty - total_spread += gunslinger_bonus - . = total_spread - return ..() + gunslinger_recoil_bonus = 2 + gunslinger_spread_bonus = 16 /obj/item/gun/ballistic/automatic/smg/c20r name = "\improper C-20r SMG" @@ -95,34 +78,6 @@ EMPTY_GUN_HELPER(automatic/smg/c20r) /obj/item/gun/ballistic/automatic/smg/wt550/no_mag spawnwithmagazine = FALSE -/obj/item/gun/ballistic/automatic/smg/mini_uzi - name = "\improper Type U3 Uzi" - desc = "A lightweight submachine gun, for when you really want someone dead. Uses 9mm rounds." - - icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' - lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' - righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' - mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' - icon_state = "uzi" - - mag_type = /obj/item/ammo_box/magazine/uzim9mm - bolt_type = BOLT_TYPE_OPEN - show_magazine_on_sprite = TRUE - - fire_sound = 'sound/weapons/gun/smg/uzi.ogg' - rack_sound = 'sound/weapons/gun/smg/uzi_cocked.ogg' - - load_sound = 'sound/weapons/gun/smg/uzi_reload.ogg' - load_empty_sound = 'sound/weapons/gun/smg/uzi_reload.ogg' - eject_sound = 'sound/weapons/gun/smg/uzi_unload.ogg' - eject_empty_sound = 'sound/weapons/gun/smg/uzi_unload.ogg' - - spread = 4 - spread_unwielded = 8 - wield_slowdown = 0.25 - wield_delay = 0.2 SECONDS - fire_delay = 0.1 SECONDS - /obj/item/gun/ballistic/automatic/smg/vector name = "\improper Vector carbine" desc = "A police carbine based on a pre-Night of Fire SMG design. Most of the complex workings have been removed for reliability. Chambered in 9mm." @@ -251,41 +206,6 @@ EMPTY_GUN_HELPER(automatic/smg/c20r) magazine = new /obj/item/ammo_box/magazine/c45_firestorm_mag/pan(src) chamber_round() -/obj/item/gun/ballistic/automatic/smg/cm5 - name = "\improper CM-5" - desc = "The standard issue SMG of CLIP. One of the few firearm designs that were left mostly intact from the designs found on the UNSV Lichtenstein. Chambered in 9mm." - icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' - lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' - righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' - mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' - - icon_state = "cm5" - item_state = "cm5" - - mag_type = /obj/item/ammo_box/magazine/smgm9mm - weapon_weight = WEAPON_LIGHT - fire_sound = 'sound/weapons/gun/smg/smg_heavy.ogg' - manufacturer = MANUFACTURER_MINUTEMAN - - fire_select_icon_state_prefix = "clip_" - adjust_fire_select_icon_state_on_safety = TRUE - -EMPTY_GUN_HELPER(automatic/smg/cm5) - -/obj/item/gun/ballistic/automatic/smg/cm5/compact - name = "\improper CM-5c" - desc = "The compact conversion of the CM-5. While not exactly restricted, it is looked down upon due to CLIP's doctrine on medium-longrange combat, however it excels at close range and is very lightweight. You feel like this gun is mildly unfinished. Chambered in 9mm." - w_class = WEIGHT_CLASS_NORMAL - spread = 25 - spread_unwielded = 40 - - fire_delay = 0.08 SECONDS - - recoil = 1 - recoil_unwielded = 2 - wield_delay = 0.2 SECONDS - wield_slowdown = 0.15 - /obj/item/gun/ballistic/automatic/smg/skm_carbine name = "\improper SKM-24v" desc = "The SKM-24v was a carbine modification of the SKM-24 during the Frontiersmen War. This, however, is just a shoddy imitation of that carbine, effectively an SKM-24 with a sawed down barrel and a folding wire stock. Can be fired with the stock folded, though accuracy suffers. Chambered in 4.6x30mm." diff --git a/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm b/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm new file mode 100644 index 000000000000..f32a2f11a508 --- /dev/null +++ b/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm @@ -0,0 +1,478 @@ +//########### PISTOLS ###########// +/obj/item/gun/ballistic/automatic/pistol/cm23 + name = "\improper CM-23" + desc = "CLIP's standard service pistol. 10 rounds of 10mm ammunition make the CM-23 deadlier than many other service pistols, but its weight and bulk have made it unpopular as a sidearm. It has largely been phased out outside of specialized units and patrols on the fringes of CLIP space. Chambered in 10mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm23" + item_state = "clip_generic" + w_class = WEIGHT_CLASS_NORMAL + mag_type = /obj/item/ammo_box/magazine/cm23 +// can_suppress = FALSE + fire_sound = 'sound/weapons/gun/pistol/cm23.ogg' + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/slide_drop.ogg' + manufacturer = MANUFACTURER_MINUTEMAN + load_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + recoil_unwielded = 3 + +/obj/item/ammo_box/magazine/cm23 + name = "CM-23 pistol magazine (10mm)" + desc = "An 10-round magazine magazine designed for the CM-70 pistol. These rounds do moderate damage, but struggle against armor." + icon_state = "cm23_mag-1" + base_icon_state = "cm23_mag" + ammo_type = /obj/item/ammo_casing/c10mm + caliber = "10mm" + max_ammo = 10 + +/obj/item/ammo_box/magazine/cm23/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/gun/ballistic/automatic/pistol/cm70 + name = "CM-70 machine pistol" + desc = "A compact machine pistol designed to rapidly fire 3-round bursts. Popular with officers and certain special units, the CM-70 is incredibly dangerous at close range. Chambered in 9mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm70" + item_state = "clip_generic" + mag_type = /obj/item/ammo_box/magazine/m9mm_cm70 +// can_suppress = FALSE + burst_size = 3 + burst_delay = 0.1 SECONDS + fire_delay = 0.4 SECONDS + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + default_firemode = FIREMODE_SEMIAUTO + manufacturer = MANUFACTURER_MINUTEMAN + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + + fire_sound = 'sound/weapons/gun/pistol/cm70.ogg' + + spread = 8 + spread_unwielded = 20 + +/obj/item/ammo_box/magazine/m9mm_cm70 + name = "CM-70 machine pistol magazine (9mm)" + desc = "A 18-round magazine designed for the CM-70 machine pistol. These rounds do okay damage, but struggle against armor." + icon_state = "cm70_mag_18" + base_icon_state = "cm70_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9mm" + max_ammo = 18 + + +/obj/item/ammo_box/magazine/m9mm_cm70/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[ammo_count() == 1 ? 1 : round(ammo_count(),3)]" + +//########### SMGS ###########// +/obj/item/gun/ballistic/automatic/smg/cm5 + name = "\improper CM-5" + desc = "CLIP's standard-issue submachine gun. Well-liked for its accuracy, stability, and ease of use compared to other submachineguns. Chambered in 9mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm5" + item_state = "cm5" + + mag_type = /obj/item/ammo_box/magazine/cm5_9mm + bolt_type = BOLT_TYPE_CLIP + weapon_weight = WEAPON_LIGHT + fire_sound = 'sound/weapons/gun/smg/cm5.ogg' + manufacturer = MANUFACTURER_MINUTEMAN + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 37, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 27, + "y" = 17, + ) + ) + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +EMPTY_GUN_HELPER(automatic/smg/cm5) + +/obj/item/ammo_box/magazine/cm5_9mm + name = "CM-5 magazine (9mm)" + desc = "A 30-round magazine for 9mm submachine guns. These rounds do okay damage, but struggle against armor." + icon_state = "cm5_mag-1" + base_icon_state = "cm5_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/gun/ballistic/automatic/smg/cm5/compact + name = "\improper CM-5c" + desc = "A modification of the CM-5 featuring a dramatically shortened barrel and removed stock. Designed for CLIP-GOLD covert enforcement agents to maximize portability without sacrificing firepower, though accuracy at range is abysmal at best. Chambered in 9mm." + icon_state = "cm5c" + item_state = "cm5c" + + w_class = WEIGHT_CLASS_NORMAL + spread = 10 + spread_unwielded = 20 + + fire_delay = 0.1 SECONDS + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 30, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 22, + "y" = 17, + ) + ) + + + recoil = 1 + recoil_unwielded = 2 + wield_delay = 0.2 SECONDS + wield_slowdown = 0.15 + + var/obj/item/storage/briefcase/current_case + +/obj/item/gun/ballistic/automatic/smg/cm5/compact/attackby(obj/item/attacking_item, mob/user, params) + . = ..() + if(current_case) + return + if(!istype(attacking_item, /obj/item/storage/briefcase)) + return + if(attacking_item.contents.len != 0) + return + to_chat(user, span_notice("...? You rig [src] to fire from within [attacking_item].")) + current_case = attacking_item + attacking_item.forceMove(src) + icon = attacking_item.icon + base_icon_state = attacking_item.icon_state + item_state = attacking_item.item_state + name = attacking_item.name + lefthand_file = attacking_item.lefthand_file + righthand_file = attacking_item.righthand_file + pickup_sound = attacking_item.pickup_sound + drop_sound = attacking_item.drop_sound + w_class = WEIGHT_CLASS_BULKY + +//how are you even supposed to hold it like this...? + spread += 10 + spread_unwielded +=10 + + cut_overlays() + update_appearance() + +/obj/item/gun/ballistic/automatic/smg/cm5/compact/AltClick(mob/user) + if(!current_case) + return ..() + current_case.forceMove(get_turf(src)) + icon = src::icon + base_icon_state = src::icon_state + item_state = src::item_state + name = src::name + lefthand_file = src::lefthand_file + righthand_file = src::righthand_file + pickup_sound = src::pickup_sound + drop_sound = src::drop_sound + w_class = WEIGHT_CLASS_NORMAL + + spread = src::spread + spread_unwielded = src::spread_unwielded + to_chat(user, span_notice("You remove the [current_case] from [src]")) + current_case = null + + cut_overlays() + update_appearance() + + +//########### MARKSMAN ###########// +/obj/item/gun/ballistic/automatic/marksman/f4 + name = "CM-F4" + desc = "CLIP's marksman rifle, used by both military and law enforcement units. Designed not long after the CM-24, the venerable F4 has adapted well to continued upgrades. Chambered in .308." + + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + fire_sound = 'sound/weapons/gun/rifle/shot.ogg' + icon_state = "f4" + item_state = "f4" + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + bolt_type = BOLT_TYPE_CLIP + mag_type = /obj/item/ammo_box/magazine/f4_308 + fire_sound = 'sound/weapons/gun/rifle/f4.ogg' + burst_size = 0 + actions_types = list() + manufacturer = MANUFACTURER_MINUTEMAN + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 17, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 35, + "y" = 16, + ) + ) + + wield_slowdown = 2 + spread = -4 + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +/obj/item/gun/ballistic/automatic/marksman/f4/inteq + name = "\improper SsG-04" + desc = "An F4 rifle purchased from CLIP and modified to suit IRMG's needs. Chambered in .308." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "f4_inteq" + item_state = "f4_inteq" + +/obj/item/gun/ballistic/automatic/marksman/f90 + name = "CM-F90" + desc = "A powerful sniper rifle used by vanishingly rare CLIP specialists, capable of impressive range and penetrating power. Chambered in 6.5mm CLIP." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "f90" + item_state = "f90" + + fire_sound = 'sound/weapons/gun/sniper/cmf90.ogg' + + mag_type = /obj/item/ammo_box/magazine/f90 + bolt_type = BOLT_TYPE_CLIP + + fire_delay = 1 SECONDS + + manufacturer = MANUFACTURER_MINUTEMAN + spread = -5 + spread_unwielded = 35 + recoil = 4 + recoil_unwielded = 10 + wield_slowdown = 1 + wield_delay = 1.3 SECONDS + + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + +/obj/item/ammo_box/magazine/f90 + name = "\improper CM-F90 Magazine (6.5x57mm CLIP)" + desc = "A large 5-round box magazine for the CM-F90 sniper rifles. These rounds deal amazing damage and bypass half of their protective equipment, though it isn't a high enough caliber to pierce armored vehicles." + base_icon_state = "f90_mag" + icon_state = "f90_mag-1" + ammo_type = /obj/item/ammo_casing/a65clip + caliber = "6.5CLIP" + max_ammo = 5 + +/obj/item/ammo_box/magazine/f90/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +//########### RIFLES ###########// +/obj/item/gun/ballistic/automatic/assault/cm82 + name = "\improper CM-82" + desc = "CLIP's standard assault rifle, still relatively new in service. Accurate, reliable, and easy to use, the CM-82 replaced the CM-24 as CLIP's assault rifle almost overnight, and has proven immensely popular since. Chambered in 5.56mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + fire_sound = 'sound/weapons/gun/rifle/cm82.ogg' + icon_state = "cm82" + item_state = "cm82" + show_magazine_on_sprite = TRUE + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + bolt_type = BOLT_TYPE_CLIP + mag_type = /obj/item/ammo_box/magazine/p16 + spread = 2 + wield_delay = 0.5 SECONDS + + fire_delay = 0.18 SECONDS + + load_sound = 'sound/weapons/gun/rifle/cm82_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/cm82_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/cm82_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/cm82_unload.ogg' + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +/obj/item/gun/ballistic/automatic/assault/skm/cm24 + name = "\improper CM-24" + desc = "An obsolete and very rugged assault rifle with a heavy projectile and slow action for its class. Once CLIP's standard assault rifle produced in phenomenal numbers for the First Frontiersman War, it now serves as an acceptable, if rare, battle rifle. Chambered in 7.62mm CLIP." + + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm24" + item_state = "cm24" + manufacturer = MANUFACTURER_NONE + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +/obj/item/gun/ballistic/automatic/hmg/cm40 + name = "\improper CM-40" + desc = "A light machine gun used by CLIP heavy weapons teams, capable of withering suppressive fire. The weight and recoil make it nearly impossible to use without deploying the bipod against appropriate cover, such as a table, or bracing against solid cover. Chambered in 7.62x40mm CLIP." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm40" + item_state = "cm40" + + fire_delay = 0 + + fire_sound = 'sound/weapons/gun/hmg/cm40.ogg' + rack_sound = 'sound/weapons/gun/hmg/cm40_cocked.ogg' + + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + + load_sound = 'sound/weapons/gun/hmg/cm40_reload.ogg' + load_empty_sound = 'sound/weapons/gun/hmg/cm40_reload.ogg' + eject_sound = 'sound/weapons/gun/hmg/cm40_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/hmg/cm40_unload.ogg' + + fire_delay = 0.1 SECONDS + + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + manufacturer = MANUFACTURER_MINUTEMAN + mag_type = /obj/item/ammo_box/magazine/cm40_762_40_box + + spread = 10 + spread_unwielded = 35 + + recoil = 2 //identical to other LMGS + recoil_unwielded = 7 //same as skm + + wield_slowdown = 1 //not as severe as other lmgs, but worse than the normal skm + wield_delay = 0.9 SECONDS //faster than normal lmgs, slower than stock skm + + has_bipod = TRUE + + deploy_recoil_bonus = -2 + deploy_spread_bonus = -6 + +/obj/item/gun/ballistic/automatic/hmg/cm40/ComponentInitialize() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.1 SECONDS) + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/ammo_box/magazine/cm40_762_40_box + name = "CM-40 box magazine (7.62x40mm CLIP)" + desc = "An 80 round box magazine for CM-40 light machine gun. These rounds do good damage with good armor penetration." + base_icon_state = "cm40_mag" + icon_state = "cm40_mag-1" + ammo_type = /obj/item/ammo_casing/a762_40 + max_ammo = 80 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/cm40_762_40_box/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +//########### MISC ###########// + +/obj/item/gun/ballistic/shotgun/cm15 + name = "\improper CM-15" + desc = "A large automatic shotgun used by CLIP. Generally employed by law enforcement and breaching specialists, and rarely by CLIP-BARD (typically with incendiary ammunition). Chambered in 12 gauge." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + + + icon_state = "cm15" + item_state = "cm15" + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + + manufacturer = MANUFACTURER_MINUTEMAN + + weapon_weight = WEAPON_MEDIUM +// can_suppress = FALSE + mag_type = /obj/item/ammo_box/magazine/cm15_12g + + empty_indicator = FALSE + unique_mag_sprites_for_variants = FALSE + + semi_auto = TRUE + internal_magazine = FALSE + casing_ejector = TRUE + tac_reloads = TRUE + pickup_sound = 'sound/items/handling/rifle_pickup.ogg' + + fire_sound = 'sound/weapons/gun/shotgun/bulldog.ogg' + + load_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + + rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg' + + spread = 4 + spread_unwielded = 16 + recoil = 1 + recoil_unwielded = 4 + wield_slowdown = 0.6 + wield_delay = 0.65 SECONDS + diff --git a/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm b/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm new file mode 100644 index 000000000000..2fbe0009a563 --- /dev/null +++ b/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm @@ -0,0 +1,43 @@ +/obj/item/gun/energy/kalix/clip + name = "CM-1" + desc = "CLIP's first standard-issue weapon, a near-copy of colonial-era weapons left behind by Free Zohil forces in CLIP's founding years. Outdated and difficult to source replacement parts for, but nevertheless still very common among BARD personnel and for ceremonal use." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm1" + item_state = "cm1" + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + cell_type = /obj/item/stock_parts/cell/gun + ammo_type = list(/obj/item/ammo_casing/energy/kalix, /obj/item/ammo_casing/energy/disabler/hitscan) + + manufacturer = MANUFACTURER_MINUTEMAN_LASER + +/obj/item/gun/energy/laser/e50/clip + name = "ECM-50" + desc = "An extensive modification of the Eoehoma E-50 Emitter, customized specifically for CLIP-BARD. Sacrifices some of the E-50's raw power for vastly improved energy efficiency, while preserving its incendiary side-effects." + + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm50" + item_state = "cm50" + shaded_charge = TRUE + charge_sections = 4 + + ammo_type = list(/obj/item/ammo_casing/energy/laser/eoehoma/e50/clip) + +/obj/item/ammo_casing/energy/laser/eoehoma/e50/clip + projectile_type = /obj/projectile/beam/emitter/hitscan/clip + fire_sound = 'sound/weapons/gun/laser/heavy_laser.ogg' + e_cost = 6250 + delay = 0.6 SECONDS + +/obj/projectile/beam/emitter/hitscan/clip + damage = 35 diff --git a/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm b/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm index faafde9656c4..4e16d2d03512 100644 --- a/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm +++ b/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm @@ -50,7 +50,6 @@ desc = "The BG-16 is the military-grade beam gun designed and manufactured by Etherbor Industries as the standard-issue close-range weapon of the PGF." icon_state = "pgfgun" item_state = "pgfgun" - w_class = WEIGHT_CLASS_NORMAL gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) default_firemode = FIREMODE_SEMIAUTO @@ -70,6 +69,7 @@ /obj/item/ammo_casing/energy/kalix/pgf projectile_type = /obj/projectile/beam/hitscan/kalix/pgf fire_sound = 'sound/weapons/gun/energy/kalixsmg.ogg' + e_cost = 666 //30 shots per cell delay = 1 /obj/item/gun/energy/kalix/pistol //blue diff --git a/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm b/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm new file mode 100644 index 000000000000..90355db145dd --- /dev/null +++ b/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm @@ -0,0 +1,229 @@ +/obj/item/gun/ballistic/automatic/pistol/mauler + name = "Mauler machine pistol" + desc = "An automatic machine pistol originating from the Shoal. Impressive volume of fire with abysmal accuracy, lackluster armor penetration, and limited magazine size render it mostly useless outside of very close quarters. Chambered in 9mm." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "mauler" + item_state = "hp_generic" + w_class = WEIGHT_CLASS_NORMAL + mag_type = /obj/item/ammo_box/magazine/m9mm_mauler + fire_delay = 0.06 SECONDS + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + spread = 25 + spread_unwielded = 50 + recoil = 1 + recoil_unwielded = 4 + fire_sound = 'sound/weapons/gun/pistol/mauler.ogg' + + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + + load_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + +/obj/item/gun/ballistic/automatic/pistol/mauler/ComponentInitialize() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.06 SECONDS) + +/obj/item/ammo_box/magazine/m9mm_mauler + name = "mauler machine pistol magazine (9mm)" + desc = "A 12-round magazine designed for the Mauler machine pistol." + icon_state = "mauler_mag-1" + base_icon_state = "mauler_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9mm" + max_ammo = 12 + +/obj/item/ammo_box/magazine/m9mm_mauler/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/gun/ballistic/automatic/pistol/spitter + name = "\improper Spitter" + desc = "An open-bolt submachine gun favored by the Frontiersmen. This design's origins are unclear, but its simple, robust design has been widely copied throughout the Frontier, and it is stereotypically used by pirates and various criminal groups that value low price and ease of concealment. Chambered in 9mm." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "spitter" + item_state = "spitter" + mag_type = /obj/item/ammo_box/magazine/spitter_9mm + bolt_type = BOLT_TYPE_OPEN + weapon_weight = WEAPON_LIGHT + show_magazine_on_sprite = TRUE + manufacturer = MANUFACTURER_IMPORT + + spread = 20 + spread_unwielded = 35 + dual_wield_spread = 35 + wield_slowdown = 0.25 + wield_delay = 0.2 SECONDS + fire_delay = 0.09 SECONDS + + fire_sound = 'sound/weapons/gun/smg/spitter.ogg' + rack_sound = 'sound/weapons/gun/smg/spitter_cocked.ogg' + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + load_sound = 'sound/weapons/gun/smg/spitter_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/spitter_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/spitter_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/spitter_unload.ogg' + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/foldable_stock/spitter + ) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_STOCK = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 32, + "y" = 23, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = -5, + "y" = 18, + ) + ) + + default_attachments = list(/obj/item/attachment/foldable_stock/spitter) + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + + +/obj/item/ammo_box/magazine/spitter_9mm + name = "spitter pistol magazine (9mm)" + desc = "A thin 30-round magazine for the Spitter submachine gun." + icon_state = "spitter_mag-1" + base_icon_state = "spitter_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9mm" + max_ammo = 30 + +/obj/item/ammo_box/magazine/spitter_9mm/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + + +/obj/item/gun/ballistic/automatic/smg/pounder + name = "Pounder" + desc = "An unusual submachine gun of Frontiersman make. A miniscule cartridge lacking both stopping power and armor penetration is compensated for with best-in-class ammunition capacity and cycle rate. Chambered in .22 LR." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "pounder" + item_state = "pounder" + mag_type = /obj/item/ammo_box/magazine/c22lr_pounder_pan + burst_size = 1 + fire_delay = 0.05 SECONDS + spread = 25 + spread_unwielded = 50 + + fire_sound = 'sound/weapons/gun/smg/pounder.ogg' + rack_sound = 'sound/weapons/gun/smg/pounder_cocked.ogg' + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + load_sound = 'sound/weapons/gun/smg/pounder_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/pounder_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/pounder_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/pounder_unload.ogg' + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + manufacturer = MANUFACTURER_IMPORT + wield_slowdown = 0.5 + +/obj/item/ammo_box/magazine/c22lr_pounder_pan + name = "pan magazine (.22 LR)" + desc = "A 50-round pan magazine for the Pounder submachine gun." + icon_state = "firestorm_pan" + base_icon_state = "firestorm_pan" + ammo_type = /obj/item/ammo_casing/c22lr + caliber = "22lr" + max_ammo = 50 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/c22lr_pounder_pan/update_icon_state() + . = ..() + icon_state = "firestorm_pan" + +/obj/item/gun/ballistic/automatic/hmg/shredder + name = "\improper Shredder" + desc = "A vastly atypical heavy machine gun, extensively modified by the Frontiersmen. Additional grips have been added to enable firing from the hip, and it has been modified to fire belts of shotgun shells. Chambered in 12g." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "shredder" + item_state = "shredder" + mag_type = /obj/item/ammo_box/magazine/m12_shredder + spread = 15 + recoil = 2 + recoil_unwielded = 7 + fire_delay = 0.16 SECONDS + mag_display_ammo = TRUE + + bolt_type = BOLT_TYPE_STANDARD + show_magazine_on_sprite = TRUE + show_magazine_on_sprite_ammo = TRUE + tac_reloads = FALSE + fire_sound = 'sound/weapons/gun/hmg/shredder.ogg' + rack_sound = 'sound/weapons/gun/hmg/shredder_cocked_alt.ogg' + + load_sound_vary = FALSE + eject_sound_vary = FALSE + load_sound = 'sound/weapons/gun/hmg/shredder_reload.ogg' + load_empty_sound = 'sound/weapons/gun/hmg/shredder_reload.ogg' + eject_sound = 'sound/weapons/gun/hmg/shredder_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/hmg/shredder_unload.ogg' + + manufacturer = MANUFACTURER_IMPORT + has_bipod = FALSE + +/obj/item/ammo_box/magazine/m12_shredder + name = "belt box (12g)" + desc = "A 40-round belt box for the Shredder heavy machine gun." + icon_state = "shredder_mag-1" + base_icon_state = "shredder_mag" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 40 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/m12_shredder/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/m12_shredder/slug + name = "belt box (12g slug)" + desc = "A 40-round belt box for the Shredder heavy machine gun." + icon_state = "shredder_mag_slug-1" + base_icon_state = "shredder_mag_slug" + ammo_type = /obj/item/ammo_casing/shotgun + caliber = "12ga" + max_ammo = 40 + w_class = WEIGHT_CLASS_NORMAL diff --git a/code/modules/projectiles/projectile/bullets/rifle.dm b/code/modules/projectiles/projectile/bullets/rifle.dm index bbdb9f1e0160..0ea5a8557c8d 100644 --- a/code/modules/projectiles/projectile/bullets/rifle.dm +++ b/code/modules/projectiles/projectile/bullets/rifle.dm @@ -72,3 +72,17 @@ name = ".229 Eoehoma caseless bullet" damage = 20 armour_penetration = 10 + +//6.5x57mm CLIP (F90) + +/obj/projectile/bullet/a65clip + name = "6.5x57mm CLIP bullet" + speed = 0.3 + stamina = 10 + damage = 40 + armour_penetration = 50 + + icon_state = "redtrac" + light_system = MOVABLE_LIGHT + light_color = COLOR_SOFT_RED + light_range = 2 diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 62661594c06e..999b576ea08c 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -87,7 +87,7 @@ else if(reagents.total_volume && is_drainable()) switch(user.a_intent) - if(INTENT_HELP) + if(INTENT_DISARM) attempt_pour(target, user) if(INTENT_HARM) user.visible_message("[user] splashes the contents of [src] onto [target]!", \ diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index 06b58f27105b..a2b295f2e6fa 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -286,7 +286,7 @@ build_type = AUTOLATHE | PROTOLATHE materials = list(/datum/material/iron = 750) build_path = /obj/item/tank/internals/emergency_oxygen/engi/empty - category = list("hacked","Misc","Equipment") + category = list("initial", "Misc","Equipment") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO /datum/design/plasmaman_tank_belt @@ -295,7 +295,7 @@ build_type = AUTOLATHE | PROTOLATHE materials = list(/datum/material/iron = 800) build_path = /obj/item/tank/internals/plasmaman/belt/empty - category = list("hacked","Misc","Equipment") + category = list("initial", "Misc","Equipment") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO /datum/design/generic_gas_tank @@ -516,14 +516,6 @@ category = list("initial","Misc", "Tool Designs") departmental_flags = DEPARTMENTAL_FLAG_SERVICE -/datum/design/foilhat - name = "Tinfoil Hat" - id = "tinfoil_hat" - build_type = AUTOLATHE - materials = list(/datum/material/iron = 5500) - build_path = /obj/item/clothing/head/foilhat - category = list("hacked", "Misc") - /datum/design/scalpel name = "Scalpel" id = "scalpel" @@ -772,31 +764,13 @@ build_path = /obj/item/ammo_box/foambox category = list("initial", "Misc") -//hacked autolathe recipes -//WS - emagged recipies -/datum/design/flamethrower - name = "Flamethrower" - id = "flamethrower" - build_type = AUTOLATHE - materials = list(/datum/material/iron = 500) - build_path = /obj/item/flamethrower/full - category = list("hacked", "Security") - -/datum/design/electropack - name = "Electropack" - id = "electropack" - build_type = AUTOLATHE - materials = list(/datum/material/iron = 10000, /datum/material/glass = 2500) - build_path = /obj/item/electropack - category = list("hacked", "Tools") - /datum/design/handcuffs name = "Handcuffs" id = "handcuffs" build_type = AUTOLATHE materials = list(/datum/material/iron = 500) build_path = /obj/item/restraints/handcuffs - category = list("hacked", "Security") + category = list("initial", "Security") /datum/design/receiver name = "Modular Receiver" @@ -804,7 +778,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 15000) build_path = /obj/item/weaponcrafting/receiver - category = list("hacked", "Security") + category = list("initial", "Security") /datum/design/c38_surplus name = "Ammo Box (.38 surplus)" @@ -828,7 +802,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 1000) //Discount for making individually - no box = less metal! build_path = /obj/item/ammo_casing/caseless/foam_dart/riot - category = list("hacked", "Security") + category = list("initial", "Security") /datum/design/riot_darts name = "Foam Riot Dart Box" @@ -836,15 +810,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 50000) //Comes with 40 darts build_path = /obj/item/ammo_box/foambox/riot - category = list("hacked", "Security") - -/datum/design/a357 - name = ".357 Casing" - id = "a357" - build_type = AUTOLATHE - materials = list(/datum/material/iron = 4000) - build_path = /obj/item/ammo_casing/a357 - category = list("emagged", "Security") + category = list("initial", "Security") /datum/design/c10mm_surplus name = "Ammo Box (10mm surplus)" @@ -900,7 +866,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 18000) build_path = /obj/item/kitchen/knife/butcher - category = list("hacked", "Dinnerware") + category = list("initial", "Dinnerware") /datum/design/spraycan name = "Spraycan" @@ -985,14 +951,6 @@ build_path = /obj/item/modular_computer/tablet category = list("initial","Misc") -/datum/design/slime_scanner - name = "Slime Scanner" - id = "slime_scanner" - build_type = AUTOLATHE - materials = list(/datum/material/iron = 300, /datum/material/glass = 200) - build_path = /obj/item/slime_scanner - category = list("initial", "Misc") - /datum/design/pet_carrier name = "Pet Carrier" id = "pet_carrier" @@ -1084,7 +1042,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 100, /datum/material/glass = 50) build_path = /obj/item/toy/gun - category = list("hacked", "Misc") + category = list("initial", "Misc") /datum/design/capbox name = "Box of Cap Gun Shots" @@ -1092,7 +1050,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 20, /datum/material/glass = 5) build_path = /obj/item/toy/ammo/gun - category = list("hacked", "Misc") + category = list("initial", "Misc") /datum/design/toy_balloon name = "Plastic Balloon" @@ -1100,7 +1058,7 @@ build_type = AUTOLATHE materials = list(/datum/material/plastic = 1200) build_path = /obj/item/toy/balloon - category = list("hacked", "Misc") + category = list("initial", "Misc") /datum/design/toy_meteor name = "Plastic Toy Meteor" @@ -1108,15 +1066,7 @@ build_type = AUTOLATHE materials = list(/datum/material/plastic = 1000) build_path = /obj/item/toy/minimeteor - category = list("hacked", "Misc") - -/datum/design/toy_armblade - name = "Plastic Armblade" - id = "toy_armblade" - build_type = AUTOLATHE - materials = list(/datum/material/plastic = 2000) - build_path = /obj/item/toy/foamblade - category = list("hacked", "Misc") + category = list("initial", "Misc") /datum/design/plastic_tree name = "Plastic Potted Plant" @@ -1181,7 +1131,7 @@ build_type = AUTOLATHE materials = list(/datum/material/iron = 20000) build_path = /obj/item/ammo_box/magazine/zip_ammo_9mm - category = list("hacked", "Security") + category = list("initial", "Security") /datum/design/pipedispenser name = "Pipe Dispenser (Machine Board)" diff --git a/code/modules/spells/spell_types/rightandwrong.dm b/code/modules/spells/spell_types/rightandwrong.dm index 55138a36c367..d1f0c920dac8 100644 --- a/code/modules/spells/spell_types/rightandwrong.dm +++ b/code/modules/spells/spell_types/rightandwrong.dm @@ -22,7 +22,6 @@ GLOBAL_LIST_INIT(summoned_guns, list( /obj/item/gun/ballistic/revolver/mateba, /obj/item/gun/ballistic/rifle/illestren, /obj/item/pneumatic_cannon/speargun, - /obj/item/gun/ballistic/automatic/smg/mini_uzi, /obj/item/gun/energy/lasercannon, /obj/item/gun/energy/kinetic_accelerator/crossbow/large, /obj/item/gun/energy/e_gun/nuclear, diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index f8d8a9a49384..0431ae9aad73 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -777,7 +777,11 @@ if (bone_status == BONE_FLAG_NORMAL && body_part & LEGS) // Because arms are not legs owner.set_broken_legs(owner.broken_legs + 1) bone_status = BONE_FLAG_BROKEN - addtimer(CALLBACK(owner, TYPE_PROC_REF(/atom, visible_message), "You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound."), 1 SECONDS) +// addtimer(CALLBACK(src, PROC_REF(break_bone_feedback), 1 SECONDS)) testing sommething + +///obj/item/bodypart/proc/break_bone_feedback() + owner.visible_message("You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound.") + playsound(owner, list('sound/health/bone/bone_break1.ogg','sound/health/bone/bone_break2.ogg','sound/health/bone/bone_break3.ogg','sound/health/bone/bone_break4.ogg','sound/health/bone/bone_break5.ogg','sound/health/bone/bone_break6.ogg'), 100, FALSE, -1) /obj/item/bodypart/proc/fix_bone() // owner.update_inv_splints() breaks diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm index 911eaed20652..670904a5735d 100644 --- a/code/modules/vending/security.dm +++ b/code/modules/vending/security.dm @@ -98,7 +98,7 @@ /obj/item/storage/box/lethalshot = 6, /obj/item/stock_parts/cell/gun = 5, - /obj/item/ammo_box/magazine/uzim9mm = 5, + /obj/item/ammo_box/magazine/spitter_9mm = 5, /obj/item/grenade/c4 = 5, /obj/item/grenade/frag = 5, @@ -107,8 +107,7 @@ premium = list() voucher_items = list( "Tactical Energy Gun" = /obj/item/gun/energy/e_gun/hades, - "Combat Shotgun" = /obj/item/gun/ballistic/shotgun/automatic/combat, - "Type U3 Uzi" = /obj/item/gun/ballistic/automatic/smg/mini_uzi) + "Combat Shotgun" = /obj/item/gun/ballistic/shotgun/automatic/combat) /obj/machinery/vending/security/marine/syndicate icon_state = "syndicate-marine" diff --git a/html/changelogs/archive/2024-08.yml b/html/changelogs/archive/2024-08.yml index f6cc478d7d15..b8ee88c318b3 100644 --- a/html/changelogs/archive/2024-08.yml +++ b/html/changelogs/archive/2024-08.yml @@ -65,3 +65,122 @@ - rscadd: Added a random plushie spawner and a random moth plushie spawner. Thera-Pissed: - rscadd: Chlorine and Hydrogen Chloride gases! Wear your goggles! +2024-08-10: + generalthrax: + - rscadd: Cleanbots now destroy cigarette butts, and dispense enough acid to do + so. + rye-rice: + - bugfix: indie sec hardsuit now shows up +2024-08-11: + firebudgy: + - rscadd: Sprites for several different kinds of scarves for Vox! + - rscadd: Additional sprites for several other items for Vox, such as chest-high + towels and hazard jackets. + - rscadd: Created onmob_neck_vox.dmi file, plus enabled support for it. + generalthrax: + - bugfix: Fixes the Industrial Jacket to be warm (For Real) + phoaly: + - balance: Talos Corpsman has been readded with a different medkit loadout. +2024-08-16: + OrionTheFox: + - rscadd: Suit Storage Units now provide feedback when Decontamination fails to + activate + - imageadd: Suit Storage Units now have a visible red light when Locked - when hacked, + Decontamination now has a red-yellow flashing light to distinguish from the + Locked light. +2024-08-18: + Cloudbreak: + - rscadd: Frontiersman sprites for the Vox! + Gristlebee: + - bugfix: water turfs reagent scooping + - code_imp: span classes to span macros + - refactor: acid turfs repathed under water + - refactor: Pouring out a reagent container is now bound to disarm intent instead + of help intent. + - rscadd: Examine hints for flipped tables + - bugfix: You can no longer phase through a flipped table. + meemofcourse: + - bugfix: Hardliner troopers should no longer spawn as Hardliner pilots +2024-08-20: + Aquidu: + - bugfix: Fixed a bug relating to literal spaghetti code. + zimon9: + - bugfix: fixed a short on the Shetland +2024-08-21: + FalloutFalcon: + - rscadd: Added var to let admins bypass do_teleport vlevel restriction + Sadhorizon: + - bugfix: Ranged frontersman with internals now drops the correct revolver type. +2024-08-22: + rye-rice, Imaginos16: + - rscadd: Adds new belt sprites +2024-08-23: + retlaw34, rye-rice, Apogee-dev: + - rscadd: Adds CLIPs weapons + - rscadd: Adds the New Frontiersmen weapons + - rscadd: 556 ammo box to cargo + - rscadd: inteq sprites should look better +2024-08-24: + generalthrax: + - rscdel: Removed a bunch of random items from fitting in wallets (notably screwdrivers, + cigarettes, suture / mesh / gauze) + - rscadd: Ship keys and derringers can now fit in wallets +2024-08-26: + Aquidu: + - rscadd: Rations now have icons that help show the contents. + - rscadd: Condiments and drink mixes now have colors when full to help show the + contents. + - rscadd: Unique sprites for the ration heater, drink packs ration drinks, and "side" + category rations. + - code_imp: Changes the sound effect for opening the drink ration to the bottle + cap sound to line up with the icon. + DIB-DOG: + - rscadd: Added a standard vest to the armory office locker + - rscadd: Added a Security camera console to the bridge + - balance: Silkenweave jackets and Betzu hats now provide cold protection but no + armor + - balance: Crying Sun now has 4 combustion/2 ion engines instead of 2/4 + - bugfix: Fixes turrets on the Crying Sun and its subshuttle, the Nail. + FalloutFalcon, MrSamu99, Fikou: + - rscadd: a few shipments of MOD control units have found there way to the frontier, + premium versions of existing hard suits with the latest tech! + - rscadd: Ported modsuits from tg, no mapped stuff yet + Gristlebee: + - balance: Durand shield consumes less charge on being hit, passive drain increased. + - bugfix: Mechs consume the correct amount of power on movement. + - refactor: normal_step_energy_drain is now base_step_energy_drain + - rscadd: Grills can now cook food. + - rscdel: Xeno-energy working as grill fuel. + - balance: Outpost food costs + - bugfix: Grills + Ms-Mee: + - bugfix: fixed handle_quirk_conflict behavior + Sadhorizon: + - bugfix: Gloves now show up in the loadout preview. + - rscadd: Brown gloves were added to the loadout. + - tweak: Moved everything out of autolathe contraband - you no longer have to hack + it, ever. + - rscdel: Removed foilhat, flamethrower, electropack, .357 casing and slime scanner + from the autolathe. + - rscadd: Added a bunch of new items to the loadout. + - tweak: Changed the description of red suspenders. + - rscadd: Ihejirka space outpost now has player-accessible cryogenics. + SomeguyManperson: + - balance: wormholes on the overmap no longer decay when used + Thera-Pissed: + - code_imp: TEG efficiency now depends on temperature delta. + Vekter: + - bugfix: Removed an extra fire extinguisher from the crew quarters on the IRMV + Talos. +2024-08-27: + Baystation12, Kapu1178, rye-rice: + - rscadd: Particles! + - rscadd: Bleeding has better feedback + - rscadd: Bone breaking now has sound effects + - rscadd: Getting shot now throws blood squirts! Live through the somme for REAL + this time! + - balance: gibbing no longer destroys your chest, no more legion transfers! + - bugfix: Lava particles should no longer destroy your FPS + FalloutFalcon: + - balance: Ballistic weapons now have a minimum camera shake. + - bugfix: Gunslinger now functions as intended. diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi index f7e2e158d422..aed7e9b4fbf4 100644 Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ diff --git a/icons/effects/magic.dmi b/icons/effects/magic.dmi new file mode 100644 index 000000000000..480332df1349 Binary files /dev/null and b/icons/effects/magic.dmi differ diff --git a/icons/effects/particles/bonfire.dmi b/icons/effects/particles/bonfire.dmi new file mode 100644 index 000000000000..e8e2e36346da Binary files /dev/null and b/icons/effects/particles/bonfire.dmi differ diff --git a/icons/effects/particles/echo.dmi b/icons/effects/particles/echo.dmi new file mode 100644 index 000000000000..60a243a8a7be Binary files /dev/null and b/icons/effects/particles/echo.dmi differ diff --git a/icons/effects/particles/generic.dmi b/icons/effects/particles/generic.dmi new file mode 100644 index 000000000000..dfbb1a47a6ef Binary files /dev/null and b/icons/effects/particles/generic.dmi differ diff --git a/icons/effects/particles/goop.dmi b/icons/effects/particles/goop.dmi new file mode 100644 index 000000000000..673c1a7ad5b6 Binary files /dev/null and b/icons/effects/particles/goop.dmi differ diff --git a/icons/effects/particles/pollen.dmi b/icons/effects/particles/pollen.dmi new file mode 100644 index 000000000000..559c4d1846f6 Binary files /dev/null and b/icons/effects/particles/pollen.dmi differ diff --git a/icons/effects/particles/smoke.dmi b/icons/effects/particles/smoke.dmi new file mode 100644 index 000000000000..4a3239499b96 Binary files /dev/null and b/icons/effects/particles/smoke.dmi differ diff --git a/icons/effects/weather_effects.dmi b/icons/effects/weather_effects.dmi index a8a7185af500..f76e34ec71ec 100644 Binary files a/icons/effects/weather_effects.dmi and b/icons/effects/weather_effects.dmi differ diff --git a/icons/hud/radial.dmi b/icons/hud/radial.dmi new file mode 100644 index 000000000000..9786d403e0de Binary files /dev/null and b/icons/hud/radial.dmi differ diff --git a/icons/mob/actions/actions_mod.dmi b/icons/mob/actions/actions_mod.dmi new file mode 100644 index 000000000000..7f030ad53d42 Binary files /dev/null and b/icons/mob/actions/actions_mod.dmi differ diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi index 7568a1274d66..94728158d0c5 100644 Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi new file mode 100644 index 000000000000..27d4df3b9023 Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ diff --git a/icons/mob/clothing/modsuit/mod_modules.dmi b/icons/mob/clothing/modsuit/mod_modules.dmi new file mode 100644 index 000000000000..11259428cf4d Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_modules.dmi differ diff --git a/icons/mob/species/misc/digitigrade_suits.dmi b/icons/mob/species/misc/digitigrade_suits.dmi index 8bdb115b0370..b622c59dfa73 100644 Binary files a/icons/mob/species/misc/digitigrade_suits.dmi and b/icons/mob/species/misc/digitigrade_suits.dmi differ diff --git a/icons/mob/species/vox/onmob_eyes_vox.dmi b/icons/mob/species/vox/onmob_eyes_vox.dmi index 947496cfb889..3975a170291a 100644 Binary files a/icons/mob/species/vox/onmob_eyes_vox.dmi and b/icons/mob/species/vox/onmob_eyes_vox.dmi differ diff --git a/icons/mob/species/vox/onmob_head_vox.dmi b/icons/mob/species/vox/onmob_head_vox.dmi index 1028f120542d..6b150380891f 100644 Binary files a/icons/mob/species/vox/onmob_head_vox.dmi and b/icons/mob/species/vox/onmob_head_vox.dmi differ diff --git a/icons/mob/species/vox/onmob_neck_vox.dmi b/icons/mob/species/vox/onmob_neck_vox.dmi new file mode 100644 index 000000000000..1877ca277c96 Binary files /dev/null and b/icons/mob/species/vox/onmob_neck_vox.dmi differ diff --git a/icons/mob/species/vox/onmob_suit_vox.dmi b/icons/mob/species/vox/onmob_suit_vox.dmi index 9914cc9786cd..47e2b625c116 100644 Binary files a/icons/mob/species/vox/onmob_suit_vox.dmi and b/icons/mob/species/vox/onmob_suit_vox.dmi differ diff --git a/icons/mob/species/vox/onmob_uniform_vox.dmi b/icons/mob/species/vox/onmob_uniform_vox.dmi index 241b13861b49..04c77c742128 100644 Binary files a/icons/mob/species/vox/onmob_uniform_vox.dmi and b/icons/mob/species/vox/onmob_uniform_vox.dmi differ diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index 86001423aeba..0a8910ae02bd 100644 Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ diff --git a/icons/obj/clothing/belt_overlays.dmi b/icons/obj/clothing/belt_overlays.dmi index 14af5186ae5c..99887c0a7613 100644 Binary files a/icons/obj/clothing/belt_overlays.dmi and b/icons/obj/clothing/belt_overlays.dmi differ diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi index 20cc5db40a44..d1857dbe878d 100644 Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi new file mode 100644 index 000000000000..d2d9e0c72e37 Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi new file mode 100644 index 000000000000..a6be94284af8 Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_construction.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi new file mode 100644 index 000000000000..69affa3fa499 Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_modules.dmi differ diff --git a/icons/obj/food/ration.dmi b/icons/obj/food/ration.dmi index 5bcf1f2b490b..42cc013cc140 100644 Binary files a/icons/obj/food/ration.dmi and b/icons/obj/food/ration.dmi differ diff --git a/icons/obj/guns/48x32guns.dmi b/icons/obj/guns/48x32guns.dmi index 960b9ec448af..708882c163aa 100644 Binary files a/icons/obj/guns/48x32guns.dmi and b/icons/obj/guns/48x32guns.dmi differ diff --git a/icons/obj/guns/attachments.dmi b/icons/obj/guns/attachments.dmi index 29ae084d5759..c06a2c7e109c 100644 Binary files a/icons/obj/guns/attachments.dmi and b/icons/obj/guns/attachments.dmi differ diff --git a/icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi b/icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi index d87a6f3c8433..e0567289abf6 100644 Binary files a/icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi and b/icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi differ diff --git a/icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi b/icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi index 7673c2f6d642..78e16bad6f98 100644 Binary files a/icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi and b/icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi differ diff --git a/icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi b/icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi index 4f9158d2d36d..fea6178e903e 100644 Binary files a/icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi and b/icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi differ diff --git a/icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi b/icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi index 4549f30f4ff9..2d5103ec36cc 100644 Binary files a/icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi and b/icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi differ diff --git a/icons/obj/guns/manufacturer/frontier_import/48x32.dmi b/icons/obj/guns/manufacturer/frontier_import/48x32.dmi index 149793c43c38..2f2db3d07ad5 100644 Binary files a/icons/obj/guns/manufacturer/frontier_import/48x32.dmi and b/icons/obj/guns/manufacturer/frontier_import/48x32.dmi differ diff --git a/icons/obj/guns/manufacturer/frontier_import/lefthand.dmi b/icons/obj/guns/manufacturer/frontier_import/lefthand.dmi index 33b3381bdfe0..eb6c552627ee 100644 Binary files a/icons/obj/guns/manufacturer/frontier_import/lefthand.dmi and b/icons/obj/guns/manufacturer/frontier_import/lefthand.dmi differ diff --git a/icons/obj/guns/manufacturer/frontier_import/onmob.dmi b/icons/obj/guns/manufacturer/frontier_import/onmob.dmi index a0706579ccb5..274aa3a15036 100644 Binary files a/icons/obj/guns/manufacturer/frontier_import/onmob.dmi and b/icons/obj/guns/manufacturer/frontier_import/onmob.dmi differ diff --git a/icons/obj/guns/manufacturer/frontier_import/righthand.dmi b/icons/obj/guns/manufacturer/frontier_import/righthand.dmi index 73945b8524df..406c40e66ac6 100644 Binary files a/icons/obj/guns/manufacturer/frontier_import/righthand.dmi and b/icons/obj/guns/manufacturer/frontier_import/righthand.dmi differ diff --git a/icons/obj/guns/manufacturer/hunterspride/48x32.dmi b/icons/obj/guns/manufacturer/hunterspride/48x32.dmi index 19b4202da78a..cdff0b09ea6e 100644 Binary files a/icons/obj/guns/manufacturer/hunterspride/48x32.dmi and b/icons/obj/guns/manufacturer/hunterspride/48x32.dmi differ diff --git a/icons/obj/guns/manufacturer/inteq/48x32.dmi b/icons/obj/guns/manufacturer/inteq/48x32.dmi index e7deb0f12ce0..021ff448ecb5 100644 Binary files a/icons/obj/guns/manufacturer/inteq/48x32.dmi and b/icons/obj/guns/manufacturer/inteq/48x32.dmi differ diff --git a/icons/obj/guns/manufacturer/inteq/lefthand.dmi b/icons/obj/guns/manufacturer/inteq/lefthand.dmi index 19335eb44ff9..84707c2e5cdc 100644 Binary files a/icons/obj/guns/manufacturer/inteq/lefthand.dmi and b/icons/obj/guns/manufacturer/inteq/lefthand.dmi differ diff --git a/icons/obj/guns/manufacturer/inteq/onmob.dmi b/icons/obj/guns/manufacturer/inteq/onmob.dmi index f402ffd24e2c..a33746030b43 100644 Binary files a/icons/obj/guns/manufacturer/inteq/onmob.dmi and b/icons/obj/guns/manufacturer/inteq/onmob.dmi differ diff --git a/icons/obj/guns/manufacturer/inteq/righthand.dmi b/icons/obj/guns/manufacturer/inteq/righthand.dmi index 33d087f394f1..481731992655 100644 Binary files a/icons/obj/guns/manufacturer/inteq/righthand.dmi and b/icons/obj/guns/manufacturer/inteq/righthand.dmi differ diff --git a/icons/obj/machines/suit_storage.dmi b/icons/obj/machines/suit_storage.dmi index a40d04f500c6..d58a9ef3c079 100644 Binary files a/icons/obj/machines/suit_storage.dmi and b/icons/obj/machines/suit_storage.dmi differ diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi index f5f04901af2a..af3c5cd4be2e 100644 Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ diff --git a/shiptest.dme b/shiptest.dme index f170643beb9e..09e6f8be9c16 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -25,6 +25,7 @@ #include "code\__DEFINES\_tick.dm" #include "code\__DEFINES\access.dm" #include "code\__DEFINES\achievements.dm" +#include "code\__DEFINES\actions.dm" #include "code\__DEFINES\admin.dm" #include "code\__DEFINES\anomalies.dm" #include "code\__DEFINES\antagonists.dm" @@ -66,6 +67,7 @@ #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\forensics.dm" +#include "code\__DEFINES\generators.dm" #include "code\__DEFINES\guns.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\icon_smoothing.dm" @@ -94,6 +96,7 @@ #include "code\__DEFINES\menu.dm" #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\mobs.dm" +#include "code\__DEFINES\mod.dm" #include "code\__DEFINES\monkeys.dm" #include "code\__DEFINES\move_force.dm" #include "code\__DEFINES\movement.dm" @@ -103,6 +106,7 @@ #include "code\__DEFINES\obj_flags.dm" #include "code\__DEFINES\overmap.dm" #include "code\__DEFINES\paper.dm" +#include "code\__DEFINES\particles.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\plumbing.dm" @@ -156,6 +160,9 @@ #include "code\__DEFINES\wires.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" +#include "code\__DEFINES\dcs\signals\signals_mod.dm" +#include "code\__DEFINES\dcs\signals\signals_storage.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_carbon.dm" #include "code\__DEFINES\dcs\signals\signals.dm" #include "code\__DEFINES\dcs\signals\signals_obj\signals_object.dm" #include "code\__DEFINES\dcs\signals\signals_obj\signals_item\signals_clothing.dm" @@ -169,9 +176,11 @@ #include "code\__HELPERS\_auxtools_api.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" +#include "code\__HELPERS\_planes.dm" #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\areas.dm" #include "code\__HELPERS\AStar.dm" +#include "code\__HELPERS\atoms.dm" #include "code\__HELPERS\bindings.dm" #include "code\__HELPERS\bitflag_lists.dm" #include "code\__HELPERS\chat.dm" @@ -183,12 +192,14 @@ #include "code\__HELPERS\files.dm" #include "code\__HELPERS\filters.dm" #include "code\__HELPERS\game.dm" +#include "code\__HELPERS\generators.dm" #include "code\__HELPERS\global_lists.dm" #include "code\__HELPERS\heap.dm" #include "code\__HELPERS\icon_smoothing.dm" #include "code\__HELPERS\icons.dm" #include "code\__HELPERS\level_traits.dm" #include "code\__HELPERS\lighting.dm" +#include "code\__HELPERS\maths.dm" #include "code\__HELPERS\matrices.dm" #include "code\__HELPERS\mobs.dm" #include "code\__HELPERS\mouse_control.dm" @@ -505,6 +516,7 @@ #include "code\datums\components\hot_ice.dm" #include "code\datums\components\igniter.dm" #include "code\datums\components\infective.dm" +#include "code\datums\components\jetpack.dm" #include "code\datums\components\jousting.dm" #include "code\datums\components\knockback.dm" #include "code\datums\components\knockoff.dm" @@ -532,6 +544,7 @@ #include "code\datums\components\remote_materials.dm" #include "code\datums\components\riding.dm" #include "code\datums\components\rotation.dm" +#include "code\datums\components\shielded.dm" #include "code\datums\components\shrink.dm" #include "code\datums\components\sitcomlaughter.dm" #include "code\datums\components\sizzle.dm" @@ -653,6 +666,7 @@ #include "code\datums\elements\digitalcamo.dm" #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\embed.dm" +#include "code\datums\elements\empprotection.dm" #include "code\datums\elements\firestacker.dm" #include "code\datums\elements\forced_gravity.dm" #include "code\datums\elements\lazy_fishing_spot.dm" @@ -803,6 +817,7 @@ #include "code\datums\wires\fax.dm" #include "code\datums\wires\microwave.dm" #include "code\datums\wires\mines.dm" +#include "code\datums\wires\mod.dm" #include "code\datums\wires\mulebot.dm" #include "code\datums\wires\particle_accelerator.dm" #include "code\datums\wires\r_n_d.dm" @@ -1109,6 +1124,12 @@ #include "code\game\objects\effects\effect_system\effects_smoke.dm" #include "code\game\objects\effects\effect_system\effects_sparks.dm" #include "code\game\objects\effects\effect_system\effects_water.dm" +#include "code\game\objects\effects\particles\acid.dm" +#include "code\game\objects\effects\particles\fire.dm" +#include "code\game\objects\effects\particles\misc.dm" +#include "code\game\objects\effects\particles\slime.dm" +#include "code\game\objects\effects\particles\smoke.dm" +#include "code\game\objects\effects\particles\water.dm" #include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\bundle.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" @@ -2817,6 +2838,30 @@ #include "code\modules\mob\living\simple_animal\slime\slime.dm" #include "code\modules\mob\living\simple_animal\slime\slime_say.dm" #include "code\modules\mob\living\simple_animal\slime\subtypes.dm" +#include "code\modules\mod\mod_actions.dm" +#include "code\modules\mod\mod_activation.dm" +#include "code\modules\mod\mod_ai.dm" +#include "code\modules\mod\mod_clothes.dm" +#include "code\modules\mod\mod_construction.dm" +#include "code\modules\mod\mod_control.dm" +#include "code\modules\mod\mod_core.dm" +#include "code\modules\mod\mod_paint.dm" +#include "code\modules\mod\mod_theme.dm" +#include "code\modules\mod\mod_types.dm" +#include "code\modules\mod\mod_ui.dm" +#include "code\modules\mod\modules\_module.dm" +#include "code\modules\mod\modules\modules_antag.dm" +#include "code\modules\mod\modules\modules_engineering.dm" +#include "code\modules\mod\modules\modules_general.dm" +#include "code\modules\mod\modules\modules_maint.dm" +#include "code\modules\mod\modules\modules_medical.dm" +#include "code\modules\mod\modules\modules_ninja.dm" +#include "code\modules\mod\modules\modules_science.dm" +#include "code\modules\mod\modules\modules_security.dm" +#include "code\modules\mod\modules\modules_service.dm" +#include "code\modules\mod\modules\modules_storage.dm" +#include "code\modules\mod\modules\modules_supply.dm" +#include "code\modules\mod\modules\modules_visor.dm" #include "code\modules\mob_spawner\burrow.dm" #include "code\modules\mob_spawner\hivebot.dm" #include "code\modules\mob_spawner\spawner.dm" @@ -3082,7 +3127,10 @@ #include "code\modules\projectiles\guns\energy\pulse.dm" #include "code\modules\projectiles\guns\energy\special.dm" #include "code\modules\projectiles\guns\energy\stun.dm" +#include "code\modules\projectiles\guns\manufacturer\clip_lanchester\ballistics.dm" +#include "code\modules\projectiles\guns\manufacturer\clip_lanchester\lasers.dm" #include "code\modules\projectiles\guns\manufacturer\etherbor\energy_gunsword.dm" +#include "code\modules\projectiles\guns\manufacturer\frontier_import\ballistics.dm" #include "code\modules\projectiles\guns\misc\beam_rifle.dm" #include "code\modules\projectiles\guns\misc\blastcannon.dm" #include "code\modules\projectiles\guns\misc\bow.dm" diff --git a/sound/ambience/storm_indoors.ogg b/sound/ambience/storm_indoors.ogg new file mode 100644 index 000000000000..62e9014a05ff Binary files /dev/null and b/sound/ambience/storm_indoors.ogg differ diff --git a/sound/ambience/storm_outdoors.ogg b/sound/ambience/storm_outdoors.ogg new file mode 100644 index 000000000000..35ae8e5297d6 Binary files /dev/null and b/sound/ambience/storm_outdoors.ogg differ diff --git a/sound/effects/splatter.ogg b/sound/effects/splatter.ogg new file mode 100644 index 000000000000..1c678cfe1268 Binary files /dev/null and b/sound/effects/splatter.ogg differ diff --git a/sound/health/bone/bone_break1.ogg b/sound/health/bone/bone_break1.ogg new file mode 100644 index 000000000000..dd2d22ec792b Binary files /dev/null and b/sound/health/bone/bone_break1.ogg differ diff --git a/sound/health/bone/bone_break2.ogg b/sound/health/bone/bone_break2.ogg new file mode 100644 index 000000000000..aa2537f894de Binary files /dev/null and b/sound/health/bone/bone_break2.ogg differ diff --git a/sound/health/bone/bone_break3.ogg b/sound/health/bone/bone_break3.ogg new file mode 100644 index 000000000000..9f66324be3b2 Binary files /dev/null and b/sound/health/bone/bone_break3.ogg differ diff --git a/sound/health/bone/bone_break4.ogg b/sound/health/bone/bone_break4.ogg new file mode 100644 index 000000000000..bbdfac1ecff3 Binary files /dev/null and b/sound/health/bone/bone_break4.ogg differ diff --git a/sound/health/bone/bone_break5.ogg b/sound/health/bone/bone_break5.ogg new file mode 100644 index 000000000000..dfee0e9baa72 Binary files /dev/null and b/sound/health/bone/bone_break5.ogg differ diff --git a/sound/health/bone/bone_break6.ogg b/sound/health/bone/bone_break6.ogg new file mode 100644 index 000000000000..d41cc8d7cf54 Binary files /dev/null and b/sound/health/bone/bone_break6.ogg differ diff --git a/sound/items/modsuit/atrocinator_step.ogg b/sound/items/modsuit/atrocinator_step.ogg new file mode 100644 index 000000000000..deda85ac354b Binary files /dev/null and b/sound/items/modsuit/atrocinator_step.ogg differ diff --git a/sound/items/modsuit/ballin.ogg b/sound/items/modsuit/ballin.ogg new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sound/items/modsuit/ballout.ogg b/sound/items/modsuit/ballout.ogg new file mode 100644 index 000000000000..f911f1a6a61d Binary files /dev/null and b/sound/items/modsuit/ballout.ogg differ diff --git a/sound/items/modsuit/flamethrower.ogg b/sound/items/modsuit/flamethrower.ogg new file mode 100644 index 000000000000..447245d50b6e Binary files /dev/null and b/sound/items/modsuit/flamethrower.ogg differ diff --git a/sound/items/modsuit/inflate_bloon.ogg b/sound/items/modsuit/inflate_bloon.ogg new file mode 100644 index 000000000000..9b030d66ced7 Binary files /dev/null and b/sound/items/modsuit/inflate_bloon.ogg differ diff --git a/sound/items/modsuit/loader_charge.ogg b/sound/items/modsuit/loader_charge.ogg new file mode 100644 index 000000000000..61d5531f72ed Binary files /dev/null and b/sound/items/modsuit/loader_charge.ogg differ diff --git a/sound/items/modsuit/loader_launch.ogg b/sound/items/modsuit/loader_launch.ogg new file mode 100644 index 000000000000..513118f3c682 Binary files /dev/null and b/sound/items/modsuit/loader_launch.ogg differ diff --git a/sound/items/modsuit/magnetic_harness.ogg b/sound/items/modsuit/magnetic_harness.ogg new file mode 100644 index 000000000000..3d19fccc5698 Binary files /dev/null and b/sound/items/modsuit/magnetic_harness.ogg differ diff --git a/sound/items/modsuit/rewinder.ogg b/sound/items/modsuit/rewinder.ogg new file mode 100644 index 000000000000..2587562dc117 Binary files /dev/null and b/sound/items/modsuit/rewinder.ogg differ diff --git a/sound/items/modsuit/springlock.ogg b/sound/items/modsuit/springlock.ogg new file mode 100644 index 000000000000..8d0013d26300 Binary files /dev/null and b/sound/items/modsuit/springlock.ogg differ diff --git a/sound/items/modsuit/tem_shot.ogg b/sound/items/modsuit/tem_shot.ogg new file mode 100644 index 000000000000..50905b95f112 Binary files /dev/null and b/sound/items/modsuit/tem_shot.ogg differ diff --git a/sound/items/modsuit/time_anchor_set.ogg b/sound/items/modsuit/time_anchor_set.ogg new file mode 100644 index 000000000000..457f8e6dbaee Binary files /dev/null and b/sound/items/modsuit/time_anchor_set.ogg differ diff --git a/sound/mecha/hydraulic.ogg b/sound/mecha/hydraulic.ogg new file mode 100644 index 000000000000..3281ed2dc0f0 Binary files /dev/null and b/sound/mecha/hydraulic.ogg differ diff --git a/sound/weapons/gun/hmg/cm40.ogg b/sound/weapons/gun/hmg/cm40.ogg new file mode 100644 index 000000000000..b45507d29a0c Binary files /dev/null and b/sound/weapons/gun/hmg/cm40.ogg differ diff --git a/sound/weapons/gun/hmg/cm40_cocked.ogg b/sound/weapons/gun/hmg/cm40_cocked.ogg new file mode 100644 index 000000000000..694d4772e980 Binary files /dev/null and b/sound/weapons/gun/hmg/cm40_cocked.ogg differ diff --git a/sound/weapons/gun/hmg/cm40_reload.ogg b/sound/weapons/gun/hmg/cm40_reload.ogg new file mode 100644 index 000000000000..490bbd4fe783 Binary files /dev/null and b/sound/weapons/gun/hmg/cm40_reload.ogg differ diff --git a/sound/weapons/gun/hmg/cm40_unload.ogg b/sound/weapons/gun/hmg/cm40_unload.ogg new file mode 100644 index 000000000000..ae9e31aee537 Binary files /dev/null and b/sound/weapons/gun/hmg/cm40_unload.ogg differ diff --git a/sound/weapons/gun/hmg/shredder.ogg b/sound/weapons/gun/hmg/shredder.ogg new file mode 100644 index 000000000000..38ddfc247eea Binary files /dev/null and b/sound/weapons/gun/hmg/shredder.ogg differ diff --git a/sound/weapons/gun/hmg/shredder_cocked.ogg b/sound/weapons/gun/hmg/shredder_cocked.ogg new file mode 100644 index 000000000000..95130493d4c3 Binary files /dev/null and b/sound/weapons/gun/hmg/shredder_cocked.ogg differ diff --git a/sound/weapons/gun/hmg/shredder_cocked_alt.ogg b/sound/weapons/gun/hmg/shredder_cocked_alt.ogg new file mode 100644 index 000000000000..a73fd1b13b73 Binary files /dev/null and b/sound/weapons/gun/hmg/shredder_cocked_alt.ogg differ diff --git a/sound/weapons/gun/hmg/shredder_reload.ogg b/sound/weapons/gun/hmg/shredder_reload.ogg new file mode 100644 index 000000000000..c7614184d3dd Binary files /dev/null and b/sound/weapons/gun/hmg/shredder_reload.ogg differ diff --git a/sound/weapons/gun/hmg/shredder_unload.ogg b/sound/weapons/gun/hmg/shredder_unload.ogg new file mode 100644 index 000000000000..01e6229aba62 Binary files /dev/null and b/sound/weapons/gun/hmg/shredder_unload.ogg differ diff --git a/sound/weapons/gun/pistol/cm23.ogg b/sound/weapons/gun/pistol/cm23.ogg new file mode 100644 index 000000000000..f207a4cda894 Binary files /dev/null and b/sound/weapons/gun/pistol/cm23.ogg differ diff --git a/sound/weapons/gun/pistol/cm70.ogg b/sound/weapons/gun/pistol/cm70.ogg new file mode 100644 index 000000000000..2b672fcfb352 Binary files /dev/null and b/sound/weapons/gun/pistol/cm70.ogg differ diff --git a/sound/weapons/gun/pistol/mauler.ogg b/sound/weapons/gun/pistol/mauler.ogg new file mode 100644 index 000000000000..70fb1f9f6eca Binary files /dev/null and b/sound/weapons/gun/pistol/mauler.ogg differ diff --git a/sound/weapons/gun/rifle/cm82.ogg b/sound/weapons/gun/rifle/cm82.ogg new file mode 100644 index 000000000000..a1bf69ae00f2 Binary files /dev/null and b/sound/weapons/gun/rifle/cm82.ogg differ diff --git a/sound/weapons/gun/rifle/cm82_reload.ogg b/sound/weapons/gun/rifle/cm82_reload.ogg new file mode 100644 index 000000000000..133ad5aede3a Binary files /dev/null and b/sound/weapons/gun/rifle/cm82_reload.ogg differ diff --git a/sound/weapons/gun/rifle/cm82_unload.ogg b/sound/weapons/gun/rifle/cm82_unload.ogg new file mode 100644 index 000000000000..af0b43ecef90 Binary files /dev/null and b/sound/weapons/gun/rifle/cm82_unload.ogg differ diff --git a/sound/weapons/gun/rifle/f4.ogg b/sound/weapons/gun/rifle/f4.ogg new file mode 100644 index 000000000000..75c571db1881 Binary files /dev/null and b/sound/weapons/gun/rifle/f4.ogg differ diff --git a/sound/weapons/gun/rifle/gal.ogg b/sound/weapons/gun/rifle/gal.ogg deleted file mode 100644 index 25402e36cc61..000000000000 Binary files a/sound/weapons/gun/rifle/gal.ogg and /dev/null differ diff --git a/sound/weapons/gun/smg/cm5.ogg b/sound/weapons/gun/smg/cm5.ogg new file mode 100644 index 000000000000..6c363db926f6 Binary files /dev/null and b/sound/weapons/gun/smg/cm5.ogg differ diff --git a/sound/weapons/gun/smg/pounder.ogg b/sound/weapons/gun/smg/pounder.ogg new file mode 100644 index 000000000000..c9e6faf8e1ef Binary files /dev/null and b/sound/weapons/gun/smg/pounder.ogg differ diff --git a/sound/weapons/gun/smg/pounder_cocked.ogg b/sound/weapons/gun/smg/pounder_cocked.ogg new file mode 100644 index 000000000000..76929be2f066 Binary files /dev/null and b/sound/weapons/gun/smg/pounder_cocked.ogg differ diff --git a/sound/weapons/gun/smg/pounder_reload.ogg b/sound/weapons/gun/smg/pounder_reload.ogg new file mode 100644 index 000000000000..55bcbc5aa87e Binary files /dev/null and b/sound/weapons/gun/smg/pounder_reload.ogg differ diff --git a/sound/weapons/gun/smg/pounder_unload.ogg b/sound/weapons/gun/smg/pounder_unload.ogg new file mode 100644 index 000000000000..5ce78fc258a2 Binary files /dev/null and b/sound/weapons/gun/smg/pounder_unload.ogg differ diff --git a/sound/weapons/gun/smg/spitter.ogg b/sound/weapons/gun/smg/spitter.ogg new file mode 100644 index 000000000000..81fd0263cb2a Binary files /dev/null and b/sound/weapons/gun/smg/spitter.ogg differ diff --git a/sound/weapons/gun/smg/spitter_cocked.ogg b/sound/weapons/gun/smg/spitter_cocked.ogg new file mode 100644 index 000000000000..b3d29c6f690a Binary files /dev/null and b/sound/weapons/gun/smg/spitter_cocked.ogg differ diff --git a/sound/weapons/gun/smg/spitter_reload.ogg b/sound/weapons/gun/smg/spitter_reload.ogg new file mode 100644 index 000000000000..4842c567de2a Binary files /dev/null and b/sound/weapons/gun/smg/spitter_reload.ogg differ diff --git a/sound/weapons/gun/smg/spitter_unload.ogg b/sound/weapons/gun/smg/spitter_unload.ogg new file mode 100644 index 000000000000..dffebe51b177 Binary files /dev/null and b/sound/weapons/gun/smg/spitter_unload.ogg differ diff --git a/sound/weapons/gun/sniper/cmf90.ogg b/sound/weapons/gun/sniper/cmf90.ogg new file mode 100644 index 000000000000..9468bfcc1b94 Binary files /dev/null and b/sound/weapons/gun/sniper/cmf90.ogg differ diff --git a/tgui/packages/tgui/interfaces/MODpaint.js b/tgui/packages/tgui/interfaces/MODpaint.js new file mode 100644 index 000000000000..22d14b656113 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MODpaint.js @@ -0,0 +1,161 @@ +import { useBackend } from '../backend'; +import { + Box, + Stack, + Section, + ByondUi, + Slider, + Flex, + Button, +} from '../components'; +import { Window } from '../layouts'; +import { capitalize } from 'common/string'; + +const colorToMatrix = (param) => { + switch (param) { + case 'red': + return [ + 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0.25, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ]; + case 'yellow': + return [ + 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0.25, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, + 0, + ]; + case 'green': + return [ + 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ]; + case 'teal': + return [ + 0.25, 0.25, 0.25, 0, 0, 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 1, 0, 0, + 0, 0, + ]; + case 'blue': + return [ + 0.25, 0, 0.25, 0, 0, 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ]; + case 'purple': + return [ + 0.5, 0, 0.5, 0, 0.25, 0.5, 0.25, 0, 0.5, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, + 0, + ]; + } +}; + +const displayText = (param) => { + switch (param) { + case 'r': + return 'Red'; + case 'g': + return 'Green'; + case 'b': + return 'Blue'; + } +}; + +export const MODpaint = (props, context) => { + const { act, data } = useBackend(context); + const { mapRef, currentColor } = data; + const [ + [rr, rg, rb, ra], + [gr, gg, gb, ga], + [br, bg, bb, ba], + [ar, ag, ab, aa], + [cr, cg, cb, ca], + ] = currentColor; + const presets = ['red', 'yellow', 'green', 'teal', 'blue', 'purple']; + const prefixes = ['r', 'g', 'b']; + return ( +