diff --git a/_maps/map_files/Campaign maps/orion_2/orionoutpost_2.dmm b/_maps/map_files/Campaign maps/orion_2/orionoutpost_2.dmm
index d8b45142d7a14..6ffe79853d85b 100644
--- a/_maps/map_files/Campaign maps/orion_2/orionoutpost_2.dmm
+++ b/_maps/map_files/Campaign maps/orion_2/orionoutpost_2.dmm
@@ -2863,7 +2863,7 @@
/area/orion_outpost/ground/underground/caveN)
"nJ" = (
/obj/item/ammo_magazine/tank/ltb_cannon,
-/obj/item/ammo_magazine/tank/ltaaap_minigun,
+/obj/item/ammo_magazine/tank/ltaap_minigun,
/turf/open/floor/mainship/black{
dir = 8
},
@@ -6740,7 +6740,7 @@
/area/orion_outpost/ground/outpostcent)
"Gr" = (
/obj/item/ammo_magazine/tank/ltb_cannon,
-/obj/item/ammo_magazine/tank/ltaaap_minigun,
+/obj/item/ammo_magazine/tank/ltaap_minigun,
/turf/open/floor/mainship/black/full,
/area/orion_outpost/surface/building/ammodepot)
"Gs" = (
diff --git a/_maps/map_files/DaedalusPrison/DaedalusPrison.dmm b/_maps/map_files/DaedalusPrison/DaedalusPrison.dmm
index 7aa821a640fdd..a5c48b10c13b9 100644
--- a/_maps/map_files/DaedalusPrison/DaedalusPrison.dmm
+++ b/_maps/map_files/DaedalusPrison/DaedalusPrison.dmm
@@ -300,7 +300,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/sportstorage)
"anl" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/red{
dir = 1
},
@@ -310,7 +310,7 @@
/turf/open/floor/tile/green/greentaupe,
/area/daedalusprison/inside/hydroponics)
"anT" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/turf/open/floor/prison/green{
dir = 6
@@ -339,10 +339,6 @@
},
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
-"aoX" = (
-/obj/machinery/miner/damaged/platinum,
-/turf/open/floor/plating/ground/ice,
-/area/daedalusprison/caves/northeast)
"apf" = (
/turf/closed/wall/prison,
/area/daedalusprison/inside/southclass)
@@ -373,7 +369,7 @@
},
/area/daedalusprison/inside/security/office)
"aqf" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/firstaid,
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical)
@@ -461,7 +457,7 @@
/turf/open/floor/prison/darkred,
/area/daedalusprison/inside/centralhalls)
"auq" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/bunker)
"auD" = (
@@ -500,7 +496,7 @@
},
/area/daedalusprison/inside/sportstorage)
"avR" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/reagent_containers/spray/cleaner,
/turf/open/floor/prison/whitegreen{
dir = 1
@@ -566,7 +562,7 @@
/turf/closed/wall,
/area/daedalusprison/inside/colonydorms)
"ays" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_rifle,
/obj/item/cell/lasgun/lasrifle,
/obj/effect/landmark/weed_node,
@@ -626,7 +622,7 @@
},
/area/daedalusprison/inside/security/office)
"aAN" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/beaker/largeweighted,
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical)
@@ -731,7 +727,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/habitationsouth)
"aFk" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/heal_pack,
/obj/machinery/light,
/turf/open/floor/prison/sterilewhite,
@@ -766,6 +762,10 @@
dir = 4
},
/area/daedalusprison/inside/pmcdropship)
+"aGY" = (
+/obj/structure/girder,
+/turf/open/floor/plating,
+/area/daedalusprison/inside/prisonshower)
"aHc" = (
/obj/effect/turf_decal/tracks/wheels/bloody{
dir = 9
@@ -777,7 +777,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
"aHB" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/tool/surgery/suture,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical/treatment)
@@ -937,7 +937,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/centralhalls)
"aMO" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/door/poddoor/shutters{
id = "DPsec"
},
@@ -1292,7 +1292,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/colonydorms)
"bfk" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/tool/hand_labeler,
/turf/open/floor/prison,
/area/daedalusprison/inside/lobby)
@@ -1335,7 +1335,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/easternhalls)
"bkc" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/book/manual/engineering_guide,
/turf/open/floor/tile/red/full,
/area/daedalusprison/inside/bunker/east)
@@ -1500,7 +1500,7 @@
},
/area/daedalusprison/inside/pmcdropship)
"bqd" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 4
},
@@ -1622,7 +1622,7 @@
},
/area/daedalusprison/inside/seccheckpoint)
"bui" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 5
},
@@ -1873,7 +1873,7 @@
/turf/open/floor/plating/ground/snow/layer0,
/area/daedalusprison/outside/south)
"bDu" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_rifle,
/obj/item/cell/lasgun/lasrifle,
/turf/open/floor/prison,
@@ -1956,8 +1956,6 @@
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"bGE" = (
-/obj/structure/cable,
-/obj/machinery/power/apc,
/turf/open/floor/prison/darkred{
dir = 1
},
@@ -2141,7 +2139,7 @@
},
/area/daedalusprison/inside/lobby)
"bQa" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison/darkred/full,
/area/daedalusprison/inside/security/easternbooth)
@@ -2150,7 +2148,6 @@
dir = 5
},
/obj/structure/cable,
-/obj/machinery/miner/damaged,
/turf/open/floor/prison,
/area/daedalusprison/inside/execution)
"bRq" = (
@@ -2236,7 +2233,7 @@
},
/area/daedalusprison/inside/habitationnorth)
"bUX" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/defibrillator,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -2357,7 +2354,7 @@
},
/area/daedalusprison/inside/security/secbreakroom)
"bZU" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/obj/structure/window/reinforced/tinted{
dir = 8
@@ -2365,7 +2362,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/westcomputerlab)
"bZX" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/firstaid/regular{
pixel_x = 5;
pixel_y = 3
@@ -2582,7 +2579,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/cargo)
"cjT" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/box/gloves{
pixel_x = 6;
pixel_y = 10
@@ -2724,7 +2721,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/security/office)
"cmU" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/syringe,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -2885,7 +2882,7 @@
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"cww" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/security/medsec)
"cwy" = (
@@ -3094,7 +3091,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/chapel)
"cEp" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/med_data/laptop,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -3441,7 +3438,7 @@
},
/area/daedalusprison/inside/habitationnorth)
"cWx" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_rifle,
/obj/item/cell/lasgun/lasrifle,
/turf/open/floor/tile/dark2,
@@ -3972,7 +3969,7 @@
/turf/closed/wall/prison,
/area/daedalusprison/inside/northclass)
"dqo" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/operating,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -4055,7 +4052,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/centralhalls)
"dtu" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/trash/plate,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/bunker/center)
@@ -4092,7 +4089,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/centralhalls)
"dvf" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/effect/landmark/weed_node,
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/south)
@@ -4243,6 +4240,13 @@
dir = 4
},
/area/daedalusprison/inside/security/office)
+"dCb" = (
+/obj/structure/fence/broken,
+/obj/structure/platform/rockcliff/icycliff/nondense{
+ dir = 8
+ },
+/turf/open/floor/plating/ground/snow/layer2,
+/area/daedalusprison/outside/south)
"dCh" = (
/obj/structure/filingcabinet/security,
/obj/machinery/light,
@@ -4461,13 +4465,12 @@
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 5
},
-/obj/structure/cable,
/obj/effect/ai_node,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
"dLr" = (
/obj/effect/decal/cleanable/dirt,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/surgical_tray,
/turf/open/floor/tile/dark,
/area/daedalusprison/inside/medical)
@@ -4494,7 +4497,7 @@
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical)
"dMn" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/clothing/gloves/insulated,
/obj/effect/decal/cleanable/cobweb{
dir = 4
@@ -4628,8 +4631,12 @@
/obj/effect/landmark/weed_node,
/turf/open/floor/prison,
/area/daedalusprison/inside/security/cameras)
+"dQI" = (
+/obj/structure/cable,
+/turf/open/floor/prison/plate,
+/area/daedalusprison/inside/security/warden)
"dQO" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/darkred{
dir = 10
},
@@ -4945,7 +4952,7 @@
dir = 8
},
/obj/structure/window/reinforced/tinted,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
"eey" = (
@@ -4973,6 +4980,10 @@
/obj/effect/ai_node,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/habitationnorth)
+"efq" = (
+/obj/effect/acid_hole,
+/turf/closed/wall/prison,
+/area/daedalusprison/inside/freezer)
"efF" = (
/obj/effect/spawner/random/misc/trash,
/turf/open/floor/tile/dark/red2{
@@ -5002,7 +5013,7 @@
/turf/open/floor/prison/darkred/full,
/area/daedalusprison/inside/centralhalls)
"egs" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/bloodpack,
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical)
@@ -5125,7 +5136,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/westcomputerlab)
"emm" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/heal_pack,
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical/treatment)
@@ -5250,7 +5261,7 @@
/turf/open/shuttle/dropship/three,
/area/daedalusprison/inside/pmcdropship)
"eqA" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison/red,
/area/daedalusprison/inside/security/cameras)
@@ -5285,7 +5296,7 @@
},
/area/daedalusprison/inside/mining)
"eri" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/firstaid,
/turf/open/floor/prison/whitegreen{
dir = 10
@@ -5451,7 +5462,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/bar)
"eAp" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/firstaid/adv,
/turf/open/floor/prison/whitegreen{
dir = 4
@@ -5507,7 +5518,7 @@
},
/area/daedalusprison/caves/research)
"eCq" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_mlaser,
/obj/item/cell/lasgun/lasrifle,
/turf/open/floor/prison,
@@ -5656,7 +5667,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/prisonshower)
"eIf" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/engineering/tool,
/obj/machinery/light,
/turf/open/floor/tile/dark2,
@@ -5767,7 +5778,7 @@
},
/area/daedalusprison/inside/pmcdropship)
"eOa" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/red{
dir = 4
},
@@ -6033,7 +6044,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/colonydorms)
"eYR" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/clothing/sunglasses,
/obj/effect/landmark/weed_node,
/turf/open/floor/prison/red{
@@ -6488,7 +6499,7 @@
},
/area/daedalusprison/inside/mechanicshop)
"fqe" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/security{
network = list("PRISON")
},
@@ -6570,7 +6581,7 @@
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"ftS" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/red/full,
/area/daedalusprison/inside/bunker/center)
"ftT" = (
@@ -6611,7 +6622,7 @@
},
/area/daedalusprison/inside/centralhalls)
"fwe" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/paperbin{
pixel_x = -3;
pixel_y = 7
@@ -6809,7 +6820,7 @@
/turf/open/floor/tile/green/greentaupecorner,
/area/daedalusprison/inside/garden)
"fET" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/ammo_casing,
/turf/open/floor/prison,
/area/daedalusprison/inside/westernbooth)
@@ -6977,7 +6988,6 @@
dir = 8
},
/obj/machinery/atmospherics/pipe/simple/green/hidden,
-/obj/structure/cable,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
"fMS" = (
@@ -6999,7 +7009,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/engineering)
"fNC" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/health_analyzer,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -7238,7 +7248,7 @@
},
/area/daedalusprison/inside/security/secbreakroom)
"fXz" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/darkred{
dir = 8
},
@@ -7393,7 +7403,7 @@
},
/area/daedalusprison/inside/habitationnorth)
"ghq" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/trash,
/turf/open/floor/prison/red,
/area/daedalusprison/inside/habitationsouth)
@@ -7471,7 +7481,7 @@
},
/area/daedalusprison/inside/pmcdropship)
"glg" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/medhud,
/turf/open/floor/prison/whitegreen{
dir = 1
@@ -7671,7 +7681,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/engineering)
"gsE" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/pillbottle,
/turf/open/floor/prison/whitegreen{
dir = 8
@@ -8086,7 +8096,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/habitationnorth)
"gLZ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/donut_box,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/seccheckpoint)
@@ -8365,7 +8375,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/colonydorms)
"gZR" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/paper{
pixel_x = 5
},
@@ -8527,7 +8537,7 @@
},
/area/daedalusprison/inside/centralhalls)
"hgT" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/paperbin{
pixel_x = -3;
pixel_y = 7
@@ -8687,7 +8697,7 @@
/turf/closed/mineral/smooth/darkfrostwall/cuttable,
/area/daedalusprison/caves/rock)
"hoW" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/donut_box,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -8892,7 +8902,7 @@
/turf/open/floor/plating,
/area/daedalusprison/inside/easternhalls)
"hzR" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/ten,
/turf/open/floor/tile/red/full,
/area/daedalusprison/inside/bunker/center)
@@ -9139,7 +9149,7 @@
/turf/open/floor/plating,
/area/daedalusprison/inside/substation)
"hIy" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense,
/obj/effect/landmark/weed_node,
/turf/open/floor/plating/ground/snow/layer2,
@@ -9241,7 +9251,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/centralhalls)
"hOg" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/heal_pack,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -9313,7 +9323,7 @@
/turf/open/floor/mainship/floor,
/area/daedalusprison/caves/research)
"hSN" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/medhud,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -9594,7 +9604,7 @@
},
/area/daedalusprison/inside/centralhalls)
"ifQ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/door_control/old/checkpoint{
id = "DPsec"
},
@@ -9804,7 +9814,7 @@
/turf/open/floor/prison/kitchen,
/area/daedalusprison/inside/prisonshower)
"inQ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/security{
network = list("PRISON")
},
@@ -10232,7 +10242,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/engineering)
"iFy" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/heal_pack,
/turf/open/floor/prison/whitegreen{
dir = 8
@@ -10742,7 +10752,7 @@
/turf/open/floor/plating,
/area/daedalusprison/inside/substation)
"iYJ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 4
},
@@ -10809,7 +10819,7 @@
/turf/open/shuttle/dropship/floor,
/area/daedalusprison/inside/pmcdropship)
"jch" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/bodybag/cryobag,
/obj/item/bodybag/cryobag,
/turf/open/floor/tile/dark,
@@ -10920,7 +10930,6 @@
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 6
},
-/obj/structure/cable,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
"jgk" = (
@@ -10967,7 +10976,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/janitorial)
"jiL" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/recharger,
/turf/open/floor/prison/whitegreen{
dir = 8
@@ -11186,7 +11195,7 @@
},
/area/daedalusprison/inside/gym)
"jrt" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/surgical_tray,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical/treatment)
@@ -11227,7 +11236,7 @@
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"jtC" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/obj/structure/window/reinforced/tinted{
dir = 8
@@ -11478,7 +11487,7 @@
/area/daedalusprison/inside/easternhalls)
"jCA" = (
/obj/effect/decal/cleanable/dirt,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/bodybag/cryobag,
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical)
@@ -11593,7 +11602,7 @@
/area/daedalusprison/inside/hydroponics)
"jGD" = (
/obj/effect/landmark/weed_node,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/darkred,
/area/daedalusprison/inside/westernbooth)
"jGF" = (
@@ -11735,6 +11744,7 @@
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 4
},
+/obj/structure/cable,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
"jMO" = (
@@ -11772,6 +11782,10 @@
dir = 4
},
/area/daedalusprison/inside/barracks)
+"jOU" = (
+/obj/machinery/miner/damaged,
+/turf/open/floor/plating/ground/snow/layer2,
+/area/daedalusprison/outside/north)
"jPp" = (
/obj/effect/landmark/weed_node,
/turf/open/floor/prison/red{
@@ -11840,7 +11854,7 @@
/turf/open/floor/plating,
/area/daedalusprison/inside/centralhalls)
"jRA" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/reagent_containers/glass/beaker/cryomix{
name = "cryo beaker"
},
@@ -11954,7 +11968,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/habitationnorth)
"jVK" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/defibrillator,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -12430,7 +12444,7 @@
dir = 4
},
/obj/structure/window/reinforced/tinted,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
"kqD" = (
@@ -12505,7 +12519,7 @@
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"ktc" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/phone,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -12714,7 +12728,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/colonydorms)
"kCB" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/tool/pen,
/turf/open/floor/wood,
/area/daedalusprison/inside/library)
@@ -12885,7 +12899,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/centralhalls)
"kKe" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/ashtray/bronze,
/turf/open/floor/tile/red/full,
/area/daedalusprison/inside/bunker/west)
@@ -13076,7 +13090,7 @@
/turf/open/floor/prison/red,
/area/daedalusprison/inside/habitationsouth)
"kRH" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/cell/lasgun/lasrifle,
/turf/open/floor/tile/red/full,
/area/daedalusprison/inside/bunker/west)
@@ -13529,6 +13543,10 @@
dir = 6
},
/area/daedalusprison/inside/westernbooth)
+"ljp" = (
+/obj/effect/acid_hole,
+/turf/closed/wall/prison,
+/area/daedalusprison/inside/medical)
"ljq" = (
/obj/machinery/atmospherics/pipe/simple/green/hidden,
/turf/open/floor/tile/dark2,
@@ -13682,7 +13700,7 @@
},
/area/daedalusprison/inside/habitationnorth)
"lqs" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/ten,
/turf/open/floor/prison/red{
dir = 4
@@ -13698,7 +13716,7 @@
/turf/open/floor/prison/red/corner,
/area/daedalusprison/inside/habitationnorth)
"lrq" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/obj/structure/window/reinforced/tinted{
dir = 4
@@ -13956,10 +13974,10 @@
},
/area/daedalusprison/inside/colonydorms)
"lzM" = (
-/obj/structure/fence,
/obj/structure/platform/rockcliff/icycliff/nondense{
- dir = 8
+ dir = 10
},
+/obj/structure/fence/broken,
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/south)
"lAz" = (
@@ -14089,7 +14107,7 @@
},
/area/daedalusprison/inside/habitationnorth)
"lHv" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/twelve,
/turf/open/floor/tile/dark/red2{
dir = 8
@@ -14313,7 +14331,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/easternhalls)
"lRF" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense,
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/south)
@@ -14410,6 +14428,10 @@
dir = 6
},
/area/daedalusprison/inside/mechanicshop)
+"lVv" = (
+/obj/machinery/miner/damaged,
+/turf/open/floor/plating/ground/snow/layer2,
+/area/daedalusprison/outside/southwest)
"lVE" = (
/obj/structure/bed/chair/comfy,
/obj/effect/landmark/weed_node,
@@ -14465,7 +14487,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/centralhalls)
"lXY" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/medbelt,
/obj/machinery/light{
dir = 4
@@ -14576,7 +14598,7 @@
},
/area/daedalusprison/inside/medical)
"mdG" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/firstaid/toxin,
/obj/item/storage/firstaid/toxin,
/turf/open/floor/prison/whitegreen{
@@ -14663,7 +14685,7 @@
/turf/open/floor/prison/whitepurple,
/area/daedalusprison/inside/medical/chemistry)
"mgR" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_rifle,
/obj/item/cell/lasgun/lasrifle,
/turf/open/floor/tile/dark2,
@@ -14733,7 +14755,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/habitationsouth)
"mkc" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/firstaid/o2,
/obj/item/storage/firstaid/o2,
/turf/open/floor/prison/whitegreen{
@@ -14840,7 +14862,7 @@
},
/area/daedalusprison/inside/hydroponics)
"mpC" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/red{
dir = 1
},
@@ -14942,7 +14964,7 @@
/turf/open/floor/plating/ground/concrete,
/area/daedalusprison/inside/garage)
"mua" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/firstaid/fire,
/obj/item/storage/firstaid/fire,
/turf/open/floor/prison/whitegreen{
@@ -15050,7 +15072,7 @@
/turf/open/floor/tile/dark/yellow2,
/area/daedalusprison/inside/engineering)
"mxI" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/obj/structure/window/reinforced/tinted{
dir = 4
@@ -15207,7 +15229,7 @@
/turf/open/floor/prison/red,
/area/daedalusprison/inside/security/office)
"mDU" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/obj/effect/landmark/weed_node,
/turf/open/floor/prison/red{
@@ -15320,7 +15342,7 @@
/turf/open/floor/plating,
/area/daedalusprison/caves/research)
"mIb" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/healthanalyzer,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -15378,7 +15400,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/prisongarden)
"mJF" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/obj/structure/window/reinforced/tinted{
dir = 8
@@ -15620,7 +15642,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/mechanicshop)
"mRv" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/bodybag,
/obj/item/bodybag,
/turf/open/floor/tile/dark,
@@ -15662,7 +15684,7 @@
/turf/open/floor/plating/ground/snow/layer0,
/area/daedalusprison/outside/northeast)
"mSE" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison,
/area/daedalusprison/inside/westernbooth)
"mSL" = (
@@ -15959,7 +15981,7 @@
},
/obj/structure/cable,
/obj/effect/landmark/weed_node,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/easternhalls)
"ngx" = (
@@ -16105,7 +16127,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/centralhalls)
"nmO" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 1
},
@@ -16533,7 +16555,7 @@
/turf/open/floor/plating/ground/snow/layer1,
/area/daedalusprison/outside/east)
"nDY" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/faxmachine,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
@@ -16733,7 +16755,7 @@
},
/area/daedalusprison/inside/gym)
"nNj" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 6
},
@@ -17158,7 +17180,7 @@
/obj/structure/window/reinforced/tinted{
dir = 4
},
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
@@ -17178,7 +17200,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/habitationsouth)
"oiU" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison/red{
@@ -17189,7 +17211,7 @@
/obj/structure/window/reinforced/tinted{
dir = 8
},
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
@@ -17216,7 +17238,7 @@
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/north)
"ojY" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/wood,
/area/daedalusprison/inside/library)
"okb" = (
@@ -17274,7 +17296,7 @@
},
/area/daedalusprison/inside/corporateoffice)
"omE" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/bloodpack,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -17391,16 +17413,16 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/barracks)
"otF" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/reagent_containers/spray/cleaner,
/turf/open/floor/tile/dark,
/area/daedalusprison/inside/medical)
"otI" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/red/full,
/area/daedalusprison/inside/bunker/east)
"otK" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/flashlight/lamp,
/obj/effect/landmark/weed_node,
/obj/effect/landmark/weed_node,
@@ -17463,7 +17485,7 @@
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
"ovt" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense,
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/southwest)
@@ -17585,7 +17607,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/centralhalls)
"ozV" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/flashlight/lamp,
/turf/open/floor/wood,
/area/daedalusprison/inside/library)
@@ -17916,7 +17938,7 @@
/turf/open/floor/prison/darkred,
/area/daedalusprison/inside/centralhalls)
"oMz" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/weaponry/gun,
/turf/open/floor/prison/darkred{
dir = 1
@@ -17968,7 +17990,7 @@
},
/area/daedalusprison/inside/medical)
"oOb" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/recharger,
/obj/structure/sign/safety/autodoc,
/obj/machinery/light,
@@ -18249,7 +18271,7 @@
},
/area/daedalusprison/inside/easternhalls)
"oXj" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/defibrillator,
/turf/open/floor/prison/whitegreen,
/area/daedalusprison/inside/medical)
@@ -18415,7 +18437,7 @@
/turf/open/floor,
/area/daedalusprison/inside/hydroponics)
"pdg" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/easternhalls)
"pdu" = (
@@ -18543,7 +18565,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/cargo)
"piI" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/south)
"piO" = (
@@ -18731,7 +18753,7 @@
},
/area/daedalusprison/inside/hydroponics)
"ppC" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_mlaser,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/bunker/west)
@@ -18920,7 +18942,7 @@
},
/area/daedalusprison/inside/habitationsouth)
"puZ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/box/gloves,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -19019,7 +19041,7 @@
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 4
},
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/pill_bottle/tramadol,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -19069,7 +19091,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/janitorial)
"pBx" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/beaker/regularweighted,
/turf/open/floor/prison/whitegreen{
dir = 1
@@ -19219,7 +19241,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/recreation)
"pID" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison,
/area/daedalusprison/inside/lobby)
"pIH" = (
@@ -19269,7 +19291,7 @@
/obj/structure/window/reinforced/tinted{
dir = 8
},
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/paperbin,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
@@ -19593,7 +19615,7 @@
/turf/open/floor/prison/green,
/area/daedalusprison/inside/northmeetingroom)
"qcb" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/faxmachine,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
@@ -19618,7 +19640,7 @@
/turf/open/floor/tile/green/greentaupecorner,
/area/daedalusprison/inside/garden)
"qdf" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/faxmachine,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -19698,12 +19720,12 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/caves/nukestorage)
"qft" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/surgical_tray,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"qfz" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/seccheckpoint)
"qfK" = (
@@ -20045,12 +20067,12 @@
/area/daedalusprison/inside/southclass)
"qsH" = (
/obj/machinery/atmospherics/pipe/simple/green/hidden,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/trash/burger,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"qta" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/paperbin{
pixel_x = -3;
pixel_y = 7
@@ -20118,7 +20140,7 @@
/turf/open/floor/prison/bright_clean/two,
/area/daedalusprison/inside/sportstorage)
"qvj" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense,
/turf/open/floor/plating/ground/snow/layer0,
/area/daedalusprison/outside/south)
@@ -20257,7 +20279,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/centralhalls)
"qDm" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/tool/hand_labeler,
/obj/machinery/door/poddoor/shutters{
id = "DPsec"
@@ -20417,7 +20439,7 @@
},
/area/daedalusprison/inside/hydroponics)
"qLA" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/box/gloves,
/turf/open/floor/prison/whitegreen{
dir = 1
@@ -20434,7 +20456,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/security/office)
"qLN" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/security{
network = list("PRISON")
},
@@ -20514,7 +20536,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/security/secbreakroom)
"qPQ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison/red{
dir = 8
@@ -20589,7 +20611,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/habitationsouth)
"qUH" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark,
/area/daedalusprison/inside/medical)
"qVp" = (
@@ -20623,7 +20645,7 @@
/turf/open/floor/prison/kitchen,
/area/daedalusprison/inside/prisonshower)
"qWH" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/effect/landmark/weed_node,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 9
@@ -21077,7 +21099,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/westcomputerlab)
"roj" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/bloodpack,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical/treatment)
@@ -21215,7 +21237,7 @@
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical/treatment)
"rvc" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/detective_scanner,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/seccheckpoint)
@@ -21342,7 +21364,7 @@
/turf/open/floor/tile/brown/full,
/area/daedalusprison/inside/engineering)
"rAg" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/medbottle,
/turf/open/floor/prison/whitegreen{
dir = 8
@@ -21793,7 +21815,7 @@
},
/area/daedalusprison/inside/westernbooth)
"rQl" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/landmark/weed_node,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/bunker/east)
@@ -21855,6 +21877,8 @@
/area/daedalusprison/inside/habitationnorth)
"rSI" = (
/obj/machinery/photocopier,
+/obj/machinery/power/apc,
+/obj/structure/cable,
/turf/open/floor/prison/darkred{
dir = 1
},
@@ -22399,7 +22423,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/habitationnorth)
"srk" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/bodybag/cryobag,
/obj/item/bodybag/cryobag,
/turf/open/floor/prison/whitegreen,
@@ -22449,7 +22473,7 @@
},
/area/daedalusprison/inside/medical)
"stv" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/machinery/random_broken_computer/small,
/turf/open/floor/prison/green,
/area/daedalusprison/inside/westcomputerlab)
@@ -22976,7 +23000,7 @@
/turf/open/floor/plating/ground/snow/layer0,
/area/daedalusprison/inside/landingzoneone)
"sJI" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/reagent_containers/spray/cleaner,
/obj/item/reagent_containers/spray/cleaner,
/turf/open/floor/prison/whitegreen{
@@ -23206,7 +23230,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/basketball)
"sTb" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/bloodpack,
/obj/machinery/light{
dir = 1
@@ -23475,7 +23499,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/cargo)
"tcJ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/security{
network = list("PRISON")
},
@@ -23789,7 +23813,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/mining)
"tsG" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/effect/landmark/weed_node,
/obj/structure/platform/rockcliff/icycliff/nondense,
/turf/open/floor/plating/ground/snow/layer2,
@@ -23800,7 +23824,7 @@
/area/daedalusprison/outside/northeast)
"ttq" = (
/obj/item/clothing/mask/bandanna/black,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/light,
/turf/open/floor/prison,
/area/daedalusprison/inside/execution)
@@ -24479,14 +24503,14 @@
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/colonydorms)
"tWQ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/syringe,
/turf/open/floor/prison/whitegreen{
dir = 8
},
/area/daedalusprison/inside/medical)
"tWS" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/toolbox/emergency,
/turf/open/floor/plating,
/area/daedalusprison/inside/substation)
@@ -24532,7 +24556,7 @@
/turf/open/floor/tile/dark/yellow2/corner,
/area/daedalusprison/inside/engineering)
"tZK" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/fancy/cigar,
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/bunker/west)
@@ -24884,7 +24908,7 @@
/turf/open/floor/tile/dark/yellow2/corner,
/area/daedalusprison/inside/engineering)
"uoi" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/ten,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
@@ -25076,7 +25100,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/security/secbreakroom)
"uwC" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/book/manual/chef_recipes,
/turf/open/floor/wood,
/area/daedalusprison/inside/library)
@@ -25122,7 +25146,7 @@
},
/area/daedalusprison/inside/engineering)
"uye" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/surgical_tray,
/obj/machinery/light,
/turf/open/floor/prison/whitegreen{
@@ -25148,7 +25172,7 @@
/obj/structure/window/reinforced/tinted{
dir = 8
},
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
"uyE" = (
@@ -25167,7 +25191,7 @@
},
/area/daedalusprison/inside/medical)
"uzt" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/bloodpack,
/obj/machinery/light{
dir = 8
@@ -25277,7 +25301,7 @@
},
/area/daedalusprison/inside/hydroponics)
"uDP" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison,
/area/daedalusprison/inside/lobby)
@@ -25378,7 +25402,7 @@
dir = 4
},
/obj/structure/cable,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/easternhalls)
"uHQ" = (
@@ -25541,7 +25565,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/mining)
"uOS" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/medhud,
/turf/open/floor/prison/whitegreen{
dir = 8
@@ -25749,7 +25773,7 @@
/turf/closed/wall/prison,
/area/daedalusprison/inside/laundromat)
"uYz" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/security/warden)
"uYA" = (
@@ -25775,7 +25799,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/library)
"uZt" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison/darkred/full,
/area/daedalusprison/inside/security/medsec)
@@ -26165,7 +26189,7 @@
},
/area/daedalusprison/inside/laundromat)
"vpx" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/tool/pen,
/obj/item/paper{
pixel_x = 5
@@ -26409,7 +26433,7 @@
/turf/open/floor/wood,
/area/daedalusprison/inside/colonydorms)
"vDj" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/obj/machinery/light{
dir = 1
@@ -26599,7 +26623,7 @@
},
/area/daedalusprison/inside/colonydorms)
"vKM" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
"vKN" = (
@@ -26677,7 +26701,7 @@
/turf/open/floor/plating/ground/snow/layer0,
/area/daedalusprison/outside/south)
"vNf" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/effect/landmark/weed_node,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 1
@@ -26685,7 +26709,7 @@
/turf/open/floor/plating/ground/snow/layer2,
/area/daedalusprison/outside/south)
"vNi" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/paperbin,
/obj/item/tool/pen/blue,
/turf/open/floor/prison/sterilewhite,
@@ -26769,7 +26793,7 @@
dir = 8
},
/obj/structure/window/reinforced/tinted,
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/misc/paperbin,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
@@ -26786,7 +26810,7 @@
/turf/open/floor/tile/dark/green2,
/area/daedalusprison/inside/colonydorms)
"vRA" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison/darkred,
/area/daedalusprison/inside/westernbooth)
@@ -27293,7 +27317,7 @@
},
/area/daedalusprison/inside/hydroponics)
"wlV" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/paper,
/turf/open/floor/wood,
/area/daedalusprison/inside/library)
@@ -27327,7 +27351,7 @@
/turf/open/shuttle/dropship/floor,
/area/daedalusprison/inside/pmcdropship)
"wmz" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/box/syringes,
/obj/item/storage/box/syringes,
/turf/open/floor/prison/whitegreen,
@@ -27455,7 +27479,7 @@
/obj/structure/window/reinforced/tinted{
dir = 4
},
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/tile/dark/purple2/corner,
/area/daedalusprison/inside/secoffices)
"wqd" = (
@@ -27686,7 +27710,7 @@
},
/area/daedalusprison/inside/engineering)
"wzL" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/ten,
/obj/effect/landmark/weed_node,
/turf/open/floor/prison/darkred{
@@ -27712,7 +27736,7 @@
/turf/open/floor/prison/plate,
/area/daedalusprison/inside/easternhalls)
"wAs" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/reagent_containers/spray/cleaner,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -27909,7 +27933,7 @@
},
/area/daedalusprison/inside/mechanicshop)
"wJE" = (
-/obj/structure/fence,
+/obj/structure/fence/broken,
/obj/structure/platform/rockcliff/icycliff/nondense{
dir = 1
},
@@ -27988,7 +28012,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/easternhalls)
"wNT" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/storage/firstaid/rad,
/obj/item/storage/firstaid/rad,
/turf/open/floor/prison/whitegreen{
@@ -28126,7 +28150,7 @@
/turf/open/shuttle/dropship/floor,
/area/daedalusprison/inside/pmcdropship)
"wSQ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/book/manual/barman_recipes,
/obj/effect/landmark/weed_node,
/turf/open/floor/wood,
@@ -28197,7 +28221,7 @@
},
/area/daedalusprison/inside/medical)
"wVL" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/computer/secure_data,
/turf/open/floor/prison,
/area/daedalusprison/inside/centralbooth)
@@ -28304,7 +28328,7 @@
/turf/open/floor/plating/plating_catwalk/prison,
/area/daedalusprison/inside/cafeteria)
"wZA" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/fourteen,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical)
@@ -28327,7 +28351,7 @@
},
/area/daedalusprison/inside/habitationnorth)
"xaQ" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/effect/spawner/random/medical/heal_pack,
/turf/open/floor/prison/sterilewhite,
/area/daedalusprison/inside/medical/treatment)
@@ -28406,7 +28430,7 @@
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
"xdi" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/revolver/judge,
/turf/open/floor/prison,
/area/daedalusprison/inside/centralbooth)
@@ -28735,7 +28759,7 @@
/turf/open/floor,
/area/daedalusprison/inside/colonydorms)
"xqC" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/button,
/obj/machinery/light,
/obj/effect/landmark/weed_node,
@@ -28803,7 +28827,7 @@
/turf/open/floor/prison/darkyellow,
/area/daedalusprison/inside/sportstorage)
"xtH" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/turf/open/floor/prison/whitegreen{
dir = 4
},
@@ -28943,7 +28967,7 @@
/turf/open/floor/tile/dark2,
/area/daedalusprison/inside/colonyauxstorage)
"xzI" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/defibrillator,
/obj/item/defibrillator,
/obj/item/defibrillator,
@@ -29096,7 +29120,7 @@
/turf/open/floor/tile/dark/red2,
/area/daedalusprison/inside/seccheckpoint)
"xGm" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/machinery/button,
/obj/machinery/light{
dir = 1
@@ -29261,7 +29285,7 @@
/turf/open/floor/prison/darkred,
/area/daedalusprison/inside/westernbooth)
"xLX" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/structure/prop/computer/broken/nineteen,
/turf/open/floor/prison/whitegreen/full,
/area/daedalusprison/inside/medical)
@@ -29625,7 +29649,7 @@
/turf/open/floor/prison,
/area/daedalusprison/inside/lobby)
"ydR" = (
-/obj/structure/table/reinforced,
+/obj/structure/table/reinforced/weak,
/obj/item/weapon/gun/energy/lasgun/lasrifle/standard_marine_rifle,
/obj/item/cell/lasgun/lasrifle,
/turf/open/floor/prison/red{
@@ -37350,7 +37374,7 @@ rUP
rUP
rUP
rUP
-rUP
+aGY
rUP
rUP
rUP
@@ -38686,7 +38710,7 @@ dLg
dVz
dVz
eZo
-nmY
+dVz
dVz
dLg
liI
@@ -39239,7 +39263,7 @@ eFJ
eFJ
eFJ
eFJ
-eFJ
+rit
eFJ
eFJ
eFJ
@@ -46946,7 +46970,7 @@ aok
fir
dmP
dmP
-dmP
+lVv
fir
dmP
dmP
@@ -51152,9 +51176,9 @@ fir
dmP
fir
piI
-lzM
-lzM
-lzM
+dCb
+dCb
+dCb
lzM
prx
prx
@@ -51937,7 +51961,7 @@ hAM
kKm
glb
xIF
-jXr
+ljp
dXL
dXL
dXL
@@ -55784,7 +55808,7 @@ jbA
jbA
uRM
uRM
-uRM
+efq
uRM
uRM
uRM
@@ -58365,8 +58389,8 @@ prx
prx
prx
qWH
-lzM
-lzM
+dCb
+dCb
piI
kaT
bUI
@@ -61479,7 +61503,7 @@ jXr
jXr
dXL
spn
-dXL
+jOU
dXL
spn
cAe
@@ -62487,7 +62511,7 @@ mkY
mkY
nMf
mkY
-aoX
+mkY
nMf
mkY
kOm
@@ -67844,7 +67868,7 @@ lMW
hUa
rSI
jMm
-vNs
+dQI
aHc
twa
sUX
diff --git a/_maps/map_files/Ice_Colony_v2/Ice_Colony_v2.dmm b/_maps/map_files/Ice_Colony_v2/Ice_Colony_v2.dmm
index 99a4b16f56087..6c1ea0929527b 100644
--- a/_maps/map_files/Ice_Colony_v2/Ice_Colony_v2.dmm
+++ b/_maps/map_files/Ice_Colony_v2/Ice_Colony_v2.dmm
@@ -21354,10 +21354,6 @@
dir = 8
},
/area/ice_colony/surface/storage_unit/power)
-"hqp" = (
-/obj/machinery/miner/damaged,
-/turf/open/floor/plating/ground/snow/layer1,
-/area/ice_colony/exterior/surface/valley/southeast)
"hqr" = (
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 4
@@ -23397,10 +23393,6 @@
dir = 8
},
/area/ice_colony/underground/requesition)
-"kqC" = (
-/obj/machinery/miner/damaged,
-/turf/open/floor/plating/ground/snow/layer0,
-/area/ice_colony/exterior/surface/valley/south)
"kqN" = (
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 4
@@ -24972,10 +24964,6 @@
/obj/item/lightstick/anchored,
/turf/open/floor/plating/ground/snow/layer0,
/area/ice_colony/exterior/surface/landing_pad2)
-"mxl" = (
-/obj/machinery/miner/damaged,
-/turf/open/floor/plating/ground/snow/layer1,
-/area/ice_colony/exterior/surface/clearing/south)
"mxE" = (
/turf/open/floor/plating/ground/ice,
/area/ice_colony/exterior/surface/landing_pad_external)
@@ -31794,10 +31782,6 @@
/obj/effect/ai_node,
/turf/open/floor/tile/dark,
/area/ice_colony/underground/westroadtunnel)
-"vGy" = (
-/obj/machinery/miner/damaged,
-/turf/open/floor/plating/ground/snow/layer2,
-/area/ice_colony/exterior/surface/valley/north)
"vGO" = (
/obj/machinery/atmospherics/pipe/simple/green/hidden{
dir = 5
@@ -49100,7 +49084,7 @@ xJX
hKV
vCr
eAl
-kqC
+eAl
bVW
eAl
eAl
@@ -54808,7 +54792,7 @@ aKC
aKC
vPZ
aXE
-mxl
+aKC
aKC
aKC
exj
@@ -59626,7 +59610,7 @@ nsF
nsF
nsF
wkf
-mTO
+wkf
wkf
wkf
hlm
@@ -60427,7 +60411,7 @@ uSg
uSg
hKV
fIm
-vGy
+fIm
fIm
fIm
fIm
@@ -67441,7 +67425,7 @@ wkf
xCs
wkf
mqj
-jPw
+mqj
wkf
wkf
xCs
@@ -70255,7 +70239,7 @@ fIR
uXx
xnK
uUO
-hqp
+xnK
xnK
sZv
sZv
@@ -70653,7 +70637,7 @@ xnK
xnK
pdQ
sZv
-hqp
+xnK
oYT
bqg
xnK
@@ -71899,7 +71883,7 @@ aiX
aiX
wkf
wkf
-wkf
+mTO
cZy
nsF
wkf
diff --git a/_maps/map_files/Orion_Military_Outpost/orionoutpost.dmm b/_maps/map_files/Orion_Military_Outpost/orionoutpost.dmm
index 9dd2d5d2ff8bd..a86ee3c971ba6 100644
--- a/_maps/map_files/Orion_Military_Outpost/orionoutpost.dmm
+++ b/_maps/map_files/Orion_Military_Outpost/orionoutpost.dmm
@@ -2640,7 +2640,7 @@
/area/orion_outpost/ground/outpostsw)
"nJ" = (
/obj/item/ammo_magazine/tank/ltb_cannon,
-/obj/item/ammo_magazine/tank/ltaaap_minigun,
+/obj/item/ammo_magazine/tank/ltaap_minigun,
/turf/open/floor/mainship/black{
dir = 8
},
@@ -6262,7 +6262,7 @@
/area/orion_outpost/ground/outpostcent)
"Gr" = (
/obj/item/ammo_magazine/tank/ltb_cannon,
-/obj/item/ammo_magazine/tank/ltaaap_minigun,
+/obj/item/ammo_magazine/tank/ltaap_minigun,
/turf/open/floor/mainship/black/full,
/area/orion_outpost/surface/building/ammodepot)
"Gs" = (
diff --git a/_maps/map_files/Sulaco/TGS_Sulaco.dmm b/_maps/map_files/Sulaco/TGS_Sulaco.dmm
index e844dde465d70..e361ad7f85da9 100644
--- a/_maps/map_files/Sulaco/TGS_Sulaco.dmm
+++ b/_maps/map_files/Sulaco/TGS_Sulaco.dmm
@@ -7767,9 +7767,6 @@
/turf/open/floor/mainship/stripesquare,
/area/sulaco/hangar/cas)
"aQI" = (
-/obj/machinery/light/mainship{
- dir = 1
- },
/obj/machinery/atmospherics/pipe/simple/yellow/hidden{
dir = 4
},
@@ -11585,6 +11582,10 @@
},
/turf/open/floor/prison/plate,
/area/shuttle/distress/arrive_2)
+"ecj" = (
+/obj/machinery/vending/weapon,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"eck" = (
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/simple/cyan/hidden/layer1{
@@ -13473,6 +13474,11 @@
/obj/effect/soundplayer,
/turf/closed/wall/mainship/gray/outer,
/area/shuttle/distress/arrive_2)
+"gGd" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/vending/armor_supply,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"gGh" = (
/obj/structure/table/mainship/nometal,
/obj/machinery/air_alarm{
@@ -14547,6 +14553,10 @@
},
/turf/open/floor/plating/plating_catwalk/prison,
/area/sulaco/cargo)
+"hTO" = (
+/obj/machinery/vending/MarineMed,
+/turf/open/floor/prison,
+/area/sulaco/hallway/lower_main_hall)
"hUb" = (
/obj/structure/rack,
/obj/item/conveyor_switch_construct,
@@ -15116,6 +15126,13 @@
dir = 4
},
/area/sulaco/engineering)
+"iFZ" = (
+/obj/machinery/light/mainship{
+ dir = 1
+ },
+/obj/machinery/vending/uniform_supply,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"iGf" = (
/obj/structure/table/mainship/nometal,
/obj/effect/spawner/random/misc/paperbin{
@@ -16406,6 +16423,7 @@
/area/space)
"kcP" = (
/obj/effect/decal/cleanable/cobweb,
+/obj/machinery/vending/armor_supply,
/turf/open/floor/prison/bright_clean,
/area/sulaco/hangar)
"kcV" = (
@@ -16420,6 +16438,10 @@
dir = 4
},
/area/sulaco/marine/chapel)
+"kcY" = (
+/obj/machinery/vending/MarineMed,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"kee" = (
/obj/effect/turf_decal/warning_stripes/thin{
dir = 10
@@ -17528,6 +17550,11 @@
},
/turf/open/floor/prison,
/area/sulaco/hallway/central_hall2)
+"lDk" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/vending/weapon,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"lDs" = (
/obj/structure/table/mainship/nometal,
/obj/item/camera,
@@ -21387,6 +21414,13 @@
dir = 1
},
/area/sulaco/marine/chapel)
+"qGU" = (
+/obj/machinery/light/mainship{
+ dir = 1
+ },
+/obj/machinery/loadout_vendor,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"qHb" = (
/obj/structure/window/framed/mainship/gray/toughened/hull,
/obj/effect/soundplayer,
@@ -23688,15 +23722,15 @@
/area/sulaco/cargo)
"tvk" = (
/obj/effect/decal/cleanable/dirt,
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
/obj/machinery/holopad{
active_power_usage = 130;
desc = "It's a floor-mounted device for projecting holographic images. This one appears to have a larger lense.";
holo_range = 7;
name = "modfied holopad"
},
-/obj/structure/disposalpipe/segment{
- dir = 4
- },
/turf/open/floor/prison,
/area/sulaco/hallway/lower_main_hall)
"tvS" = (
@@ -25285,6 +25319,11 @@
/obj/effect/ai_node,
/turf/open/floor/plating/plating_catwalk/prison,
/area/sulaco/hallway/central_hall)
+"vyG" = (
+/obj/machinery/light/mainship,
+/obj/machinery/loadout_vendor,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"vyQ" = (
/turf/open/floor/mainship_hull/gray/dir{
dir = 10
@@ -25578,6 +25617,11 @@
dir = 1
},
/area/sulaco/medbay)
+"vRA" = (
+/obj/machinery/light/mainship,
+/obj/machinery/vending/uniform_supply,
+/turf/open/floor/prison/bright_clean,
+/area/sulaco/hangar)
"vRH" = (
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/simple/cyan/hidden/layer1,
@@ -57615,7 +57659,7 @@ aGy
pIP
xKR
pIP
-pIP
+hTO
fVo
xzB
ajQ
@@ -61454,7 +61498,7 @@ rFM
rFM
qBE
xca
-aPF
+kcY
aQI
aQr
fEL
@@ -61711,7 +61755,7 @@ gkO
mZj
vhM
xca
-aRR
+qGU
uVR
vVE
vNm
@@ -61968,7 +62012,7 @@ qBE
vIJ
sLp
xca
-aPF
+ecj
gHU
aPF
igo
@@ -62252,7 +62296,7 @@ kpD
aPZ
svV
gHU
-gQS
+vyG
bFa
bFa
bFa
@@ -62509,7 +62553,7 @@ aPZ
aPZ
svV
aPF
-gHU
+lDk
xHm
rId
ptp
@@ -63537,7 +63581,7 @@ aPZ
aPZ
svV
aPF
-gHU
+gGd
aPY
ryR
aYA
@@ -63794,7 +63838,7 @@ jYG
aPZ
svV
aPF
-gQS
+vRA
bFa
bFa
bFa
@@ -64281,7 +64325,7 @@ yjP
byI
scy
xca
-aRR
+iFZ
aPF
aPF
igo
diff --git a/_maps/map_files/slumbridge/slumbridge.dmm b/_maps/map_files/slumbridge/slumbridge.dmm
index f76e2c59413cc..affa414a5db52 100644
--- a/_maps/map_files/slumbridge/slumbridge.dmm
+++ b/_maps/map_files/slumbridge/slumbridge.dmm
@@ -9303,7 +9303,7 @@
/turf/open/floor/tile/yellow/full,
/area/slumbridge/inside/engi/central)
"hey" = (
-/obj/item/ammo_magazine/tank/ltaaap_minigun,
+/obj/item/ammo_magazine/tank/ltaap_minigun,
/obj/structure/cable,
/turf/open/floor/plating,
/area/slumbridge/inside/sombase/east)
diff --git a/_maps/shuttles/distress.dmm b/_maps/shuttles/distress.dmm
index 5352cc09e2569..b1794c00b5f28 100644
--- a/_maps/shuttles/distress.dmm
+++ b/_maps/shuttles/distress.dmm
@@ -177,11 +177,30 @@
dir = 1
},
/area/shuttle/ert)
+"R" = (
+/obj/structure/barricade/plasteel{
+ dir = 4
+ },
+/obj/effect/mapping_helpers/barricade/closed,
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert)
"S" = (
/turf/closed/shuttle/ert/engines/right{
dir = 1
},
/area/shuttle/ert)
+"U" = (
+/obj/structure/barricade/plasteel,
+/obj/effect/mapping_helpers/barricade/closed,
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert)
+"W" = (
+/obj/structure/barricade/plasteel{
+ dir = 8
+ },
+/obj/effect/mapping_helpers/barricade/closed,
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert)
"X" = (
/turf/closed/shuttle/ert/engines/right/two{
dir = 1
@@ -212,11 +231,11 @@ O
a
e
j
-g
+W
s
s
s
-g
+W
u
y
i
@@ -235,7 +254,7 @@ q
v
g
D
-g
+U
I
"}
(4,1,1) = {"
@@ -265,18 +284,18 @@ q
w
g
E
-g
+U
I
"}
(6,1,1) = {"
a
e
n
-g
+R
t
t
t
-g
+R
u
A
o
diff --git a/_maps/shuttles/distress_pmc.dmm b/_maps/shuttles/distress_pmc.dmm
index afdd205c5c635..bb2959314ac06 100644
--- a/_maps/shuttles/distress_pmc.dmm
+++ b/_maps/shuttles/distress_pmc.dmm
@@ -183,6 +183,13 @@
"N" = (
/turf/closed/shuttle/dropship_white/engine_corner,
/area/shuttle/ert/pmc)
+"O" = (
+/obj/effect/mapping_helpers/barricade/closed,
+/obj/structure/barricade/plasteel{
+ dir = 4
+ },
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert/pmc)
"P" = (
/turf/closed/shuttle/ert/engines/right/two{
dir = 1
@@ -201,6 +208,13 @@
dir = 4
},
/area/shuttle/ert/pmc)
+"T" = (
+/obj/effect/mapping_helpers/barricade/closed,
+/obj/structure/barricade/plasteel{
+ dir = 8
+ },
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert/pmc)
"U" = (
/turf/closed/shuttle/ert/engines/right/three{
dir = 1
@@ -226,6 +240,11 @@
dir = 1
},
/area/shuttle/ert/pmc)
+"Z" = (
+/obj/effect/mapping_helpers/barricade/closed,
+/obj/structure/barricade/plasteel,
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert/pmc)
(1,1,1) = {"
a
@@ -246,11 +265,11 @@ V
a
e
j
-g
+T
t
t
t
-g
+T
H
L
F
@@ -269,7 +288,7 @@ q
v
g
E
-g
+Z
I
"}
(4,1,1) = {"
@@ -299,18 +318,18 @@ q
w
g
G
-g
+Z
I
"}
(6,1,1) = {"
a
e
n
-g
+O
u
u
u
-g
+O
H
M
J
diff --git a/_maps/shuttles/distress_ufo.dmm b/_maps/shuttles/distress_ufo.dmm
index d452516ae7c52..db5021569ad4f 100644
--- a/_maps/shuttles/distress_ufo.dmm
+++ b/_maps/shuttles/distress_ufo.dmm
@@ -103,6 +103,8 @@
/turf/open/floor/light,
/area/shuttle/ert)
"x" = (
+/obj/structure/barricade/plasteel,
+/obj/effect/mapping_helpers/barricade/closed,
/turf/open/floor/podhatch/floor,
/area/shuttle/ert)
"y" = (
diff --git a/_maps/shuttles/distress_upp.dmm b/_maps/shuttles/distress_upp.dmm
index 16337ebf08d74..6e98a999f1449 100644
--- a/_maps/shuttles/distress_upp.dmm
+++ b/_maps/shuttles/distress_upp.dmm
@@ -184,11 +184,23 @@
dir = 1
},
/area/shuttle/ert/upp)
+"P" = (
+/obj/effect/mapping_helpers/barricade/closed,
+/obj/structure/barricade/plasteel{
+ dir = 4
+ },
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert/upp)
"Q" = (
/turf/closed/shuttle/ert/engines/left/three{
dir = 1
},
/area/shuttle/ert/upp)
+"S" = (
+/obj/effect/mapping_helpers/barricade/closed,
+/obj/structure/barricade/plasteel,
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert/upp)
"V" = (
/turf/closed/shuttle/ert/engines/right/three{
dir = 1
@@ -197,6 +209,13 @@
"W" = (
/turf/closed/shuttle/dropship_dark/interior_wall,
/area/shuttle/ert/upp)
+"Y" = (
+/obj/effect/mapping_helpers/barricade/closed,
+/obj/structure/barricade/plasteel{
+ dir = 8
+ },
+/turf/open/shuttle/dropship/floor,
+/area/shuttle/ert/upp)
"Z" = (
/turf/closed/shuttle/ert/engines/right{
dir = 1
@@ -222,11 +241,11 @@ N
a
e
j
-g
+Y
t
t
t
-g
+Y
x
B
W
@@ -245,7 +264,7 @@ q
v
g
E
-g
+S
I
"}
(4,1,1) = {"
@@ -275,18 +294,18 @@ q
w
g
G
-g
+S
I
"}
(6,1,1) = {"
a
e
n
-g
+P
u
u
u
-g
+P
y
D
o
diff --git a/code/__DEFINES/action.dm b/code/__DEFINES/action.dm
index ab3c7d28559ac..adcb55668bb24 100644
--- a/code/__DEFINES/action.dm
+++ b/code/__DEFINES/action.dm
@@ -30,6 +30,8 @@
#define VREF_MUTABLE_EARTH_PILLAR "VREF_EARTH_PILLAR"
// extra reference for savage's cooldown
#define VREF_MUTABLE_SAVAGE_COOLDOWN "VREF_SAVAGE_COOLDOWN"
+// extra define for jab charges
+#define VREF_MUTABLE_JAB "VREF_JAB"
/// Actions that toggle on click/trigger
diff --git a/code/__DEFINES/actions.dm b/code/__DEFINES/actions.dm
index 5f6911b78eac6..cb4202abef14b 100644
--- a/code/__DEFINES/actions.dm
+++ b/code/__DEFINES/actions.dm
@@ -6,7 +6,6 @@
#define ABILITY_USE_CRESTED (1 << 5) // ignore being in crest defense
#define ABILITY_USE_NOTTURF (1 << 6) // ignore not being on a turf (like in a vent)
#define ABILITY_USE_BUSY (1 << 7) // ignore being in a do_after or similar
-#define ABILITY_USE_AGILITY (1 << 8) // ignore agility mode
#define ABILITY_TARGET_SELF (1 << 9) // allow self-targetting
#define ABILITY_IGNORE_PLASMA (1 << 10) // ignore plasma cost
#define ABILITY_USE_CLOSEDTURF (1 << 11) // can be used while being on a closed turf.
@@ -15,7 +14,6 @@
#define ABILITY_IGNORE_SELECTED_ABILITY (1 << 14) // bypass the check of the selected ability
#define ABILITY_DO_AFTER_ATTACK (1 << 15) //Let the xeno attack the object and perform the ability.
#define ABILITY_USE_BURROWED (1 << 16) // ignore being burrowed
-#define ABILITY_USE_ROOTED (1 << 17) // ignore being currently rooted
#define ABILITY_TURF_TARGET (1 << 0) // ability targets turfs
#define ABILITY_MOB_TARGET (1 << 1) // ability targets mobs
diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index a931dd438c05c..200341c526c27 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -254,6 +254,8 @@ Important note: colors can end up significantly different from the basic html pi
#define LIGHT_COLOR_FLAME "#F88818"
/// Rich and bright blue. rgb(0, 183, 255)
#define LIGHT_COLOR_BLUE_FLAME "#00b8ff"
+///Strong red orange
+#define LIGHT_COLOR_RED_ORANGE "#ff2802"
//Ammo and grenade colors
#define COLOR_AMMO_AIRBURST "#2272eb"
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index dde078e355dd7..f6f7385a38d9d 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -71,3 +71,6 @@
#define EXPLODE_HEAVY 2
#define EXPLODE_LIGHT 3
#define EXPLODE_WEAK 4
+
+///Xenomorph accuracy
+#define XENO_DEFAULT_ACCURACY 70
diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm
index 80ba981c66d1c..4831c7bf47ea1 100644
--- a/code/__DEFINES/cooldowns.dm
+++ b/code/__DEFINES/cooldowns.dm
@@ -51,6 +51,7 @@
#define COOLDOWN_DROPPOD_TARGETTING "cooldown_droppod_targetting"
#define COOLDOWN_TRY_TTS "cooldown_try_tts"
#define COOLDOWN_EVASION_ACTIVATION "cooldown_evasion_activation"
+#define COOLDOWN_TANK_SWIVEL "tank_turret_swivel"
//Mecha cooldowns
#define COOLDOWN_MECHA "mecha"
@@ -62,6 +63,7 @@
#define COOLDOWN_MECHA_SKYFALL "mecha_skyfall"
#define COOLDOWN_MECHA_MISSILE_STRIKE "mecha_missile_strike"
+#define COOLDOWN_VEHICLE_CRUSHSOUND "cooldown_vehicle_crushsound"
//// COOLDOWN SYSTEMS
/*
* We have 2 cooldown systems: timer cooldowns (divided between stoppable and regular) and world.time cooldowns.
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 3f252a906c3b2..a52c122184e56 100755
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -291,6 +291,8 @@
#define COMSIG_MOVABLE_BUCKLE "buckle" //from base of atom/movable/buckle_mob(): (mob, force)
#define COMPONENT_MOVABLE_BUCKLE_STOPPED (1<<0)
#define COMSIG_MOVABLE_UNBUCKLE "unbuckle" //from base of atom/movable/unbuckle_mob(): (mob, force)
+///from /obj/vehicle/sealed/proc/driver_move
+#define COMSIG_VEHICLE_MOVE "vehicle_move"
///from /obj/vehicle/proc/driver_move, caught by the riding component to check and execute the driver trying to drive the vehicle
#define COMSIG_RIDDEN_DRIVER_MOVE "driver_move"
#define COMPONENT_DRIVER_BLOCK_MOVE (1<<0)
@@ -440,6 +442,8 @@
#define COMSIG_MECH_FIRE "mech_fire"
#define COMSIG_MECH_STOP_FIRE "mech_stop_fire"
+#define COMSIG_ARMORED_FIRE "armored_fire"
+#define COMSIG_ARMORED_STOP_FIRE "armored_stop_fire"
// /obj/item/clothing signals
#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): ()
@@ -742,6 +746,7 @@
#define COMSIG_KB_TOGGLE_MINIMAP "toggle_minimap"
#define COMSIG_KB_TOGGLE_EXTERNAL_MINIMAP "toggle_external_minimap"
#define COMSIG_KB_SELFHARM "keybind_selfharm"
+#define COMSIG_KB_INTERACTIVE_EMOTE "keybinding_interactive_emote"
// mecha keybinds
#define COMSIG_MECHABILITY_TOGGLE_INTERNALS "mechability_toggle_internals"
@@ -796,7 +801,6 @@
#define COMSIG_XENOABILITY_TOGGLE_BOMB "xenoability_toggle_bomb"
#define COMSIG_XENOABILITY_TOGGLE_BOMB_RADIAL "xenoability_toggle_bomb_radial"
#define COMSIG_XENOABILITY_CREATE_BOMB "xenoability_create_bomb"
-#define COMSIG_XENOABILITY_ROOT "xenoability_root"
#define COMSIG_XENOABILITY_BOMBARD "xenoability_bombard"
#define COMSIG_XENOABILITY_THROW_HUGGER "xenoability_throw_hugger"
diff --git a/code/__DEFINES/equipment.dm b/code/__DEFINES/equipment.dm
index a7ccd2bfdf329..0b33f985d3f57 100644
--- a/code/__DEFINES/equipment.dm
+++ b/code/__DEFINES/equipment.dm
@@ -22,6 +22,8 @@
#define PASS_AIR (1<<9)
///Mobs can walk freely between turfs with walkover flagged objects
#define PASS_WALKOVER (1<<10)
+///when jumping, mobs can pass onto tanks
+#define PASS_TANK (1<<11)
#define PASSABLE (PASS_THROW|PASS_PROJECTILE|PASS_AIR)
#define HOVERING (PASS_LOW_STRUCTURE|PASS_MOB|PASS_DEFENSIVE_STRUCTURE|PASS_FIRE)
@@ -65,47 +67,47 @@
//clothing specific stuff uses flags_inventory.
//flags_item
/// when an item has this it produces no "X has been hit by Y with Z" message with the default handler
-#define NOBLUDGEON (1<<0)
+#define NOBLUDGEON (1<<0)
/// Deletes on drop instead of falling on the floor.
-#define DELONDROP (1<<1)
+#define DELONDROP (1<<1)
/// The item is twohanded.
-#define TWOHANDED (1<<2)
+#define TWOHANDED (1<<2)
/// The item is wielded with both hands.
-#define WIELDED (1<<3)
+#define WIELDED (1<<3)
///The item is abstract (grab, powerloader_clamp, etc)
-#define ITEM_ABSTRACT (1<<4)
+#define ITEM_ABSTRACT (1<<4)
///Dont need hands to use it
-#define DOES_NOT_NEED_HANDS (1<<5)
+#define DOES_NOT_NEED_HANDS (1<<5)
///Prevents synths from wearing items with this flag
-#define SYNTH_RESTRICTED (1<<6)
+#define SYNTH_RESTRICTED (1<<6)
///Reduce the range of jetpack
-#define IMPEDE_JETPACK (1<<7)
+#define IMPEDE_JETPACK (1<<7)
///Item triggers bump attack
-#define CAN_BUMP_ATTACK (1<<8)
+#define CAN_BUMP_ATTACK (1<<8)
///Item can be deployed into a machine
-#define IS_DEPLOYABLE (1<<9)
+#define IS_DEPLOYABLE (1<<9)
///Item deploys on initialize
#define DEPLOY_ON_INITIALIZE (1<<10)
///If this is on an item, said item is currently deployed
-#define IS_DEPLOYED (1<<11)
+#define IS_DEPLOYED (1<<11)
///Disables deployed item pickup
-#define DEPLOYED_NO_PICKUP (1<<12)
+#define DEPLOYED_NO_PICKUP (1<<12)
///Disables deployed item rotation abilities to rotate.
-#define DEPLOYED_NO_ROTATE (1<<13)
+#define DEPLOYED_NO_ROTATE (1<<13)
///Disables deployed item rotation if anchored.
-#define DEPLOYED_NO_ROTATE_ANCHORED (1<<14)
+#define DEPLOYED_NO_ROTATE_ANCHORED (1<<14)
///If this is on an item, the item can only be disassembled using a wrench once deployed.
-#define DEPLOYED_WRENCH_DISASSEMBLE (1<<15)
+#define DEPLOYED_WRENCH_DISASSEMBLE (1<<15)
///Disables firing deployable if it is not anchored.
-#define DEPLOYED_ANCHORED_FIRING_ONLY (1<<16)
+#define DEPLOYED_ANCHORED_FIRING_ONLY (1<<16)
///If the item is properly wielded. Used for guns
-#define FULLY_WIELDED (1<<17)
+#define FULLY_WIELDED (1<<17)
///If a holster has underlay sprites
#define HAS_UNDERLAY (1<<18)
///is this item equipped into an inventory slot or hand of a mob?
#define IN_INVENTORY (1<<19)
///This item is used for autobalance calculations or excluded, such as valhalla items
-#define AUTOBALANCE_CHECK (1<<20)
+#define AUTOBALANCE_CHECK (1<<20)
//flags_storage
///If a storage container can be restocked into a vendor
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 93180bf020c48..80fca41c8eb8d 100755
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -250,6 +250,8 @@ GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur")))
#define ismecha(A) (istype(A, /obj/vehicle/sealed/mecha))
+#define isarmoredvehicle(A) (istype(A, /obj/vehicle/sealed/armored))
+
#define isorgan(A) (istype(A, /datum/limb))
#define isidcard(A) (istype(A, /obj/item/card/id))
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 52a84fb8d53ef..5bb076768fdf2 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -129,7 +129,7 @@
///for platform corner structures
#define ABOVE_MOB_PLATFORM_LAYER 4.11
-#define TANK_BARREL_LAYER 4.2
+#define ABOVE_MOB_PROP_LAYER 4.2
#define TANK_TURRET_LAYER 4.27
diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm
index f2195ed8b59ff..98268209f26d4 100644
--- a/code/__DEFINES/mecha.dm
+++ b/code/__DEFINES/mecha.dm
@@ -97,6 +97,8 @@
///degree of cone in front of which mech is allowed to fire at
#define MECH_FIRE_CONE_ALLOWED 120
+///degree of cone in front of which armored vehicles are allowed to fire at
+#define ARMORED_FIRE_CONE_ALLOWED 110
/**
* greyscale mech shenanigans
*/
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 6dfa4dd52fc38..8bf306243f951 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -700,11 +700,6 @@ GLOBAL_LIST_INIT(xenoupgradetiers, list(XENO_UPGRADE_BASETYPE, XENO_UPGRADE_INVA
#define WRAITH_TELEPORT_DEBUFF_STAGGER_STACKS 2 SECONDS //Stagger and slow stacks applied to adjacent living hostiles before/after a teleport
#define WRAITH_TELEPORT_DEBUFF_SLOWDOWN_STACKS 3 //Stagger and slow stacks applied to adjacent living hostiles before/after a teleport
-//Warrior defines
-
-#define WARRIOR_COMBO_THRESHOLD 2 //After how many abilities should warrior get an empowered cast (2 meaning the 3rd one is empowered)
-#define WARRIOR_COMBO_FADEOUT_TIME 10 SECONDS //How much time does it take for a combo to completely disappear
-
//Larva defines
#define LARVA_VENT_CRAWL_TIME 1 SECONDS //Larva can crawl into vents fast
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 39db0a5f344c1..b3785191133ff 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -80,6 +80,7 @@
#define RADIAL_STACKS (1<<2)
#define AUTO_INTERACT_DEPLOYABLES (1<<3)
#define RADIAL_LASERGUNS (1<<4)
+#define DIRECTIONAL_ATTACKS (1<<5)
#define PARALLAX_INSANE -1 //for show offs
#define PARALLAX_HIGH 0 //default.
@@ -101,7 +102,7 @@
#define SCALING_METHOD_DISTORT "distort"
#define SCALING_METHOD_BLUR "blur"
-#define TOGGLES_GAMEPLAY_DEFAULT (RADIAL_MEDICAL|MIDDLESHIFTCLICKING|RADIAL_STACKS|AUTO_INTERACT_DEPLOYABLES|RADIAL_LASERGUNS)
+#define TOGGLES_GAMEPLAY_DEFAULT (RADIAL_MEDICAL|MIDDLESHIFTCLICKING|RADIAL_STACKS|AUTO_INTERACT_DEPLOYABLES|RADIAL_LASERGUNS|DIRECTIONAL_ATTACKS)
#define CHARACTER_CUSTOMIZATION 1
#define BACKGROUND_INFORMATION 2
diff --git a/code/__DEFINES/skills.dm b/code/__DEFINES/skills.dm
index 2eeb44cb7bc2e..734255fe42d58 100644
--- a/code/__DEFINES/skills.dm
+++ b/code/__DEFINES/skills.dm
@@ -161,7 +161,7 @@
#define SKILL_STAMINA_TRAINED 1
#define SKILL_STAMINA_SUPER 2
-#define STAMINA_SKILL_COOLDOWN_MOD 2
+#define STAMINA_SKILL_COOLDOWN_MOD 2 SECONDS
#define STAMINA_SKILL_REGEN_MOD 0.15
////////////////////////////////////////////////
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index fdfec5e8ca086..dc49d2c6f02c4 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
-#define TGS_DMAPI_VERSION "7.0.2"
+#define TGS_DMAPI_VERSION "7.1.0"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -50,6 +50,13 @@
#endif
+#ifndef TGS_FILE2TEXT_NATIVE
+#ifdef file2text
+#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You can fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses
+#endif
+#define TGS_FILE2TEXT_NATIVE file2text
+#endif
+
// EVENT CODES
/// Before a reboot mode change, extras parameters are the current and new reboot mode enums.
@@ -489,6 +496,16 @@
/// Returns a list of connected [/datum/tgs_chat_channel]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
/world/proc/TgsChatChannelInfo()
return
+
+/**
+ * Trigger an event in TGS. Requires TGS version >= 6.3.0. Returns [TRUE] if the event was triggered successfully, [FALSE] otherwise. This function may sleep!
+ *
+ * event_name - The name of the event to trigger
+ * parameters - Optional list of string parameters to pass as arguments to the event script. The first parameter passed to a script will always be the running game's directory followed by these parameters.
+ * wait_for_completion - If set, this function will not return until the event has run to completion.
+ */
+/world/proc/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
+ return
/*
The MIT License
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ea60e43df840a..5d9f105ab64c5 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -169,6 +169,8 @@
#define TRAIT_LIGHT_PAIN_RESIST "light_pain_resist"
///Pain reduction medium
#define TRAIT_MEDIUM_PAIN_RESIST "medium_pain_resist"
+///is currently riding an armored vehicle
+#define TRAIT_TANK_DESANT "tank_desant"
/// Prevents usage of manipulation appendages (picking, holding or using items, manipulating storage).
@@ -243,6 +245,8 @@
//this mech is melee core boosted
#define TRAIT_MELEE_CORE "melee_core"
+///stops tanks from being able to ram this mob
+#define TRAIT_STOPS_TANK_COLLISION "stops_tanks"
//added to escaped humans
#define TRAIT_HAS_ESCAPED "escaped_marine"
diff --git a/code/__DEFINES/vehicles.dm b/code/__DEFINES/vehicles.dm
index bca10c692842b..fafc791f7799c 100644
--- a/code/__DEFINES/vehicles.dm
+++ b/code/__DEFINES/vehicles.dm
@@ -45,3 +45,14 @@
// For fireman carries, the carrying human needs an arm
#define CARRIER_NEEDS_ARM (1<<4)
+
+//Armored vehicle defines
+#define ARMORED_HAS_UNDERLAY (1<<0)
+#define ARMORED_HAS_MAP_VARIANTS (1<<2)
+#define ARMORED_HAS_PRIMARY_WEAPON (1<<3)
+#define ARMORED_HAS_SECONDARY_WEAPON (1<<4)
+#define ARMORED_LIGHTS_ON (1<<5)
+#define ARMORED_HAS_HEADLIGHTS (1<<6)
+
+#define MODULE_PRIMARY (1<<0)
+#define MODULE_SECONDARY (1<<1)
diff --git a/code/__HELPERS/announce.dm b/code/__HELPERS/announce.dm
index 99e46b5d03a90..576609e8fa7a5 100644
--- a/code/__HELPERS/announce.dm
+++ b/code/__HELPERS/announce.dm
@@ -135,7 +135,7 @@
* * alert - optional, alert or notice?
* * receivers - a list of all players to send the message to
*/
-/proc/minor_announce(message, title = "Attention:", alert, list/receivers = GLOB.alive_human_list)
+/proc/minor_announce(message, title = "Attention:", alert, list/receivers = GLOB.alive_human_list, should_play_sound = TRUE)
if(!message)
return
@@ -148,7 +148,8 @@
message = message,
minor = TRUE
))
- SEND_SOUND(M, S)
+ if(should_play_sound)
+ SEND_SOUND(M, S)
#undef span_alert_header
#undef span_faction_alert_title
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index e9cf14de4080d..83496fdb3eab7 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -100,7 +100,6 @@
return WEST
return NORTH|WEST
-
//returns the north-zero clockwise angle in degrees, given a direction
/proc/dir2angle(D)
switch(D)
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index f8d2d288946a3..8d732965d350d 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -143,7 +143,8 @@ GLOBAL_LIST_INIT(bitfields, list(
"PASS_THROW" = PASS_THROW,
"PASS_PROJECTILE" = PASS_PROJECTILE,
"PASS_AIR" = PASS_AIR,
- "PASS_WALKOVER" = PASS_WALKOVER
+ "PASS_WALKOVER" = PASS_WALKOVER,
+ "PASS_TANK" = PASS_TANK,
),
"pass_flags" = list(
"PASS_LOW_STRUCTURE" = PASS_LOW_STRUCTURE,
@@ -156,7 +157,8 @@ GLOBAL_LIST_INIT(bitfields, list(
"PASS_THROW" = PASS_THROW,
"PASS_PROJECTILE" = PASS_PROJECTILE,
"PASS_AIR" = PASS_AIR,
- "PASS_WALKOVER" = PASS_WALKOVER
+ "PASS_WALKOVER" = PASS_WALKOVER,
+ "PASS_TANK" = PASS_TANK,
),
"status_flags" = list(
"CANSTUN" = CANSTUN,
@@ -170,7 +172,7 @@ GLOBAL_LIST_INIT(bitfields, list(
"TK_USER" = TK_USER,
"CANUNCONSCIOUS" = CANUNCONSCIOUS,
"CANCONFUSE" = CANCONFUSE,
- "INCORPOREAL" = INCORPOREAL
+ "INCORPOREAL" = INCORPOREAL,
),
"muted" = list(
"MUTE_IC" = MUTE_IC,
@@ -514,6 +516,7 @@ GLOBAL_LIST_INIT(bitfields, list(
"RADIAL_STACKS" = RADIAL_STACKS,
"AUTO_INTERACT_DEPLOYABLES" = AUTO_INTERACT_DEPLOYABLES,
"RADIAL_LASERGUNS" = RADIAL_LASERGUNS,
+ "DIRECTIONAL_ATTACKS" = DIRECTIONAL_ATTACKS,
),
"toggles_deadchat" = list(
"DISABLE_DEATHRATTLE" = DISABLE_DEATHRATTLE,
@@ -598,7 +601,6 @@ GLOBAL_LIST_INIT(bitfields, list(
"ABILITY_USE_CRESTED" = ABILITY_USE_CRESTED,
"ABILITY_USE_NOTTURF" = ABILITY_USE_NOTTURF,
"ABILITY_USE_BUSY" = ABILITY_USE_BUSY,
- "ABILITY_USE_AGILITY" = ABILITY_USE_AGILITY,
"ABILITY_TARGET_SELF" = ABILITY_TARGET_SELF,
"ABILITY_IGNORE_PLASMA" = ABILITY_IGNORE_PLASMA,
"ABILITY_USE_CLOSEDTURF" = ABILITY_USE_CLOSEDTURF,
@@ -607,7 +609,6 @@ GLOBAL_LIST_INIT(bitfields, list(
"ABILITY_IGNORE_SELECTED_ABILITY" = ABILITY_IGNORE_SELECTED_ABILITY,
"ABILITY_DO_AFTER_ATTACK" = ABILITY_DO_AFTER_ATTACK,
"ABILITY_USE_BURROWED" = ABILITY_USE_BURROWED,
- "ABILITY_USE_ROOTED" = ABILITY_USE_ROOTED
),
"pipe_flags" = list(
"PIPING_ALL_LAYER" = PIPING_ALL_LAYER,
diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm
index b4c341ddf5fb3..b22f15555f2d5 100644
--- a/code/_onclick/adjacent.dm
+++ b/code/_onclick/adjacent.dm
@@ -102,6 +102,8 @@
//Multitile special cases.
/obj/vehicle/Adjacent(atom/neighbor)
+ if(hitbox)
+ return hitbox.Adjacent(neighbor)
if(bound_width > 32 || bound_height > 32)
for(var/turf/myloc AS in locs)
if(myloc.Adjacent(neighbor, target = neighbor, mover = src))
@@ -115,6 +117,12 @@
return TRUE
return FALSE
+/obj/hitbox/Adjacent(atom/neighbor, atom/target, atom/movable/mover)
+ for(var/turf/T AS in locs)
+ if(T.Adjacent(neighbor, neighbor, mover))
+ return TRUE
+ return FALSE
+
/mob/living/silicon/decoy/Adjacent(atom/neighbor)
for(var/turf/myloc AS in locs)
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 003f84db3441f..2fd4ad49d4f31 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -156,7 +156,9 @@
else
if(A.Adjacent(src))
A.attack_hand(src)
- RangedAttack(A, params)
+
+ if(!A.Adjacent(src))
+ RangedAttack(A, params)
/atom/movable/proc/CanReach(atom/ultimate_target, obj/item/tool, view_only = FALSE)
@@ -355,6 +357,7 @@ if(selected_ability.target_flags & flagname && !istype(A, typepath)){\
return FALSE
if(COMSIG_MOB_CLICK_HANDLED)
return TRUE
+
return A.RightClick(src)
/mob/living/carbon/human/RightClickOn(atom/A)
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 2a46774ad3558..c34578f12cf24 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -5,8 +5,6 @@
layer = ABOVE_HUD_LAYER
/atom/movable/screen/human/equip/Click()
- if(istype(usr.loc, /obj/vehicle/multitile/root/cm_armored)) // stops inventory actions in a mech
- return TRUE
SEND_SIGNAL(usr, COMSIG_CLICK_QUICKEQUIP)
diff --git a/code/_onclick/hud/interactive_emotes.dm b/code/_onclick/hud/interactive_emotes.dm
index 8ac6ebcdacfcb..f52cfdcdaace5 100644
--- a/code/_onclick/hud/interactive_emotes.dm
+++ b/code/_onclick/hud/interactive_emotes.dm
@@ -1,32 +1,31 @@
///All in one function to begin interactions
/mob/proc/interaction_emote(mob/target)
- var/atom/movable/screen/interaction/interaction
- if(can_interact(target))
- switch(zone_selected)
- if(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
- if(isxeno(target) && isxeno(src)) //Benos don't high five each other, they slap tails!
- interaction = /atom/movable/screen/interaction/fist_bump
- else
- interaction = /atom/movable/screen/interaction
- if(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND)
- interaction = /atom/movable/screen/interaction/fist_bump
- if(BODY_ZONE_HEAD)
- interaction = /atom/movable/screen/interaction/headbutt
+ if(!target || target == src)
+ return
+
+ var/list/interactions_list = list("Headbutt" = /atom/movable/screen/interaction/headbutt) //Universal interactions
+ if(isxeno(src)) //Benos don't high five each other, they slap tails! A beno cannot initiate a high five, but can recieve one if prompted by a human
+ interactions_list["Tail Slap"] = /atom/movable/screen/interaction/fist_bump
+ else
+ interactions_list["High Five"] = /atom/movable/screen/interaction
+ interactions_list["Fist Bump"] = /atom/movable/screen/interaction/fist_bump
+
+ var/atom/movable/screen/interaction/interaction = interactions_list[tgui_input_list(src, "Select an interaction type", "Interactive Emotes", interactions_list)]
if(!interaction)
- return FALSE
+ return
if(LAZYLEN(target.queued_interactions))
for(var/atom/movable/screen/interaction/element AS in target.queued_interactions)
if(element.initiator == src)
balloon_alert(src, "Slow your roll!")
- return FALSE
+ return
interaction = new interaction()
interaction.owner = target
interaction.initiator = src
interaction.register_movement_signals()
- LAZYADD(queued_interactions, interaction)
+ LAZYADD(target.queued_interactions, interaction)
if(target.client && target.hud_used)
target.hud_used.update_interactive_emotes()
@@ -36,8 +35,6 @@
interaction.timer_id = addtimer(CALLBACK(interaction, TYPE_PROC_REF(/atom/movable/screen/interaction, end_interaction), FALSE), interaction.timeout, TIMER_STOPPABLE|TIMER_UNIQUE)
- return TRUE
-
//Mob interactions
/atom/movable/screen/interaction
name = "high five"
@@ -265,7 +262,7 @@
viewer.client.screen |= interaction
return TRUE
-//If anyone wants to add more interactions, here is an easy test item to use, just be sure to comment out any can_interact checks and to use the target mob's zone_selected
+//If anyone wants to add more interactions, here is an easy test item to use, just be sure to comment out any can_interact checks and to use the target tgui input list
/obj/item/interaction_tester
name = "interaction tester"
icon_state = "coin"
@@ -274,4 +271,4 @@
var/mob/target = tgui_input_list(user, "Select a target", "Select a target", GLOB.alive_living_list)
if(!target)
return
- target.interaction_emote(user, TRUE)
+ target.interaction_emote(user)
diff --git a/code/_onclick/hud/screen_objects/menu_text_objects.dm b/code/_onclick/hud/screen_objects/menu_text_objects.dm
index 70beaa29930fe..1b2db453df4ac 100644
--- a/code/_onclick/hud/screen_objects/menu_text_objects.dm
+++ b/code/_onclick/hud/screen_objects/menu_text_objects.dm
@@ -112,6 +112,15 @@
var/mob/new_player/player = hud.mymob
player.view_manifest()
+/atom/movable/screen/text/lobby/clickable/xenomanifest
+ maptext = "VIEW HIVE"
+ icon_state = "manifest"
+
+/atom/movable/screen/text/lobby/clickable/xenomanifest/Click()
+ . = ..()
+ var/mob/new_player/player = hud.mymob
+ player.view_xeno_manifest()
+
/atom/movable/screen/text/lobby/clickable/background
maptext = "BACKGROUND"
icon_state = "background"
@@ -138,7 +147,7 @@
/atom/movable/screen/text/lobby/clickable/polls/update_text()
INVOKE_ASYNC(src, PROC_REF(fetch_polls)) //this sleeps and it shouldn't because update_text uses a signal sometimes
-///Proc that fetches the polls, exists so we can async it in update_text
+///Proc that fetches the polls, exists so we can async it in update_text
/atom/movable/screen/text/lobby/clickable/polls/proc/fetch_polls()
var/mob/new_player/player = hud.mymob
var/hasnewpolls = player.check_playerpolls()
diff --git a/code/_onclick/hud/screen_objects/screen_objects.dm b/code/_onclick/hud/screen_objects/screen_objects.dm
index eb902db5f22b7..9749d77840203 100644
--- a/code/_onclick/hud/screen_objects/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects/screen_objects.dm
@@ -85,9 +85,6 @@
if(isobserver(usr) || usr.incapacitated(TRUE))
return TRUE
- if(istype(usr.loc, /obj/vehicle/multitile/root/cm_armored)) // stops inventory actions in a mech/tank
- return TRUE
-
//If there is an item in the slot you are clicking on, this will relay the click to the item within the slot
var/atom/item_in_slot = usr.get_item_by_slot(slot_id)
if(item_in_slot)
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index a5a2af09f74ae..2235cbefca380 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -90,7 +90,7 @@
span_warning("You hit [src] with [I]!"), visible_message_flags = COMBAT_MESSAGE)
log_combat(user, src, "attacked", I)
var/power = I.force + round(I.force * MELEE_SKILL_DAM_BUFF * user.skills.getRating(SKILL_MELEE_WEAPONS))
- take_damage(power, I.damtype, MELEE)
+ take_damage(power, I.damtype, MELEE, blame_mob = user)
return TRUE
diff --git a/code/datums/actions/ability_actions.dm b/code/datums/actions/ability_actions.dm
index acfd1bcaecaee..d7d11e942d1ec 100644
--- a/code/datums/actions/ability_actions.dm
+++ b/code/datums/actions/ability_actions.dm
@@ -167,15 +167,6 @@
/datum/action/ability/proc/on_xeno_upgrade()
return
-///Adds an outline around the ability button
-/datum/action/ability/proc/add_empowered_frame()
- button.add_overlay(visual_references[VREF_MUTABLE_EMPOWERED_FRAME])
-
-///Removes an outline around the ability button
-/datum/action/ability/proc/remove_empowered_frame()
- button.cut_overlay(visual_references[VREF_MUTABLE_EMPOWERED_FRAME])
-
-
/datum/action/ability/activable
action_type = ACTION_SELECT
diff --git a/code/datums/actions/xeno_action.dm b/code/datums/actions/xeno_action.dm
index ee166717d13a4..775a5249af517 100644
--- a/code/datums/actions/xeno_action.dm
+++ b/code/datums/actions/xeno_action.dm
@@ -37,16 +37,6 @@
X.balloon_alert(X, "Cannot while in crest defense")
return FALSE
- if(!(flags_to_check & ABILITY_USE_ROOTED) && HAS_TRAIT_FROM(X, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- if(!silent)
- X.balloon_alert(X, "Cannot while rooted")
- return FALSE
-
- if(!(flags_to_check & ABILITY_USE_AGILITY) && X.agility)
- if(!silent)
- X.balloon_alert(X, "Cannot in agility mode")
- return FALSE
-
if(!(flags_to_check & ABILITY_IGNORE_PLASMA) && X.plasma_stored < ability_cost)
if(!silent)
X.balloon_alert(X, "Need [ability_cost - X.plasma_stored] more plasma")
@@ -88,16 +78,6 @@
X.balloon_alert(X, "Cannot while in crest defense")
return FALSE
- if(!(flags_to_check & ABILITY_USE_ROOTED) && HAS_TRAIT_FROM(X, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- if(!silent)
- X.balloon_alert(X, "Cannot while rooted")
- return FALSE
-
- if(!(flags_to_check & ABILITY_USE_AGILITY) && X.agility)
- if(!silent)
- X.balloon_alert(X, "Cannot in agility mode")
- return FALSE
-
if(!(flags_to_check & ABILITY_IGNORE_PLASMA) && X.plasma_stored < ability_cost)
if(!silent)
X.balloon_alert(X, "Need [ability_cost - X.plasma_stored] more plasma")
diff --git a/code/datums/components/autofire.dm b/code/datums/components/autofire.dm
index 527f1838bf03d..b06b8f85fb301 100644
--- a/code/datums/components/autofire.dm
+++ b/code/datums/components/autofire.dm
@@ -32,8 +32,8 @@
RegisterSignal(parent, COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED, PROC_REF(modify_burst_shots_to_fire))
RegisterSignal(parent, COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED, PROC_REF(modify_burstfire_shot_delay))
RegisterSignal(parent, COMSIG_GUN_AUTO_BURST_SHOT_DELAY_MODIFIED, PROC_REF(modify_autoburstfire_shot_delay))
- RegisterSignals(parent, list(COMSIG_GUN_FIRE, COMSIG_XENO_FIRE, COMSIG_MECH_FIRE), PROC_REF(initiate_shot))
- RegisterSignals(parent, list(COMSIG_GUN_STOP_FIRE, COMSIG_XENO_STOP_FIRE, COMSIG_MECH_STOP_FIRE), PROC_REF(stop_firing))
+ RegisterSignals(parent, list(COMSIG_GUN_FIRE, COMSIG_XENO_FIRE, COMSIG_MECH_FIRE, COMSIG_ARMORED_FIRE), PROC_REF(initiate_shot))
+ RegisterSignals(parent, list(COMSIG_GUN_STOP_FIRE, COMSIG_XENO_STOP_FIRE, COMSIG_MECH_STOP_FIRE, COMSIG_ARMORED_STOP_FIRE), PROC_REF(stop_firing))
auto_fire_shot_delay = _auto_fire_shot_delay
burstfire_shot_delay = _burstfire_shot_delay
diff --git a/code/datums/components/jump.dm b/code/datums/components/jump.dm
index 2b3ba90941c44..2aa1676f14483 100644
--- a/code/datums/components/jump.dm
+++ b/code/datums/components/jump.dm
@@ -37,7 +37,7 @@
set_vars(_jump_duration, _jump_cooldown, _stamina_cost, _jump_height, _jump_sound, _jump_flags, _jumper_allow_pass_flags)
///Actually sets the jump vars
-/datum/component/jump/proc/set_vars(_jump_duration = 0.5 SECONDS, _jump_cooldown = 1 SECONDS, _stamina_cost = 8, _jump_height = 16, _jump_sound = null, _jump_flags = JUMP_SHADOW, _jumper_allow_pass_flags = PASS_LOW_STRUCTURE|PASS_FIRE)
+/datum/component/jump/proc/set_vars(_jump_duration = 0.5 SECONDS, _jump_cooldown = 1 SECONDS, _stamina_cost = 8, _jump_height = 16, _jump_sound = null, _jump_flags = JUMP_SHADOW, _jumper_allow_pass_flags = PASS_LOW_STRUCTURE|PASS_FIRE|PASS_TANK)
jump_duration = _jump_duration
jump_cooldown = _jump_cooldown
stamina_cost = _stamina_cost
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 79800bebb8aa0..dc94b4776ad6e 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -118,6 +118,39 @@ GLOBAL_DATUM_INIT(datacore, /datum/datacore, new)
return dat
+/// Gathers the information necessary to display the hive's leader/queen status to lobby.
+/datum/datacore/proc/get_xeno_manifest(monochrome)
+ var/even = 0
+
+ var/dat = {"
+
+
+ Caste | Name |
+ "}
+
+ var/datum/hive_status/normal/HN = GLOB.hive_datums[XENO_HIVE_NORMAL]
+ if(HN.living_xeno_ruler)
+ dat += "Hive Ruler |
"
+ dat += "[HN.living_xeno_ruler.xeno_caste.display_name] | [HN.living_xeno_ruler.name] |
"
+ even = !even
+
+ if(length(HN.xeno_leader_list) > 0)
+ dat += "Hive Leaders |
"
+ for(var/x in HN.xeno_leader_list)
+ var/mob/living/carbon/xenomorph/leader = x
+ dat += "[leader.xeno_caste.display_name] | [leader.name] |
"
+ even = !even
+
+ dat += "
"
+
+ return dat
/datum/datacore/proc/manifest()
medical = list()
diff --git a/code/datums/elements/directional_attack.dm b/code/datums/elements/directional_attack.dm
new file mode 100644
index 0000000000000..3e85efc616df3
--- /dev/null
+++ b/code/datums/elements/directional_attack.dm
@@ -0,0 +1,43 @@
+/*!
+ * This element allows the mob its attached to the ability to click an adjacent mob by clicking a distant atom
+ * that is in the general direction relative to the parent.
+ */
+/datum/element/directional_attack/Attach(datum/target)
+ . = ..()
+ if(!ismob(target))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(target, COMSIG_MOB_ATTACK_RANGED, PROC_REF(on_ranged_attack))
+
+/datum/element/directional_attack/Detach(datum/source, ...)
+ . = ..()
+ UnregisterSignal(source, COMSIG_MOB_ATTACK_RANGED)
+
+/**
+ * This proc handles clicks on tiles that aren't adjacent to the source mob
+ * In addition to clicking the distant tile, it checks the tile in the direction and clicks the mob in the tile if there is one
+ * Arguments:
+ * * source - The mob clicking
+ * * clicked_atom - The atom being clicked (should be a distant one)
+ * * click_params - Miscellaneous click parameters, passed from Click itself
+ */
+/datum/element/directional_attack/proc/on_ranged_attack(mob/source, atom/clicked_atom, click_params)
+ SIGNAL_HANDLER
+
+ if(!(source?.client?.prefs?.toggles_gameplay & DIRECTIONAL_ATTACKS))
+ return
+
+ if(QDELETED(clicked_atom))
+ return
+
+ var/turf/turf_to_check = get_step(source, angle_to_dir(Get_Angle(source, clicked_atom)))
+ if(!turf_to_check)
+ return
+
+ var/mob/target_mob = locate() in turf_to_check
+ if(!target_mob)
+ return
+
+ //This is here to undo the +1 the click on the distant turf adds so we can click the mob near us
+ source.next_click = world.time - 1
+ INVOKE_ASYNC(source, TYPE_PROC_REF(/mob, ClickOn), target_mob, turf_to_check, click_params)
diff --git a/code/datums/elements/shrapnel_removal.dm b/code/datums/elements/shrapnel_removal.dm
index 0a6a0d4e871e1..8bc0c3ea9b738 100644
--- a/code/datums/elements/shrapnel_removal.dm
+++ b/code/datums/elements/shrapnel_removal.dm
@@ -66,6 +66,10 @@
if(is_type_in_list(I, GLOB.known_implants))
continue
I.unembed_ourself(FALSE)
+ if(user.ckey)
+ var/datum/personal_statistics/personal_statistics = GLOB.personal_statistics_list[user.ckey]
+ personal_statistics.shrapnel_removed ++
+ personal_statistics.mission_shrapnel_removed ++
if(skill < SKILL_MEDICAL_PRACTICED)
user.visible_message(span_notice("[user] violently rips out [I] from [target]!"), span_notice("You violently rip out [I] from [target]!"))
targetlimb.take_damage_limb(30 * (SKILL_MEDICAL_PRACTICED - skill), 0, FALSE, FALSE)
diff --git a/code/datums/gamemodes/campaign/campaign_mission.dm b/code/datums/gamemodes/campaign/campaign_mission.dm
index 564bf23d151f6..8c33d6edeb927 100644
--- a/code/datums/gamemodes/campaign/campaign_mission.dm
+++ b/code/datums/gamemodes/campaign/campaign_mission.dm
@@ -68,11 +68,11 @@
)
///cash rewards for the mission type
var/list/cash_rewards = list(
- MISSION_OUTCOME_MAJOR_VICTORY = list(650, 450),
- MISSION_OUTCOME_MINOR_VICTORY = list(550, 450),
- MISSION_OUTCOME_DRAW = list(450, 450),
- MISSION_OUTCOME_MINOR_LOSS = list(450, 550),
- MISSION_OUTCOME_MAJOR_LOSS = list(450, 650),
+ MISSION_OUTCOME_MAJOR_VICTORY = list(700, 500),
+ MISSION_OUTCOME_MINOR_VICTORY = list(600, 500),
+ MISSION_OUTCOME_DRAW = list(500, 500),
+ MISSION_OUTCOME_MINOR_LOSS = list(500, 600),
+ MISSION_OUTCOME_MAJOR_LOSS = list(500, 700),
)
/// Timer used to calculate how long till mission ends
var/game_timer
diff --git a/code/datums/gamemodes/campaign/faction_stats.dm b/code/datums/gamemodes/campaign/faction_stats.dm
index 6d499e24a9965..0409c58833835 100644
--- a/code/datums/gamemodes/campaign/faction_stats.dm
+++ b/code/datums/gamemodes/campaign/faction_stats.dm
@@ -90,7 +90,7 @@ GLOBAL_LIST_INIT(campaign_mission_pool, list(
var/active_attrition_points = 0
///Multiplier on the passive attrition point gain for this faction
var/attrition_gain_multiplier = 1
- ///cumulative loss bonus which is applied to attrition gain mult
+ ///cumulative loss bonus which is applied to attrition gain mult and player credit mission reward
var/loss_bonus = 0
///Future missions this faction can currently choose from
var/list/datum/campaign_mission/available_missions = list()
@@ -234,6 +234,7 @@ GLOBAL_LIST_INIT(campaign_mission_pool, list(
/datum/faction_stats/proc/apply_cash(amount)
if(!amount)
return
+ amount *= 1 + loss_bonus
accumulated_mission_reward += amount
for(var/i in individual_stat_list)
var/datum/individual_stats/player_stats = individual_stat_list[i]
@@ -242,7 +243,7 @@ GLOBAL_LIST_INIT(campaign_mission_pool, list(
///Returns all faction members back to base after the mission is completed
/datum/faction_stats/proc/return_to_base(datum/campaign_mission/completed_mission)
for(var/mob/living/carbon/human/human_mob AS in GLOB.alive_human_list_faction[faction])
- if((human_mob.z != completed_mission.mission_z_level.z_value) && human_mob.job.job_cost && human_mob.client)
+ if((human_mob.z && human_mob.z != completed_mission.mission_z_level.z_value) && human_mob.job.job_cost && human_mob.client) //why is byond so cursed that being inside something makes you z = 0
human_mob.revive(TRUE)
human_mob.overlay_fullscreen_timer(0.5 SECONDS, 10, "roundstart1", /atom/movable/screen/fullscreen/black)
human_mob.overlay_fullscreen_timer(2 SECONDS, 20, "roundstart2", /atom/movable/screen/fullscreen/spawning_in)
diff --git a/code/datums/gamemodes/campaign/individual_stats.dm b/code/datums/gamemodes/campaign/individual_stats.dm
index 0871258400e8f..3c5c2068c9ccb 100644
--- a/code/datums/gamemodes/campaign/individual_stats.dm
+++ b/code/datums/gamemodes/campaign/individual_stats.dm
@@ -4,7 +4,7 @@
///currently occupied mob - if any
var/mob/living/carbon/current_mob
///Credits. You buy stuff with it
- var/currency = 300
+ var/currency = 450
///List of job types based on faction
var/list/valid_jobs = list()
///Single list of unlocked perks for easy reference
@@ -126,7 +126,7 @@
return TRUE
///Adds and equips a loadout item, replacing another
-/datum/individual_stats/proc/replace_loadout_option(new_item, removed_item, job_type_or_types, job_req_override = TRUE)
+/datum/individual_stats/proc/replace_loadout_option(new_item, removed_item, job_type_or_types, job_req_override = FALSE)
if(!islist(job_type_or_types))
job_type_or_types = list(job_type_or_types)
var/datum/loadout_item/item = GLOB.campaign_loadout_item_type_list[new_item]
diff --git a/code/datums/gamemodes/campaign/missions/asat_capture.dm b/code/datums/gamemodes/campaign/missions/asat_capture.dm
index 20427ea4dbc54..a17748ca315a8 100644
--- a/code/datums/gamemodes/campaign/missions/asat_capture.dm
+++ b/code/datums/gamemodes/campaign/missions/asat_capture.dm
@@ -53,7 +53,7 @@
/datum/campaign_mission/capture_mission/asat/load_objective_description()
starting_faction_objective_description = "Major Victory:Capture all [objectives_total] ASAT systems.[min_capture_amount ? " Minor Victory: Capture at least [min_capture_amount] ASAT systems." : ""]"
- hostile_faction_objective_description = "Major Victory:Prevent the capture of all [objectives_total] ASAT systems.[min_capture_amount ? " Minor Victory: Prevent the capture of atleast [min_capture_amount] ASAT systems." : ""]"
+ hostile_faction_objective_description = "Major Victory:Prevent the capture of all [objectives_total] ASAT systems.[min_capture_amount ? " Minor Victory: Prevent the capture of atleast [objectives_total - min_capture_amount + 1] ASAT systems." : ""]"
/datum/campaign_mission/capture_mission/asat/check_mission_progress()
if(outcome)
diff --git a/code/datums/gamemodes/campaign/outfit_holder.dm b/code/datums/gamemodes/campaign/outfit_holder.dm
index 672e475ec1d19..97f6233f440c7 100644
--- a/code/datums/gamemodes/campaign/outfit_holder.dm
+++ b/code/datums/gamemodes/campaign/outfit_holder.dm
@@ -52,7 +52,7 @@
///Adds a new loadout_item to the available list
/datum/outfit_holder/proc/unlock_new_option(datum/loadout_item/new_item)
- available_list["[new_item.item_slot]"] += new_item
+ available_list["[new_item.item_slot]"] |= new_item
purchasable_list["[new_item.item_slot]"] -= new_item
///Adds a new loadout_item to the purchasable list
diff --git a/code/datums/gamemodes/campaign/perks.dm b/code/datums/gamemodes/campaign/perks.dm
index dbc628df80cb1..58e2f2a3ffb39 100644
--- a/code/datums/gamemodes/campaign/perks.dm
+++ b/code/datums/gamemodes/campaign/perks.dm
@@ -99,13 +99,13 @@ Needed both for a purchase list and effected list (if one perk impacts multiple
/datum/perk/trait/hp_boost
name = "Improved constitution"
- desc = "Through disciplined training and hypno indoctrination, your body is able to tolerate higher levels of trauma. +15 max health, +15 pain resistance."
+ desc = "Through disciplined training and hypno indoctrination, your body is able to tolerate higher levels of trauma. +25 max health, +25 pain resistance."
ui_icon = "health_1"
all_jobs = TRUE
unlock_cost = 800
traits = list(TRAIT_LIGHT_PAIN_RESIST)
///How much this perk increases your maxhp by
- var/health_mod = 15
+ var/health_mod = 25
/datum/perk/trait/hp_boost/apply_perk(mob/living/carbon/owner)
. = ..()
@@ -117,7 +117,7 @@ Needed both for a purchase list and effected list (if one perk impacts multiple
/datum/perk/trait/hp_boost/two
name = "Extreme constitution"
- desc = "Military grade biological augmentations are used to harden your body against grievous bodily harm. Provides an addition +15 max health and +10 pain resistance."
+ desc = "Military grade biological augmentations are used to harden your body against grievous bodily harm. Provides an addition +25 max health and +10 pain resistance."
req_desc = "Requires Improved constitution."
ui_icon = "health_2"
prereq_perks = list(/datum/perk/trait/hp_boost)
@@ -234,7 +234,7 @@ Needed both for a purchase list and effected list (if one perk impacts multiple
ui_icon = "firearms"
firearms = 1
all_jobs = TRUE
- unlock_cost = 600
+ unlock_cost = 400
/datum/perk/skill_mod/pistols
name = "Advanced pistol training"
@@ -306,9 +306,9 @@ Needed both for a purchase list and effected list (if one perk impacts multiple
name = "Advanced construction training"
desc = "Faster construction times when building. Some items may no longer have a penalty delay when constructing."
ui_icon = "construction"
- construction = 1
+ construction = 2
all_jobs = TRUE
- unlock_cost = 400
+ unlock_cost = 300
/datum/perk/skill_mod/leadership
name = "Advanced leadership training"
@@ -322,9 +322,9 @@ Needed both for a purchase list and effected list (if one perk impacts multiple
name = "Advanced medical training"
desc = "Faster at applying medical items. Some items may no longer have a penalty delay. Unlocks access to improved first aid pouches if not already available."
ui_icon = "medical"
- medical = 1
+ medical = 2
all_jobs = TRUE
- unlock_cost = 400
+ unlock_cost = 300
/datum/perk/skill_mod/medical/unlock_bonus(mob/living/carbon/owner, datum/individual_stats/owner_stats)
if(!istype(owner_stats))
@@ -347,7 +347,7 @@ Needed both for a purchase list and effected list (if one perk impacts multiple
all_jobs = TRUE
unlock_cost = 600
///How much this perk increases your max_stam by
- var/stam_mod = 5
+ var/stam_mod = 10
/datum/perk/skill_mod/stamina/apply_perk(mob/living/carbon/owner)
. = ..()
diff --git a/code/datums/gamemodes/infestation.dm b/code/datums/gamemodes/infestation.dm
index efeaaf618d1b4..04f1044ed805e 100644
--- a/code/datums/gamemodes/infestation.dm
+++ b/code/datums/gamemodes/infestation.dm
@@ -52,9 +52,8 @@
/datum/game_mode/infestation/announce_bioscans(show_locations = TRUE, delta = 2, ai_operator = FALSE, announce_humans = TRUE, announce_xenos = TRUE, send_fax = TRUE)
if(ai_operator)
- var/mob/living/silicon/ai/bioscanning_ai = usr
#ifndef TESTING
-
+ var/mob/living/silicon/ai/bioscanning_ai = usr
if((bioscanning_ai.last_ai_bioscan + COOLDOWN_AI_BIOSCAN) > world.time)
to_chat(bioscanning_ai, "Bioscan instruments are still recalibrating from their last use.")
return
diff --git a/code/datums/jobs/access.dm b/code/datums/jobs/access.dm
index b5a2eb0403158..490b939242863 100644
--- a/code/datums/jobs/access.dm
+++ b/code/datums/jobs/access.dm
@@ -236,13 +236,17 @@
if("E6")
. = size ? "SSGT" : "Staff Sergeant"
if("E7")
+ . = size ? "SFC" : "Sergeant First Class"
+ if("E7E")
. = size ? "GYSGT" : "Gunnery Sergeant"
if("E8")
. = size ? "MSGT" : "Master Sergeant"
if("E8E")
. = size ? "FSGT" : "First Sergeant"
if("E9")
- . = size ? "SGM" : "Sergeant Major"
+ . = size ? "MGYSGT" : "Master Gunnery Sergeant"
+ if("E9A")
+ . = size ? "SGM" : "Sergeant Major" //above master gunnery sergeant, below command sergeant major
if("E9E")
. = size ? "CSGM" : "Command Sergeant Major"
if("O1")
@@ -283,6 +287,10 @@
. = size ? "MAJ" : "Major"
if("MO5")
. = size ? "LtCol" : "Lieutenant Colonel"
+ if("MO6")
+ . = size ? "Col" : "Colonel"
+ if("M10")
+ . = size ? "Gen" : "General"
if("UPP1")
. = size ? "UGNR" : "USL Gunner"
if("UPP2")
diff --git a/code/datums/jobs/job/marines.dm b/code/datums/jobs/job/marines.dm
index ea1cbd8dc6602..4c4ff5ac574c2 100644
--- a/code/datums/jobs/job/marines.dm
+++ b/code/datums/jobs/job/marines.dm
@@ -137,10 +137,12 @@ Your squaddies will look to you when it comes to construction in the field of ba
new_human.wear_id.paygrade = "E3"
if(1501 to 6000) // 25 hrs
new_human.wear_id.paygrade = "E4"
- if(6001 to 60000) // 100 hrs
+ if(6001 to 18000) // 100 hrs
new_human.wear_id.paygrade = "E5"
+ if(18001 to 60000) // 300 hrs
+ new_human.wear_id.paygrade = "E6"
if(60001 to INFINITY) // 1000 hrs
- new_human.wear_id.paygrade = "E9" //If you play way too much TGMC. 1000 hours.
+ new_human.wear_id.paygrade = "E9A" //If you play way too much TGMC. 1000 hours.
//Squad Corpsman
/datum/job/terragov/squad/corpsman
@@ -197,10 +199,12 @@ You may not be a fully-fledged doctor, but you stand between life and death when
new_human.wear_id.paygrade = "E3"
if(1501 to 6000) // 25 hrs
new_human.wear_id.paygrade = "E4"
- if(6001 to 60000) // 100 hrs
+ if(6001 to 18000) // 100 hrs
new_human.wear_id.paygrade = "E5"
+ if(18001 to 60000) // 300 hrs
+ new_human.wear_id.paygrade = "E6"
if(60001 to INFINITY) // 1000 hrs
- new_human.wear_id.paygrade = "E9" //If you play way too much TGMC. 1000 hours.
+ new_human.wear_id.paygrade = "E9A" //If you play way too much TGMC. 1000 hours.
//Squad Smartgunner
/datum/job/terragov/squad/smartgunner
@@ -248,10 +252,12 @@ You may not be a fully-fledged doctor, but you stand between life and death when
new_human.wear_id.paygrade = "E3"
if(1501 to 6000) // 25 hrs
new_human.wear_id.paygrade = "E4"
- if(6001 to 60000) // 100 hrs
+ if(6001 to 18000) // 100 hrs
new_human.wear_id.paygrade = "E5"
+ if(18001 to 60000) // 300 hrs
+ new_human.wear_id.paygrade = "E6"
if(60001 to INFINITY) // 1000 hrs
- new_human.wear_id.paygrade = "E9" //If you play way too much TGMC. 1000 hours.
+ new_human.wear_id.paygrade = "E9A" //If you play way too much TGMC. 1000 hours.
/datum/outfit/job/marine/smartgunner
name = SQUAD_SMARTGUNNER
@@ -347,11 +353,13 @@ You are also in charge of communicating with command and letting them know about
var/playtime_mins = user?.client?.get_exp(title)
switch(playtime_mins)
if(0 to 1500) // starting
- new_human.wear_id.paygrade = "E5"
- if(1501 to 7500) // 25 hrs
- new_human.wear_id.paygrade = "E6"
- if(7501 to 60000) // 125 hrs
new_human.wear_id.paygrade = "E7"
+ if(1501 to 6000) // 25 hrs
+ new_human.wear_id.paygrade = "E7E"
+ if(6001 to 18000) // 100 hrs
+ new_human.wear_id.paygrade = "E8E"
+ if(18001 to 60000) // 300 hrs
+ new_human.wear_id.paygrade = "E9"
if(60001 to INFINITY) // 1000 hrs
new_human.wear_id.paygrade = "E9E" //If you play way too much TGMC. 1000 hours.
if(!latejoin)
diff --git a/code/datums/jobs/job/shipside.dm b/code/datums/jobs/job/shipside.dm
index 02eb5a4460185..83c744192fdd4 100644
--- a/code/datums/jobs/job/shipside.dm
+++ b/code/datums/jobs/job/shipside.dm
@@ -154,12 +154,16 @@ Make the TGMC proud!"})
if(!playtime_mins || playtime_mins < 1 )
return
switch(playtime_mins)
- if(0 to 1500) //starting
+ if(0 to 1500) // starting
new_human.wear_id.paygrade = "O3"
- if(1500 to 7500) // 25 hrs
+ if(1501 to 6000) // 25hrs
new_human.wear_id.paygrade = "MO4"
- if(7501 to INFINITY) // 125 hrs
+ if(6001 to 18000) // 100 hrs
new_human.wear_id.paygrade = "MO5"
+ if(18001 to 60000) // 300 hrs
+ new_human.wear_id.paygrade = "MO6"
+ if(60001 to INFINITY) // 1000 hrs
+ new_human.wear_id.paygrade = "M10" //If you play way too much TGMC. 1000 hours.
/datum/outfit/job/command/fieldcommander
@@ -483,12 +487,16 @@ You can serve your Division in a variety of roles, so choose carefully."})
if(!playtime_mins || playtime_mins < 1 )
return
switch(playtime_mins)
- if(0 to 1500) //starting
+ if(0 to 1500) // starting
new_human.wear_id.paygrade = "E3"
- if(1500 to 7500) // 25 hrs
+ if(1501 to 6000) // 25 hrs
new_human.wear_id.paygrade = "E4"
- if(7501 to INFINITY) // 125 hrs
+ if(6001 to 18000) // 100 hrs
new_human.wear_id.paygrade = "E5"
+ if(18001 to 60000) // 300 hrs
+ new_human.wear_id.paygrade = "E6"
+ if(60001 to INFINITY) // 1000 hrs
+ new_human.wear_id.paygrade = "E9A" //If you play way too much TGMC. 1000 hours.
/datum/outfit/job/command/mech_pilot
name = MECH_PILOT
diff --git a/code/datums/jobs/squads.dm b/code/datums/jobs/squads.dm
index b474c0c6e5fcf..b61225e7a5e9f 100644
--- a/code/datums/jobs/squads.dm
+++ b/code/datums/jobs/squads.dm
@@ -421,8 +421,8 @@ GLOBAL_LIST_EMPTY_TYPED(custom_squad_radio_freqs, /datum/squad)
var/freq = FREQ_CUSTOM_SQUAD_MIN + 2 * length(GLOB.custom_squad_radio_freqs)
if(freq > FREQ_CUSTOM_SQUAD_MAX)
return
-
- var/new_id = lowertext(squad_name) + "_squad"
+ var/lowertext_name = lowertext(squad_name)
+ var/new_id = lowertext_name + "_squad"
if(SSjob.squads[new_id])
return
@@ -436,6 +436,21 @@ GLOBAL_LIST_EMPTY_TYPED(custom_squad_radio_freqs, /datum/squad)
LAZYADDASSOCSIMPLE(GLOB.radiochannels, "[radio_channel_name]", freq)
LAZYADDASSOCSIMPLE(GLOB.reverseradiochannels, "[freq]", radio_channel_name)
new_squad.faction = squad_faction
+ var/key_prefix = lowertext_name[1]
+ if(GLOB.department_radio_keys[key_prefix])
+ for(var/letter in splittext(lowertext_name, ""))
+ if(!GLOB.department_radio_keys[letter])
+ key_prefix = letter
+ break
+ //okay... mustve been a very short name, randomly pick things from the alphabet now
+ for(var/letter in shuffle(GLOB.alphabet))
+ if(!GLOB.department_radio_keys[letter])
+ key_prefix = letter
+ break
+ key_prefix = "ERROR"
+ GLOB.department_radio_keys[key_prefix] = radio_channel_name
+ GLOB.channel_tokens[radio_channel_name] = ":[key_prefix]"
+
if(new_squad.faction == FACTION_TERRAGOV)
var/list/terragov_server_freqs = GLOB.telecomms_freq_listening_list[/obj/machinery/telecomms/server/presets/alpha]
var/list/terragov_bus_freqs = GLOB.telecomms_freq_listening_list[/obj/machinery/telecomms/bus/preset_three]
diff --git a/code/datums/keybinding/mob.dm b/code/datums/keybinding/mob.dm
index 343afcc7a5780..28b2d3b3bc296 100644
--- a/code/datums/keybinding/mob.dm
+++ b/code/datums/keybinding/mob.dm
@@ -312,3 +312,32 @@
return
user.mob.do_self_harm = !user.mob.do_self_harm
user.mob.balloon_alert(user.mob, "You can [user.mob.do_self_harm ? "now" : "no longer"] hit yourself")
+
+/datum/keybinding/mob/interactive_emote
+ name = "interactive_emote"
+ full_name = "Do interactive emote"
+ description = "Perform an interactive emote with another player."
+ keybind_signal = COMSIG_KB_INTERACTIVE_EMOTE
+
+/datum/keybinding/mob/interactive_emote/down(client/user)
+ . = ..()
+ if(. || !isliving(user.mob) || CHECK_BITFIELD(user.mob.status_flags, INCORPOREAL) || !user.mob.can_interact(user.mob))
+ return
+
+ var/list/adjacent_mobs = cheap_get_living_near(user.mob, 1)
+ adjacent_mobs.Remove(user.mob) //Get rid of self
+ for(var/mob/M AS in adjacent_mobs)
+ if(!M.client)
+ adjacent_mobs.Remove(M) //Get rid of non-players
+
+ if(!length(adjacent_mobs))
+ return
+
+ if(length(adjacent_mobs) == 1)
+ user.mob.interaction_emote(adjacent_mobs[1])
+ return
+
+ var/mob/target = tgui_input_list(user, "Who do you want to interact with?", "Select a target", adjacent_mobs)
+ if(!target || !user.mob.Adjacent(target)) //In case the target moved away while selecting them
+ return
+ user.mob.interaction_emote(target)
diff --git a/code/datums/keybinding/xeno.dm b/code/datums/keybinding/xeno.dm
index 38a46566429a7..993e78e739281 100644
--- a/code/datums/keybinding/xeno.dm
+++ b/code/datums/keybinding/xeno.dm
@@ -253,13 +253,6 @@
keybind_signal = COMSIG_XENOABILITY_CREATE_BOMB
hotkey_keys = list("F")
-/datum/keybinding/xeno/root
- name = "root"
- full_name = "Boiler: Root in place"
- description = "Begin rooting in place."
- keybind_signal = COMSIG_XENOABILITY_ROOT
- hotkey_keys = list("C")
-
/datum/keybinding/xeno/bombard
name = "bombard"
full_name = "Boiler: Bombard"
diff --git a/code/datums/personal_statistics.dm b/code/datums/personal_statistics.dm
index 26ce80e2462c2..8efe948565e0c 100644
--- a/code/datums/personal_statistics.dm
+++ b/code/datums/personal_statistics.dm
@@ -38,6 +38,8 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
var/projectile_damage = 0
var/melee_damage = 0
+ var/mechs_destroyed = 0
+
//We are watching
var/friendly_fire_damage = 0
@@ -62,6 +64,7 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
var/times_revived = 0
var/deaths = 0
+ var/shrapnel_removed = 0
//Downtime
var/time_resting = 0
@@ -117,6 +120,10 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
var/mission_blocker_destroyed = 0
var/mission_objective_captured = 0
var/mission_objective_decaptured = 0
+ var/mission_mechs_destroyed = 0
+ var/mission_shrapnel_removed = 0
+ var/mission_traps_created = 0
+ var/mission_grenades_primed = 0
/datum/personal_statistics/New()
. = ..()
@@ -170,6 +177,9 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
if(internal_injuries_inflicted)
stats += "Inflicted [internal_injuries_inflicted] internal injur[internal_injuries_inflicted > 1 ? "ies" : "y"] on [internal_injuries_inflicted > 1 ? "others" : "somebody"]."
+ if(mechs_destroyed)
+ stats += "[mechs_destroyed] hostile mechs destroyed."
+
//Medical
stats += "
"
if(self_heals)
@@ -183,6 +193,8 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
if(times_revived)
stats += "You were revived [times_revived] time\s."
stats += deaths ? "You died [deaths] time\s." : "You survived the whole round."
+ if(shrapnel_removed)
+ stats += "Removed [shrapnel_removed] piece\s of shrapnel."
//Downtime
var/list/downtime_stats = list()
@@ -298,6 +310,10 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
mission_blocker_destroyed = 0
mission_objective_captured = 0
mission_objective_decaptured = 0
+ mission_mechs_destroyed = 0
+ mission_shrapnel_removed = 0
+ mission_traps_created = 0
+ mission_grenades_primed = 0
///Returns the credit bonus based on stats from the current mission
/datum/personal_statistics/proc/get_mission_reward()
@@ -306,13 +322,17 @@ GLOBAL_LIST_EMPTY(personal_statistics_list)
credit_bonus -= mission_friendly_fire_damage * 0.2
credit_bonus += mission_melee_damage * 0.2
credit_bonus += mission_delimbs * 3
- credit_bonus += mission_revives * 10
+ credit_bonus += mission_revives * 15
credit_bonus += mission_times_revived * 5 //purple heart
- credit_bonus += mission_structures_built * 2
- credit_bonus += mission_objective_destroyed * 15
- credit_bonus += mission_blocker_destroyed * 10
- credit_bonus += mission_objective_captured * 5
- credit_bonus += mission_objective_decaptured * 3
+ credit_bonus += mission_structures_built * 6
+ credit_bonus += mission_objective_destroyed * 50
+ credit_bonus += mission_blocker_destroyed * 30
+ credit_bonus += mission_objective_captured * 20
+ credit_bonus += mission_objective_decaptured * 20
+ credit_bonus += mission_mechs_destroyed * 20
+ credit_bonus += mission_shrapnel_removed * 3
+ credit_bonus += mission_traps_created * 4
+ credit_bonus += mission_grenades_primed * 2
return max(floor(credit_bonus), 0)
@@ -579,6 +599,7 @@ The alternative is scattering them everywhere under their respective objects whi
return FALSE
var/datum/personal_statistics/personal_statistics = GLOB.personal_statistics_list[ckey]
personal_statistics.traps_created++
+ personal_statistics.mission_traps_created++
return TRUE
///Tally up bullets caught/reflected
diff --git a/code/datums/weather/weather_types/acid_rain.dm b/code/datums/weather/weather_types/acid_rain.dm
index 49b038bfcc60e..19f48eb3f81d3 100644
--- a/code/datums/weather/weather_types/acid_rain.dm
+++ b/code/datums/weather/weather_types/acid_rain.dm
@@ -14,7 +14,7 @@
weather_duration_upper = 1500
end_duration = 100
- end_message = span_boldannounce("The downpour gradually slows to a light shower. It should be safe outside now.")
+ end_message = span_danger("The downpour gradually slows to a light shower. It should be safe outside now.")
end_overlay = "rain_low"
area_type = /area
@@ -52,7 +52,7 @@
return
if(prob(L.modify_by_armor(100, ACID)))
L.adjustFireLoss(7)
- to_chat(L, span_boldannounce("You feel the acid rain melting you away!"))
+ to_chat(L, span_danger("You feel the acid rain melting you away!"))
L.clean_mob()
if(L.fire_stacks > -20)
L.fire_stacks = max(-20, L.fire_stacks - 1)
@@ -60,14 +60,14 @@
/datum/weather/acid_rain/harmless
target_trait = ZTRAIT_RAIN
- telegraph_message = span_boldannounce("Thunder rumbles far above. You hear droplets drumming against the canopy.")
+ telegraph_message = span_danger("Thunder rumbles far above. You hear droplets drumming against the canopy.")
telegraph_overlay = "rain_med"
telegraph_sound = null
- weather_message = span_boldannounce("Rain pours down around you!")
+ weather_message = span_danger("Rain pours down around you!")
weather_overlay = "rain_high"
- end_message = span_boldannounce("The downpour gradually slows to a light shower.")
+ end_message = span_danger("The downpour gradually slows to a light shower.")
end_overlay = "rain_low"
probability = 60
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index c1c250579f832..3f603288df4bd 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -12,7 +12,7 @@
weather_duration_upper = 1200
weather_overlay = "ash_storm"
- end_message = span_boldannounce("The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.")
+ end_message = span_danger("The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.")
end_duration = 300
end_overlay = "light_ash"
@@ -82,7 +82,7 @@
name = "emberfall"
desc = "A passing ash storm blankets the area in harmless embers."
- telegraph_message = span_boldannounce("An eerie moan rises on the wind. Sheets of burning ash blacken the horizon.")
+ telegraph_message = span_danger("An eerie moan rises on the wind. Sheets of burning ash blacken the horizon.")
weather_message = span_notice("Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by...")
weather_overlay = "light_ash"
diff --git a/code/datums/weather/weather_types/sand_storm.dm b/code/datums/weather/weather_types/sand_storm.dm
index ceef8344252d3..08b5ac694f9da 100644
--- a/code/datums/weather/weather_types/sand_storm.dm
+++ b/code/datums/weather/weather_types/sand_storm.dm
@@ -11,7 +11,7 @@
weather_duration_upper = 1200
weather_overlay = "dust_high"
- end_message = span_boldannounce("The shrieking wind whips away the last of the sand and falls to its usual murmur. It should be safe to go outside now.")
+ end_message = span_danger("The shrieking wind whips away the last of the sand and falls to its usual murmur. It should be safe to go outside now.")
end_duration = 300
end_overlay = "dust_med"
@@ -26,13 +26,13 @@
if(is_storm_immune(L))
return
L.adjustBruteLoss(6)
- to_chat(L, span_boldannounce("You are battered by the coarse sand!"))
+ to_chat(L, span_danger("You are battered by the coarse sand!"))
/datum/weather/ash_storm/sand/harmless
name = "Sandfall"
desc = "A passing sandstorm blankets the area in sand."
- telegraph_message = span_boldannounce("The wind begins to intensify, blowing sand up from the ground...")
+ telegraph_message = span_danger("The wind begins to intensify, blowing sand up from the ground...")
telegraph_overlay = "dust_low"
telegraph_sound = null
diff --git a/code/datums/weather/weather_types/snow_storm.dm b/code/datums/weather/weather_types/snow_storm.dm
index 7338d48ae53ea..0361a75abc014 100644
--- a/code/datums/weather/weather_types/snow_storm.dm
+++ b/code/datums/weather/weather_types/snow_storm.dm
@@ -13,7 +13,7 @@
weather_duration_upper = 1500
end_duration = 100
- end_message = span_boldannounce("The snowfall begins to slow.")
+ end_message = span_danger("The snowfall begins to slow.")
area_type = /area
protect_indoors = TRUE
diff --git a/code/game/atoms/atom_movable.dm b/code/game/atoms/atom_movable.dm
index d9867a0f33e92..b769bf8613dbc 100644
--- a/code/game/atoms/atom_movable.dm
+++ b/code/game/atoms/atom_movable.dm
@@ -477,7 +477,7 @@
var/old_throw_source = throw_source
hit_successful = hit_atom.hitby(src, speed)
if(hit_successful)
- SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, speed)
if(bounce && hit_atom.density && !isliving(hit_atom))
INVOKE_NEXT_TICK(src, PROC_REF(throw_bounce), hit_atom, old_throw_source)
return hit_successful //if the throw missed, it continues
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index bef9153a495ec..dc408b2239401 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -174,7 +174,6 @@
/mob/living/carbon/xenomorph/med_hud_set_status()
- hud_set_plasma()
hud_set_pheromone()
diff --git a/code/game/objects/items/cocoon.dm b/code/game/objects/items/cocoon.dm
index c6b7d5f5d26a9..90b134a5dbc1b 100644
--- a/code/game/objects/items/cocoon.dm
+++ b/code/game/objects/items/cocoon.dm
@@ -47,7 +47,7 @@
//Gives marine cloneloss for a total of 30.
victim.adjustCloneLoss(0.5)
-/obj/structure/cocoon/take_damage(damage_amount, damage_type, damage_flag, effects, attack_dir, armour_penetration)
+/obj/structure/cocoon/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
. = ..()
if(anchored && obj_integrity < max_integrity / 2)
unanchor_from_nest()
diff --git a/code/game/objects/items/devices/tablets.dm b/code/game/objects/items/devices/tablets.dm
index cb0cb9295e93a..acaf5ddc11b59 100644
--- a/code/game/objects/items/devices/tablets.dm
+++ b/code/game/objects/items/devices/tablets.dm
@@ -50,6 +50,11 @@
dat += " delta"
network = list("delta")
req_access = list(ACCESS_MARINE_LEADER, ACCESS_MARINE_DELTA)
+ else
+ var/lowername = lowertext(squad.name)
+ dat = dat + " " + lowername
+ network = list(lowername)
+ req_access = list(ACCESS_MARINE_LEADER)
dat += " squad leader's"
if(/datum/job/terragov/command/captain)
dat += " captain's"
diff --git a/code/game/objects/items/explosives/grenades/bullet_grenade.dm b/code/game/objects/items/explosives/grenades/bullet_grenade.dm
index 4ae55adb8c4dc..a5733b0a549d4 100644
--- a/code/game/objects/items/explosives/grenades/bullet_grenade.dm
+++ b/code/game/objects/items/explosives/grenades/bullet_grenade.dm
@@ -51,3 +51,19 @@
fire_sound = 'sound/weapons/burst_phaser2.ogg'
projectile_count = 20
ammo_type = /datum/ammo/energy/lasburster
+
+/obj/item/explosive/grenade/bullet/hefa
+ name = "\improper M25 HEFA grenade"
+ desc = "High explosive fragmentation grenades cause a powerful yet very small explosion combined with a scattering ring of buckshot shrapnel, please throw very, very, VERY far away."
+ icon_state = "grenade_hefa2"
+ item_state = "grenade_hefa2"
+ icon_state_mini = "grenade_hefa"
+ hud_state = "grenade_hefa2"
+ rotations = -1
+ fire_sound = null
+ projectile_count = 50
+ ammo_type = /datum/ammo/bullet/hefa_buckshot
+
+/obj/item/explosive/grenade/bullet/hefa/prime()
+ explosion(loc, light_impact_range = 2, heavy_impact_range = 1)
+ return ..()
diff --git a/code/game/objects/items/explosives/grenades/grenade.dm b/code/game/objects/items/explosives/grenades/grenade.dm
index c916839ad6018..cd424cca11ce9 100644
--- a/code/game/objects/items/explosives/grenades/grenade.dm
+++ b/code/game/objects/items/explosives/grenades/grenade.dm
@@ -73,7 +73,8 @@
if(user)
log_bomber(user, "primed", src)
var/datum/personal_statistics/personal_statistics = GLOB.personal_statistics_list[user.ckey]
- personal_statistics.grenades_primed++
+ personal_statistics.grenades_primed ++
+ personal_statistics.mission_grenades_primed ++
icon_state = initial(icon_state) + "_active"
active = TRUE
diff --git a/code/game/objects/items/radio/headset.dm b/code/game/objects/items/radio/headset.dm
index 8db63a999fc90..4c32db6d6c7c1 100644
--- a/code/game/objects/items/radio/headset.dm
+++ b/code/game/objects/items/radio/headset.dm
@@ -446,7 +446,10 @@ GLOBAL_LIST_INIT(channel_tokens, list(
/obj/item/radio/headset/mainship/marine/Initialize(mapload, datum/squad/squad, rank)
if(squad)
- icon_state = "headset_marine_[lowertext(squad.name)]"
+ icon_state = "headset_marine_greyscale"
+ var/image/coloring = image(icon, icon_state="headset_marine_overlay")
+ coloring.color = squad.color
+ add_overlay(coloring)
var/dat = "marine [lowertext(squad.name)]"
frequency = squad.radio_freq
if(ispath(rank, /datum/job/terragov/squad/leader))
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 6cd72dcc91f43..ff27a0e630168 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -1218,6 +1218,13 @@
spawn_type = /obj/item/explosive/grenade/bullet/laser
closed_overlay = "grenade_box_overlay_grenade_lasburster"
+/obj/item/storage/box/visual/grenade/hefa
+ name = "\improper M25 HEFA grenade box"
+ desc = "A secure box holding 25 M25 high explosive fragmentation grenades. Keep very far away from extreme heat and flame."
+ spawn_number = 25
+ spawn_type = /obj/item/explosive/grenade/bullet/hefa
+ closed_overlay = "grenade_box_overlay_grenade_hefa2"
+
/obj/item/storage/box/visual/grenade/training
name = "\improper M07 training grenade box"
desc = "A secure box holding 25 M07 training grenades. Harmless and reusable."
diff --git a/code/game/objects/items/storage/internal.dm b/code/game/objects/items/storage/internal.dm
index 2604e4522a868..15950a7b7fb8b 100644
--- a/code/game/objects/items/storage/internal.dm
+++ b/code/game/objects/items/storage/internal.dm
@@ -43,9 +43,6 @@
if(user.lying_angle || user.incapacitated()) //Can't use your inventory when lying
return FALSE
- if(istype(user.loc, /obj/vehicle/multitile/root/cm_armored)) //Stops inventory actions in a mech/tank
- return FALSE
-
if(over_object == user && Adjacent(user)) //This must come before the screen objects only block
open(user)
return FALSE
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index 6ddd3c93c9bf6..36db5ea7abc6e 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -86,9 +86,6 @@
if(usr.lying_angle)
return
- if(istype(usr.loc, /obj/vehicle/multitile/root/cm_armored)) // stops inventory actions in a mech/tank
- return
-
if(over_object == usr && Adjacent(usr)) // this must come before the screen objects only block
open(usr)
return
@@ -307,9 +304,6 @@
if(usr.incapacitated(TRUE))
return
- if(istype(usr.loc, /obj/vehicle/multitile/root/cm_armored)) // stops inventory actions in a mech/tank
- return
-
var/list/PL = params2list(params)
if(!master)
diff --git a/code/game/objects/machinery/doors/multi_tile.dm b/code/game/objects/machinery/doors/multi_tile.dm
index 69595ac2e6439..c53d57728187e 100644
--- a/code/game/objects/machinery/doors/multi_tile.dm
+++ b/code/game/objects/machinery/doors/multi_tile.dm
@@ -2,10 +2,10 @@
/obj/machinery/door/airlock/multi_tile
width = 2
-/obj/machinery/door/airlock/multi_tile/close() //Nasty as hell O(n^2) code but unfortunately necessary
+/obj/machinery/door/airlock/multi_tile/close() //Nasty as hell O(n^2) code but unfortunately necessary //honestly probably not, TODO fixme
for(var/turf/T in locs)
- for(var/obj/vehicle/multitile/M in T)
- if(M) return FALSE
+ for(var/obj/hitbox/hit in T)
+ return FALSE
return ..()
diff --git a/code/game/objects/machinery/overwatch.dm b/code/game/objects/machinery/overwatch.dm
index c1ea8ea919526..72a6f8a0679db 100644
--- a/code/game/objects/machinery/overwatch.dm
+++ b/code/game/objects/machinery/overwatch.dm
@@ -319,30 +319,8 @@ GLOBAL_LIST_EMPTY(active_cas_targets)
state = OW_MAIN
if("monitor")
state = OW_MONITOR
- if("monitoralpha_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(ALPHA_SQUAD)
- if("monitorbravo_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(BRAVO_SQUAD)
- if("monitorcharlie_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(CHARLIE_SQUAD)
- if("monitordelta_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(DELTA_SQUAD)
- if("monitorzulu_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(ZULU_SQUAD)
- if("monitoryankee_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(YANKEE_SQUAD)
- if("monitorxray_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(XRAY_SQUAD)
- if("monitorwhiskey_squad")
- state = OW_MONITOR
- current_squad = get_squad_by_id(WHISKEY_SQUAD)
+ if(href_list["squad_id"])
+ current_squad = get_squad_by_id(href_list["squad_id"])
if("change_operator")
if(operator != usr)
if(current_squad)
@@ -548,7 +526,7 @@ GLOBAL_LIST_EMPTY(active_cas_targets)
dat += "Squad Overwatch: [S.overwatch_officer.name]
"
else
dat += "Squad Overwatch: NONE
"
- dat += "[S.name] Squad Monitor
"
+ dat += "[S.name] Squad Monitor
"
dat += "----------------------
"
dat += "Orbital Bombardment Control
"
dat += "Current Cannon Status: "
@@ -608,7 +586,7 @@ GLOBAL_LIST_EMPTY(active_cas_targets)
dat += "Squad Overwatch: [S.overwatch_officer.name]
"
else
dat += "Squad Overwatch: NONE
"
- dat += "[S.name] Squad Monitor
"
+ dat += "[S.name] Squad Monitor
"
if(OW_MONITOR)//Info screen.
dat += get_squad_info()
diff --git a/code/game/objects/machinery/robotic_cradle.dm b/code/game/objects/machinery/robotic_cradle.dm
index 47bc4e5b26e3d..d39b662e776f5 100644
--- a/code/game/objects/machinery/robotic_cradle.dm
+++ b/code/game/objects/machinery/robotic_cradle.dm
@@ -6,6 +6,8 @@
#define CRADLE_NOTICE_IDIOT_EJECT 6
#define CRADLE_NOTICE_FORCE_EJECT 7
//Cradle
+//This code is so shit, I don't even want to fix it. If someone wants to, please fix var/repairing never being used and try to make sense of the procs
+//Like auto_start() gets called 20 seconds after the machine says it's starting, which seems to be turning on the machine, but immediately calls repair_op() which pops out the occupant ???????
/obj/machinery/robotic_cradle
name = "robotic cradle"
@@ -15,17 +17,16 @@
density = TRUE
max_integrity = 350
soft_armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 0, BIO = 100, FIRE = 30, ACID = 30)
- //This var is used to see if the machine is currently repairing or not.
- var/repairing = FALSE
- //This var is the reference used for the patient
- var/mob/living/carbon/human/occupant
-
//It uses power
use_power = ACTIVE_POWER_USE
idle_power_usage = 15
active_power_usage = 10000 // It rebuilds you from nothing...
- //This var is in reference to the radio the cradle uses to speak to the craw.
+ ///This var is used to see if the machine is currently repairing or not.
+ var/repairing = FALSE
+ ///This var is the reference used for the patient
+ var/mob/living/carbon/human/occupant
+ ///This var is in reference to the radio the cradle uses to speak to the crew
var/obj/item/radio/headset/mainship/doc/radio
/obj/machinery/robotic_cradle/Initialize(mapload)
@@ -39,16 +40,9 @@
return ..()
/obj/machinery/robotic_cradle/update_icon_state()
- . = ..()
- if(machine_stat & NOPOWER)
- icon_state = "borgcharger0"
- return
- if(repairing)
+ if(occupant && !(machine_stat & NOPOWER))
icon_state = "borgcharger1"
- return
- if(occupant)
- icon_state = "borgcharger1"
- return
+ return ..()
icon_state = "borgcharger0"
/obj/machinery/robotic_cradle/power_change()
@@ -72,7 +66,7 @@
if(!repairing)
return
-//This proc handles the actual repair once the timer is up, ejection of the healed robot and radio message of ejection.
+///This proc handles the actual repair once the timer is up, ejection of the healed robot and radio message of ejection.
/obj/machinery/robotic_cradle/proc/repair_op()
if(QDELETED(occupant) || occupant.stat == DEAD)
if(!ishuman(occupant))
@@ -161,12 +155,12 @@
dropped.stop_pulling()
dropped.forceMove(src)
occupant = dropped
- icon_state = "pod_0"
var/implants = list(/obj/item/implant/neurostim)
var/mob/living/carbon/human/H = occupant
var/doc_dat
med_scan(H, doc_dat, implants, TRUE)
start_processing()
+ update_icon()
say("Automatic mode engaged, initialising procedure.")
addtimer(CALLBACK(src, PROC_REF(auto_start)), 20 SECONDS)
@@ -181,9 +175,9 @@
say("Beginning repair procedure.")
repair_op()
-/obj/machinery/robotic_cradle/MouseDrop_T(mob/M, mob/user)
+/obj/machinery/robotic_cradle/MouseDrop_T(mob/dropping, mob/user)
. = ..()
- move_inside_wrapper(M, user)
+ move_inside_wrapper(dropping, user)
/obj/machinery/robotic_cradle/verb/move_inside()
set name = "Enter Cradle"
@@ -192,10 +186,9 @@
move_inside_wrapper(usr, usr)
-//This proc is called when someone has a robot grabbed either by hand or in a stasis bag. It is also lets docs/engineers use health analyzers on the cradle if they really want to.
/obj/machinery/robotic_cradle/attackby(obj/item/I, mob/user, params)
. = ..()
-
+ //This proc is called when someone has a robot grabbed either by hand or in a stasis bag. It is also lets docs/engineers use health analyzers on the cradle if they really want to.
if(!ishuman(user))
return //no
@@ -265,7 +258,7 @@
M.forceMove(src)
occupant = M
- icon_state = "pod_1"
+ update_icon()
var/implants = list(/obj/item/implant/neurostim)
var/mob/living/carbon/human/H = occupant
med_scan(H, null, implants, TRUE)
@@ -281,7 +274,7 @@
return
do_eject()
-//This proc ejects whomever is inside the cradle, by force if needed depending if the cradle is destroyed or not.
+///This proc ejects whomever is inside the cradle, by force if needed depending if the cradle is destroyed or not.
/obj/machinery/robotic_cradle/proc/do_eject(forceeject)
if(!occupant)
return
@@ -312,7 +305,7 @@
if(!do_after(usr, fumbling_time, NONE, src, BUSY_ICON_UNSKILLED) || !occupant)
return
if(repairing)
- repairing = 0
+ repairing = FALSE
if(usr.skills.getRating(SKILL_ENGINEER) < SKILL_ENGINEER_ENGI) //Untrained people will fail to terminate the repair properly.
visible_message("\The [src] malfunctions as [usr] aborts the rapair in progress.")
occupant.take_limb_damage(rand(30,50),rand(30,50))
@@ -321,3 +314,11 @@
go_out(CRADLE_NOTICE_IDIOT_EJECT)
return
go_out()
+
+#undef CRADLE_NOTICE_SUCCESS
+#undef CRADLE_NOTICE_DEATH
+#undef CRADLE_NOTICE_NO_RECORD
+#undef CRADLE_NOTICE_NO_POWER
+#undef CRADLE_NOTICE_XENO_FUCKERY
+#undef CRADLE_NOTICE_IDIOT_EJECT
+#undef CRADLE_NOTICE_FORCE_EJECT
diff --git a/code/game/objects/machinery/vending/marine_vending.dm b/code/game/objects/machinery/vending/marine_vending.dm
index 392b668093eb3..413996b674914 100644
--- a/code/game/objects/machinery/vending/marine_vending.dm
+++ b/code/game/objects/machinery/vending/marine_vending.dm
@@ -120,10 +120,11 @@
/obj/item/explosive/grenade/incendiary = 50,
/obj/item/explosive/grenade/smokebomb = 25,
/obj/item/explosive/grenade/smokebomb/cloak = 25,
- /obj/item/explosive/grenade/smokebomb/antigas = 10,
+ /obj/item/explosive/grenade/smokebomb/antigas = 20,
/obj/item/explosive/grenade/sticky/cloaker = 10,
/obj/item/explosive/grenade/mirage = 100,
/obj/item/explosive/grenade/bullet/laser = 30,
+ /obj/item/explosive/grenade/bullet/hefa = 10,
/obj/item/storage/box/m94 = 200,
/obj/item/storage/box/m94/cas = 30,
),
diff --git a/code/game/objects/machinery/vending/vending.dm b/code/game/objects/machinery/vending/vending.dm
index 9c5dd403a5b91..e2b30de41f5c0 100644
--- a/code/game/objects/machinery/vending/vending.dm
+++ b/code/game/objects/machinery/vending/vending.dm
@@ -874,7 +874,7 @@
. = TRUE
-/obj/machinery/vending/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0)
+/obj/machinery/vending/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
if(density && damage_amount >= knockdown_threshold)
tip_over()
return ..()
diff --git a/code/game/objects/machinery/vending/vending_types.dm b/code/game/objects/machinery/vending/vending_types.dm
index dd98590df3fbb..b818bf4a1f3b1 100644
--- a/code/game/objects/machinery/vending/vending_types.dm
+++ b/code/game/objects/machinery/vending/vending_types.dm
@@ -381,6 +381,7 @@
/obj/machinery/vending/nanomed/tadpolemed
name = "Flight surgeon medical equipment dispenser"
desc = "Dedicated for the surgeon with wings, this humble box contains a lot for its size."
+ layer = ABOVE_OBJ_LAYER
products = list(
"Autoinjectors" = list(
/obj/item/reagent_containers/hypospray/autoinjector/sleeptoxin = 2,
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index cfb9681b8c182..f0ec377736c42 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -1,4 +1,4 @@
-/obj/proc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0)
+/obj/proc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
if(QDELETED(src))
CRASH("[src] taking damage after deletion")
if(!damage_amount)
@@ -22,7 +22,7 @@
//DESTROYING SECOND
if(obj_integrity <= 0)
- obj_destruction(damage_amount, damage_type, damage_flag)
+ obj_destruction(damage_amount, damage_type, damage_flag, blame_mob)
///Increase obj_integrity and record it to the repairer's stats
/obj/proc/repair_damage(repair_amount, mob/user)
@@ -90,7 +90,7 @@
return
playsound(loc, P.hitsound, 50, 1)
visible_message(span_warning("\the [src] is damaged by \the [P]!"), visible_message_flags = COMBAT_MESSAGE)
- take_damage(P.damage, P.ammo.damage_type, P.ammo.armor_type, 0, REVERSE_DIR(P.dir), P.ammo.penetration)
+ take_damage(P.damage, P.ammo.damage_type, P.ammo.armor_type, 0, REVERSE_DIR(P.dir), P.ammo.penetration, isliving(P.firer) ? P.firer : null)
/obj/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = "", effects = TRUE, armor_penetration = 0) //used by attack_alien, attack_animal, and attack_slime
@@ -149,7 +149,7 @@
///what happens when the obj's integrity reaches zero.
-/obj/proc/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/proc/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
SHOULD_CALL_PARENT(TRUE)
if(destroy_sound)
playsound(loc, destroy_sound, 35, 1)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index ad1db49cc3377..c0aec6a2f2422 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -124,7 +124,7 @@
return FALSE
if((allow_pass_flags & PASS_MOB))
return TRUE
- if((allow_pass_flags & PASS_WALKOVER) && SEND_SIGNAL(target, COMSIG_OBJ_TRY_ALLOW_THROUGH))
+ if((allow_pass_flags & PASS_WALKOVER) && SEND_SIGNAL(target, COMSIG_OBJ_TRY_ALLOW_THROUGH, mover))
return TRUE
///Handles extra checks for things trying to exit this objects turf
@@ -147,7 +147,7 @@
return COMPONENT_ATOM_BLOCK_EXIT
///Signal handler to check if you can move from one low object to another
-/obj/proc/can_climb_over(datum/source)
+/obj/proc/can_climb_over(datum/source, atom/mover)
SIGNAL_HANDLER
if(!(flags_atom & ON_BORDER) && density)
return TRUE
diff --git a/code/game/objects/structures/campaign_structures/destroy_objectives.dm b/code/game/objects/structures/campaign_structures/destroy_objectives.dm
index d93c75896dafe..ddc07b4891e0c 100644
--- a/code/game/objects/structures/campaign_structures/destroy_objectives.dm
+++ b/code/game/objects/structures/campaign_structures/destroy_objectives.dm
@@ -273,10 +273,10 @@
switch(status)
if(BLUESPACE_CORE_OK)
. += image(icon, icon_state = "top_overlay", layer = ABOVE_MOB_LAYER)
- . += image(icon, icon_state = "bsd_c_s", layer = TANK_BARREL_LAYER)
+ . += image(icon, icon_state = "bsd_c_s", layer = ABOVE_MOB_PROP_LAYER)
if(BLUESPACE_CORE_UNSTABLE)
. += image(icon, icon_state = "top_overlay", layer = ABOVE_MOB_LAYER)
- . += image(icon, icon_state = "bsd_c_u", layer = TANK_BARREL_LAYER)
+ . += image(icon, icon_state = "bsd_c_u", layer = ABOVE_MOB_PROP_LAYER)
if(BLUESPACE_CORE_BROKEN)
. += image(icon, icon_state = "top_overlay_broken", layer = ABOVE_MOB_LAYER)
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
index 9fe779360f734..2d2999e545d14 100644
--- a/code/game/objects/structures/fence.dm
+++ b/code/game/objects/structures/fence.dm
@@ -16,6 +16,8 @@
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = list(SMOOTH_GROUP_FENCE)
canSmoothWith = list(SMOOTH_GROUP_FENCE)
+ ///Chance for the fence to break on /init
+ var/chance_to_break = 80 //Defaults to 80%
/obj/structure/fence/ex_act(severity)
switch(severity)
@@ -123,7 +125,7 @@
/obj/structure/fence/Initialize(mapload, start_dir)
. = ..()
- if(prob(80))
+ if(prob(chance_to_break))
obj_integrity = 0
deconstruct(FALSE)
@@ -139,3 +141,6 @@
if(exposed_temperature > T0C + 800)
take_damage(round(exposed_volume / 100), BURN, "fire")
return ..()
+
+/obj/structure/fence/broken
+ chance_to_break = 100
diff --git a/code/game/objects/structures/orbital_cannon.dm b/code/game/objects/structures/orbital_cannon.dm
index 1f6d075f97d4c..10e387eb2bc26 100644
--- a/code/game/objects/structures/orbital_cannon.dm
+++ b/code/game/objects/structures/orbital_cannon.dm
@@ -345,7 +345,7 @@
. += "Moving this will require some sort of lifter."
-/obj/structure/ob_ammo/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/structure/ob_ammo/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
explosion(loc, light_impact_range = 2, flash_range = 3, flame_range = 2)
return ..()
diff --git a/code/game/objects/structures/prop.dm b/code/game/objects/structures/prop.dm
index b4f805ee9635d..f5c58c81ae9b1 100644
--- a/code/game/objects/structures/prop.dm
+++ b/code/game/objects/structures/prop.dm
@@ -1095,7 +1095,7 @@
///BROKEN VEHICLE PROPS
/obj/structure/prop/vehicle
- layer = TANK_BARREL_LAYER
+ layer = ABOVE_MOB_PROP_LAYER
/obj/structure/prop/vehicle/van
name = "van"
desc = "An old van, seems to be broken down."
@@ -1243,7 +1243,7 @@
/obj/structure/prop/vehicle/tank/east/barrel
icon_state = "ltb_cannon_0"
- layer = TANK_BARREL_LAYER
+ layer = ABOVE_MOB_PROP_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/structure/prop/vehicle/tank/east/barrel/broken
diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
index bf7dc7ba5bfcc..c4c45dbfe6c4d 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
@@ -280,7 +280,7 @@
var/is_animating = 0
/obj/structure/bed/chair/dropship/passenger/CanAllowThrough(atom/movable/mover, turf/target, height = 0, air_group = 0)
- if(chair_state == DROPSHIP_CHAIR_UNFOLDED && istype(mover, /obj/vehicle/multitile) && !is_animating)
+ if(chair_state == DROPSHIP_CHAIR_UNFOLDED && istype(mover, /obj/vehicle/sealed) && !is_animating)
visible_message(span_danger("[mover] slams into [src] and breaks it!"))
INVOKE_ASYNC(src, PROC_REF(fold_down), TRUE)
return FALSE
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 33d24eeed3be6..b20b96066e868 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -465,6 +465,10 @@
table_status = TABLE_STATUS_FIRM
return TRUE
+/obj/structure/table/reinforced/weak //used for the icon, functionally similar to a table.
+ name = "rickety reinforced table"
+ desc = "A square metal surface resting on four legs. It has seen better days to whence it was strong."
+ max_integrity = 40
/obj/structure/table/reinforced/prison
desc = "A square metal surface resting on four legs. This one has side panels, making it useful as a desk, but impossible to flip."
diff --git a/code/game/turfs/snow.dm b/code/game/turfs/snow.dm
index 625236a1f91ab..da9b0a7d7ab7e 100644
--- a/code/game/turfs/snow.dm
+++ b/code/game/turfs/snow.dm
@@ -6,7 +6,7 @@
/turf/open/floor/plating/ground/snow
name = "snow layer"
icon = 'icons/turf/snow2.dmi'
- icon_state = "snow_0"
+ icon_state = "snow_0_1"
hull_floor = TRUE
shoefootstep = FOOTSTEP_SNOW
barefootstep = FOOTSTEP_SNOW
@@ -190,20 +190,20 @@
//SNOW LAYERS-----------------------------------//
/turf/open/floor/plating/ground/snow/layer0
- icon_state = "snow_0"
+ icon_state = "snow_0_1"
slayer = 0
minimap_color = MINIMAP_DIRT
/turf/open/floor/plating/ground/snow/layer1
- icon_state = "snow_1"
+ icon_state = "snow_1_1"
slayer = 1
/turf/open/floor/plating/ground/snow/layer2
- icon_state = "snow_2"
+ icon_state = "snow_2_1"
slayer = 2
/turf/open/floor/plating/ground/snow/layer3
- icon_state = "snow_3"
+ icon_state = "snow_3_1"
slayer = 3
diff --git a/code/game/turfs/walls/r_wall.dm b/code/game/turfs/walls/r_wall.dm
index 670207fe4a80a..c337bf8ed6df5 100644
--- a/code/game/turfs/walls/r_wall.dm
+++ b/code/game/turfs/walls/r_wall.dm
@@ -7,12 +7,14 @@
opacity = TRUE
density = TRUE
- max_integrity = 3000
+ max_integrity = 5000
max_temperature = 6000
walltype = "rwall"
explosion_block = 4
+ soft_armor = list(MELEE = 0, BULLET = 75, LASER = 75, ENERGY = 100, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0)
+
/turf/closed/wall/r_wall/get_acid_delay()
return 10 SECONDS
diff --git a/code/game/turfs/walls/resin.dm b/code/game/turfs/walls/resin.dm
index 50bfab98ae55c..67e3e62c620ea 100644
--- a/code/game/turfs/walls/resin.dm
+++ b/code/game/turfs/walls/resin.dm
@@ -15,7 +15,8 @@
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = list(SMOOTH_GROUP_XENO_STRUCTURES)
canSmoothWith = list(SMOOTH_GROUP_XENO_STRUCTURES)
- soft_armor = list(MELEE = 0, BULLET = 70, LASER = 60, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0)
+ soft_armor = list(MELEE = 0, BULLET = 80, LASER = 75, ENERGY = 75, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0)
+ hard_armor = list(MELEE = 0, BULLET = 15, LASER = 10, ENERGY = 10, BOMB =0 , BIO = 0, FIRE = 0, ACID = 0)
resistance_flags = UNACIDABLE
/turf/closed/wall/resin/add_debris_element()
@@ -122,6 +123,8 @@
var/multiplier = 1
if(I.damtype == BURN) //Burn damage deals extra vs resin structures (mostly welders).
multiplier += 1
+ else if(I.damtype == BRUTE)
+ multiplier += 0.75
if(istype(I, /obj/item/tool/pickaxe/plasmacutter) && !user.do_actions)
var/obj/item/tool/pickaxe/plasmacutter/P = I
@@ -152,14 +155,14 @@
* Regenerating walls that start with lower health, but grow to a much higher hp over time
*/
/turf/closed/wall/resin/regenerating
- max_integrity = 150
+ max_integrity = 75
/// Total health possible for a wall after regenerating at max health
var/max_upgradable_health = 300
/// How much the walls integrity heals per tick (5 seconds)
var/heal_per_tick = 25
/// How much the walls max_integrity increases per tick (5 seconds)
- var/max_upgrade_per_tick = 3
+ var/max_upgrade_per_tick = 6
/// How long should the wall stop healing for when taking dmg
var/cooldown_on_taking_dmg = 30 SECONDS
///Whether we have a timer already to stop from clogging up the timer ss
@@ -202,4 +205,4 @@
/* Hivelord walls, they start off stronger */
/turf/closed/wall/resin/regenerating/thick
- max_integrity = 250
+ max_integrity = 125
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 415a5d1c323e7..9ca6f86458135 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -1232,11 +1232,11 @@
if(!check_rights(R_ADMIN))
return
- for(var/obj/vehicle/multitile/root/cm_armored/CA AS in GLOB.tank_list)
- CA.remove_all_players()
+ for(var/obj/vehicle/sealed/armored/armor AS in GLOB.tank_list)
+ armor.dump_mobs(TRUE)
- log_admin("[key_name(usr)] forcibly removed all players from [CA].")
- message_admins("[ADMIN_TPMONTY(usr)] forcibly removed all players from [CA].")
+ log_admin("[key_name(usr)] forcibly removed all players from [armor].")
+ message_admins("[ADMIN_TPMONTY(usr)] forcibly removed all players from [armor].")
/// Admin verb to delete a squad completely
/datum/admins/proc/delete_squad()
diff --git a/code/modules/admin/fun_verbs.dm b/code/modules/admin/fun_verbs.dm
index 52299a0233c15..c4f15b830e321 100644
--- a/code/modules/admin/fun_verbs.dm
+++ b/code/modules/admin/fun_verbs.dm
@@ -1104,15 +1104,15 @@
if("Low gravity")
to_chat(GLOB.mob_living_list, span_highdanger("You feel gravity pull gently at you."))
for(var/mob/living/living_mob AS in GLOB.mob_living_list)
- living_mob.set_jump_component(duration = 1 SECONDS, cooldown = 1.5 SECONDS, cost = 2, height = 32, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE|PASS_DEFENSIVE_STRUCTURE)
+ living_mob.set_jump_component(duration = 1 SECONDS, cooldown = 1.5 SECONDS, cost = 2, height = 32, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE|PASS_DEFENSIVE_STRUCTURE|PASS_TANK)
if("John Woo")
to_chat(GLOB.mob_living_list, span_highdanger("You feel gravity grow weak, and the urge to fly."))
for(var/mob/living/living_mob AS in GLOB.mob_living_list)
- living_mob.set_jump_component(duration = 1 SECONDS, cooldown = 1.5 SECONDS, cost = 2, height = 48, sound = "jump", flags = JUMP_SPIN, flags_pass = HOVERING|PASS_PROJECTILE)
+ living_mob.set_jump_component(duration = 1 SECONDS, cooldown = 1.5 SECONDS, cost = 2, height = 48, sound = "jump", flags = JUMP_SPIN, flags_pass = HOVERING|PASS_PROJECTILE|PASS_TANK)
if("Exceeding orbital velocity")
to_chat(GLOB.mob_living_list, span_highdanger("You feel gravity fade to nothing. Will you even come back down?"))
for(var/mob/living/living_mob AS in GLOB.mob_living_list)
- living_mob.set_jump_component(duration = 4 SECONDS, cooldown = 6 SECONDS, cost = 0, height = 128, sound = "jump", flags = JUMP_SPIN, flags_pass = HOVERING|PASS_PROJECTILE)
+ living_mob.set_jump_component(duration = 4 SECONDS, cooldown = 6 SECONDS, cost = 0, height = 128, sound = "jump", flags = JUMP_SPIN, flags_pass = HOVERING|PASS_PROJECTILE|PASS_TANK)
else
return
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index ec19378458eed..d8cfa151bb142 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -430,6 +430,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
AddInteraction("Reopened by [key_name_admin(usr)]")
log_admin_private("Ticket (#[id]) reopened by [key_name(usr)].")
+ to_chat(initiator, span_adminhelp("Your ticket has been reopened."))
TicketPanel() //can only be done from here, so refresh it
@@ -451,11 +452,13 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
msg = "an admin ticket"
AddInteraction("Made admin ticket by: [key_name_admin(usr)].")
message_admins("Ticket [TicketHref("#[id]")] has been made [msg] by [ref].")
+ to_chat(initiator, span_adminhelp("Your ticket has been tiered to an adminhelp."))
else if(tier == TICKET_ADMIN)
tier = TICKET_MENTOR
msg = "a mentor ticket"
AddInteraction("Made mentor ticket by: [key_name_admin(usr)].")
message_staff("Ticket [TicketHref("#[id]")] has been made [msg] by [ref].")
+ to_chat(initiator, span_adminhelp("Your ticket has been tiered to a mentorhelp."))
if(!irc)
for(var/client/X in GLOB.admins)
if(!is_mentor(X))
@@ -483,6 +486,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else if(tier == TICKET_ADMIN)
message_admins("Ticket [TicketHref("#[id]")] has been unmarked by [ADMIN_TPMONTY(usr)].")
log_admin_private("Ticket (#[id]) has been unmarked by [key_name(usr)].")
+ to_chat(initiator, span_adminhelp("Your ticket has been unmarked."))
return
else if(alert("This ticket has already been marked by [marked], do you want to replace them?", "Confirmation", "Yes", "No") != "Yes")
return
@@ -492,6 +496,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
message_admins("Ticket [TicketHref("#[id]")] has been re-marked by [ADMIN_TPMONTY(usr)].")
marked = usr.client.key
log_admin_private("Ticket (#[id]) has been re-marked by [key_name(usr)].")
+ to_chat(initiator, span_adminhelp("Your ticket has been marked by another admin."))
return
marked = usr.client.key
if(tier == TICKET_MENTOR)
@@ -499,6 +504,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else if(tier == TICKET_ADMIN)
message_admins("Ticket [TicketHref("#[id]")] has been marked by [ADMIN_TPMONTY(usr)].")
log_admin_private("Ticket (#[id]) has been marked by [key_name(usr)].")
+ to_chat(initiator, span_adminhelp("Your ticket has been marked by an admin."))
//private
@@ -529,6 +535,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
AddInteraction("Closed by [key_name_admin(usr)].")
if(!silent)
log_admin_private("Ticket (#[id]) closed by [key_name(usr)].")
+ to_chat(initiator, span_adminhelp("Your ticket has been closed."))
if(tier == TICKET_MENTOR)
message_staff("Ticket [TicketHref("#[id]")] closed by [ref].")
else if(tier == TICKET_ADMIN)
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 9b5c84ce6f1f5..ba986f940da0e 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -188,7 +188,7 @@
if(CONFIG_GET(flag/log_access))
for(var/I in GLOB.clients)
if(!I)
- stack_trace("null in GLOB.clients during client/New()")
+ listclearnulls(GLOB.clients)
continue
if(I == src)
continue
diff --git a/code/modules/client/preferences_ui.dm b/code/modules/client/preferences_ui.dm
index 2a9cbf093e63f..9a883a9f99c21 100644
--- a/code/modules/client/preferences_ui.dm
+++ b/code/modules/client/preferences_ui.dm
@@ -125,6 +125,7 @@
data["radialstackspref"] = !!(toggles_gameplay & RADIAL_STACKS)
data["radiallasersgunpref"] = !!(toggles_gameplay & RADIAL_LASERGUNS)
data["autointeractdeployablespref"] = !!(toggles_gameplay & AUTO_INTERACT_DEPLOYABLES)
+ data["directional_attacks"] = !!(toggles_gameplay & DIRECTIONAL_ATTACKS)
data["scaling_method"] = scaling_method
data["pixel_size"] = pixel_size
data["parallax"] = parallax
@@ -808,6 +809,9 @@
if("autointeractdeployablespref")
toggles_gameplay ^= AUTO_INTERACT_DEPLOYABLES
+ if("directional_attacks")
+ toggles_gameplay ^= DIRECTIONAL_ATTACKS
+
if("pixel_size")
switch(pixel_size)
if(PIXEL_SCALING_AUTO)
diff --git a/code/modules/clothing/modular_armor/attachments/modules.dm b/code/modules/clothing/modular_armor/attachments/modules.dm
index 89db7366920c0..7549409b8d0dd 100644
--- a/code/modules/clothing/modular_armor/attachments/modules.dm
+++ b/code/modules/clothing/modular_armor/attachments/modules.dm
@@ -426,8 +426,11 @@
affected.add_filter("eshield", 2, outline_filter(1, new_color))
/obj/item/armor_module/module/eshield/overclocked
- max_shield_health = 65
- damaged_shield_cooldown = 6 SECONDS
+ max_shield_health = 75
+ damaged_shield_cooldown = 5 SECONDS
+ shield_color_low = COLOR_MAROON
+ shield_color_mid = LIGHT_COLOR_RED_ORANGE
+ shield_color_full = LIGHT_COLOR_ELECTRIC_CYAN
//original Martian design, donutsteel
/obj/item/armor_module/module/eshield/som
@@ -435,8 +438,11 @@
desc = "A sophisticated shielding unit, designed to disperse the energy of incoming impacts, rendering them harmless to the user. If it sustains too much it will deactivate, and leave the user vulnerable. It is unclear if this was a purely SOM designed module, or whether it was reverse engineered from the TGMC's 'Svalinn' shield system which was developed around the same time."
/obj/item/armor_module/module/eshield/som/overclocked
- max_shield_health = 65
- damaged_shield_cooldown = 6 SECONDS
+ max_shield_health = 75
+ damaged_shield_cooldown = 5 SECONDS
+ shield_color_low = COLOR_MAROON
+ shield_color_mid = LIGHT_COLOR_RED_ORANGE
+ shield_color_full = LIGHT_COLOR_ELECTRIC_CYAN
/obj/item/armor_module/module/style
name = "\improper Armor Equalizer"
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index df06ff15d0208..3e0f5e747b788 100755
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -537,6 +537,96 @@
else
airlock.cyclelinkeddir = dir
+/obj/effect/mapping_helpers/barricade
+ name = "base barricade helper"
+
+/obj/effect/mapping_helpers/barricade/wired
+ name = "wired barricade helper"
+ icon_state = "barricade_wired"
+
+/obj/effect/mapping_helpers/barricade/wired/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("### MAP WARNING, [src] spawned outside of mapload!")
+ return
+ var/obj/structure/barricade/foundbarricade = locate(/obj/structure/barricade) in loc
+ if(!foundbarricade)
+ CRASH("### MAP WARNING, [src] failed to find a barricade at [AREACOORD(src)]")
+ if(foundbarricade.is_wired || !foundbarricade.can_wire)
+ stack_trace("### MAP WARNING, [src] at [AREACOORD(src)] tried to make [foundbarricade] wired but it's already wired!")
+ foundbarricade.wire()
+
+/obj/effect/mapping_helpers/barricade/bomb
+ name = "bomb armor barricade helper"
+ icon_state = "barricade_bomb"
+
+/obj/effect/mapping_helpers/barricade/bomb/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("### MAP WARNING, [src] spawned outside of mapload!")
+ return
+ var/obj/structure/barricade/metal/foundbarricade = locate(/obj/structure/barricade/metal) in loc
+ if(!foundbarricade)
+ CRASH("### MAP WARNING, [src] failed to find a barricade at [AREACOORD(src)]")
+ if(foundbarricade.barricade_upgrade_type)
+ stack_trace("### MAP WARNING, [src] at [AREACOORD(src)] tried to upgrade [foundbarricade] but it already has armor!")
+ foundbarricade.soft_armor = soft_armor.modifyRating(bomb = 50)
+ foundbarricade.barricade_upgrade_type = CADE_TYPE_BOMB
+ foundbarricade.update_icon()
+
+/obj/effect/mapping_helpers/barricade/acid
+ name = "acid armor barricade helper"
+ icon_state = "barricade_acid"
+
+/obj/effect/mapping_helpers/barricade/acid/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("### MAP WARNING, [src] spawned outside of mapload!")
+ return
+ var/obj/structure/barricade/metal/foundbarricade = locate(/obj/structure/barricade/metal) in loc
+ if(!foundbarricade)
+ CRASH("### MAP WARNING, [src] failed to find a barricade at [AREACOORD(src)]")
+ if(foundbarricade.barricade_upgrade_type)
+ stack_trace("### MAP WARNING, [src] at [AREACOORD(src)] tried to upgrade [foundbarricade] but it already has armor!")
+ foundbarricade.barricade_upgrade_type = CADE_TYPE_ACID
+ foundbarricade.soft_armor = soft_armor.modifyRating(acid = 20)
+ foundbarricade.resistance_flags |= UNACIDABLE
+ foundbarricade.update_icon()
+
+/obj/effect/mapping_helpers/barricade/melee
+ name = "melee armor barricade helper"
+ icon_state = "barricade_melee"
+
+/obj/effect/mapping_helpers/barricade/melee/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("### MAP WARNING, [src] spawned outside of mapload!")
+ return
+ var/obj/structure/barricade/metal/foundbarricade = locate(/obj/structure/barricade/metal) in loc
+ if(!foundbarricade)
+ CRASH("### MAP WARNING, [src] failed to find a barricade at [AREACOORD(src)]")
+ if(foundbarricade.barricade_upgrade_type)
+ stack_trace("### MAP WARNING, [src] at [AREACOORD(src)] tried to upgrade [foundbarricade] but it already has armor!")
+ foundbarricade.barricade_upgrade_type = CADE_TYPE_MELEE
+ foundbarricade.soft_armor = soft_armor.modifyRating(melee = 30, bullet = 30, laser = 30, energy = 30)
+ foundbarricade.update_icon()
+
+/obj/effect/mapping_helpers/barricade/closed
+ name = "closed plasteel barricade helper"
+ icon_state = "barricade_closed"
+
+/obj/effect/mapping_helpers/barricade/closed/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("### MAP WARNING, [src] spawned outside of mapload!")
+ return
+ var/obj/structure/barricade/plasteel/foundbarricade = locate(/obj/structure/barricade/plasteel) in loc
+ if(!foundbarricade)
+ CRASH("### MAP WARNING, [src] failed to find a plasteel barricade at [AREACOORD(src)]")
+ if(!foundbarricade.closed)
+ stack_trace("### MAP WARNING, [src] at [AREACOORD(src)] tried to open [foundbarricade] but it's already open!")
+ foundbarricade.toggle_open()
+
//needs to do its thing before spawn_rivers() is called
/*
INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index 47796afa97083..2292f4143cff9 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -31,10 +31,6 @@
return TRUE
if(health >= get_crit_threshold())
-
- if(interaction_emote(src))
- return TRUE
-
help_shake_act(human_user)
return TRUE
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index bca1b3e113bdc..5c2fa668c23ee 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -213,11 +213,6 @@ Contains most of the procs that are called when a mob is attacked by something
//Melee weapon embedded object code.
if(affecting.limb_status & LIMB_DESTROYED)
hit_report += "(delimbed [affecting.display_name])"
- else if(I.damtype == BRUTE && !(HAS_TRAIT(I, TRAIT_NODROP) || (I.flags_item & DELONDROP)))
- if (percentage_penetration && weapon_sharp && prob(I.embedding.embed_chance))
- user.dropItemToGround(I, TRUE)
- I.embed_into(src, affecting)
- hit_report += "(embedded in [affecting.display_name])"
record_melee_damage(user, applied_damage, affecting.limb_status & LIMB_DESTROYED)
log_combat(user, src, "attacked", I, "(INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(I.damtype)]) [hit_report.Join(" ")]")
diff --git a/code/modules/mob/living/carbon/shock.dm b/code/modules/mob/living/carbon/shock.dm
index 1fe312ae2ad46..dd4d6f19d8ed4 100644
--- a/code/modules/mob/living/carbon/shock.dm
+++ b/code/modules/mob/living/carbon/shock.dm
@@ -96,9 +96,9 @@
traumatic_shock += reagent_pain_modifier
if(HAS_TRAIT(src, TRAIT_MEDIUM_PAIN_RESIST))
- traumatic_shock += PAIN_REDUCTION_MEDIUM
+ traumatic_shock += PAIN_REDUCTION_HEAVY
else if(HAS_TRAIT(src, TRAIT_LIGHT_PAIN_RESIST))
- traumatic_shock += PAIN_REDUCTION_LIGHT
+ traumatic_shock += PAIN_REDUCTION_MEDIUM
return traumatic_shock
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities.dm
index d573344cd3114..755b3dea99f89 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities.dm
@@ -6,7 +6,7 @@
name = "Rest"
action_icon_state = "resting"
desc = "Rest on weeds to regenerate health and plasma."
- use_state_flags = ABILITY_USE_LYING|ABILITY_USE_CRESTED|ABILITY_USE_AGILITY|ABILITY_USE_CLOSEDTURF
+ use_state_flags = ABILITY_USE_LYING|ABILITY_USE_CRESTED|ABILITY_USE_CLOSEDTURF
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_REST,
)
@@ -1321,7 +1321,7 @@
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_BLESSINGSMENU,
)
- use_state_flags = ABILITY_USE_LYING|ABILITY_USE_CRESTED|ABILITY_USE_AGILITY
+ use_state_flags = ABILITY_USE_LYING|ABILITY_USE_CRESTED
/datum/action/ability/xeno_action/blessing_menu/should_show()
return FALSE // Blessings meni now done through hive status UI!
diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
index c6533d7f703cf..8c283b276e7ab 100644
--- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
@@ -47,7 +47,7 @@
else if(SEND_SIGNAL(X, COMSIG_XENOMORPH_ZONE_SELECT) & COMSIG_ACCURATE_ZONE)
affecting = get_limb(X.zone_selected)
else
- affecting = get_limb(ran_zone(X.zone_selected, 70))
+ affecting = get_limb(ran_zone(X.zone_selected, XENO_DEFAULT_ACCURACY - X.xeno_caste.accuracy_malus))
if(!affecting || (random_location && !set_location) || (ignore_destroyed && !affecting.is_usable())) //No organ or it's destroyed, just get a random one
affecting = get_limb(ran_zone(null, 0))
if(!affecting || (no_head && affecting == get_limb("head")) || (ignore_destroyed && !affecting.is_usable()))
@@ -204,9 +204,6 @@
X.visible_message(span_danger("[X] stares at [src]."), span_notice("We stare at the roasting [src], toasty."), null, 5)
return FALSE
- if(interaction_emote(src))
- return TRUE
-
X.visible_message(span_notice("\The [X] caresses [src] with its scythe-like arm."), \
span_notice("We caress [src] with our scythe-like arm."), null, 5)
return FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/baneling/baneling.dm b/code/modules/mob/living/carbon/xenomorph/castes/baneling/baneling.dm
index 689a6f7448aeb..9b24feaab5007 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/baneling/baneling.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/baneling/baneling.dm
@@ -56,7 +56,7 @@
qdel(src)
/// We eject and kill our stored baneling if we have one
-/obj/structure/xeno/baneling_pod/obj_destruction()
+/obj/structure/xeno/baneling_pod/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
if(length(contents) <= 0)
return ..()
for(var/mob/living/carbon/xenomorph/xeno in contents)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/behemoth/abilities_behemoth.dm b/code/modules/mob/living/carbon/xenomorph/castes/behemoth/abilities_behemoth.dm
index ab446b023a06f..ba8037cc4e19e 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/behemoth/abilities_behemoth.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/behemoth/abilities_behemoth.dm
@@ -131,7 +131,7 @@
#define LANDSLIDE_ENDING_COLLISION_DELAY 0.3 SECONDS
#define LANDSLIDE_KNOCKDOWN_DURATION 1 SECONDS
#define LANDSLIDE_DAMAGE_MULTIPLIER 1.2
-#define LANDSLIDE_DAMAGE_MECHA_MODIFIER 20
+#define LANDSLIDE_DAMAGE_VEHICLE_MODIFIER 20
#define LANDSLIDE_OBJECT_INTEGRITY_THRESHOLD 150
#define LANDSLIDE_ENDED_CANCELLED (1<<0)
@@ -391,9 +391,9 @@
var/obj/structure/earth_pillar/affected_pillar = affected_atom
affected_pillar.throw_pillar(get_ranged_target_turf(xeno_owner, xeno_owner.dir, 7), TRUE)
continue
- if(ismecha(affected_atom))
- var/obj/vehicle/sealed/mecha/affected_mecha = affected_atom
- affected_mecha.take_damage(damage * LANDSLIDE_DAMAGE_MECHA_MODIFIER, MELEE)
+ if(isvehicle(affected_atom))
+ var/obj/vehicle/veh_victim = affected_atom
+ veh_victim.take_damage(damage * LANDSLIDE_DAMAGE_VEHICLE_MODIFIER, MELEE)
continue
var/obj/affected_object = affected_atom
if(!affected_object.density || affected_object.allow_pass_flags & PASS_MOB || affected_object.resistance_flags & INDESTRUCTIBLE)
@@ -453,9 +453,9 @@
var/obj/structure/earth_pillar/affected_pillar = affected_atom
affected_pillar.throw_pillar(get_ranged_target_turf(xeno_owner, xeno_owner.dir, 7), TRUE)
continue
- if(ismecha(affected_atom))
- var/obj/vehicle/sealed/mecha/affected_mecha = affected_atom
- affected_mecha.take_damage(damage * LANDSLIDE_DAMAGE_MECHA_MODIFIER, MELEE)
+ if(isvehicle(affected_atom))
+ var/obj/vehicle/veh_victim = affected_atom
+ veh_victim.take_damage(damage * LANDSLIDE_DAMAGE_VEHICLE_MODIFIER, MELEE)
continue
var/obj/affected_object = affected_atom
if(!affected_object.density || affected_object.allow_pass_flags & PASS_MOB || affected_object.resistance_flags & INDESTRUCTIBLE)
@@ -1391,7 +1391,7 @@
// ***************************************
// *********** Global Procs
// ***************************************
-#define AREA_ATTACK_DAMAGE_MECHA_MODIFIER 10
+#define AREA_ATTACK_DAMAGE_VEHICLE_MODIFIER 10
/**
* Checks for any atoms caught in the attack's range, and applies several effects based on the atom's type.
@@ -1422,7 +1422,7 @@
shake_camera(affected_living, 1, 0.8)
affected_living.Paralyze(paralyze_duration)
affected_living.apply_damage(attack_damage, BRUTE, blocked = MELEE)
- else if(isearthpillar(affected_atom) || ismecha(affected_atom) || istype(affected_atom, /obj/structure/reagent_dispensers/fueltank))
+ else if(isearthpillar(affected_atom) || isvehicle(affected_atom) || istype(affected_atom, /obj/structure/reagent_dispensers/fueltank))
affected_atom.do_jitter_animation()
new /obj/effect/temp_visual/behemoth/landslide/hit(affected_atom.loc)
playsound(affected_atom.loc, get_sfx("behemoth_earth_pillar_hit"), 40)
@@ -1436,9 +1436,9 @@
do_warning(xeno_owner, spread_turfs, wind_up_duration)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(behemoth_area_attack), xeno_owner, spread_turfs, enhanced), wind_up_duration)
continue
- if(ismecha(affected_atom))
- var/obj/vehicle/sealed/mecha/affected_mecha = affected_atom
- affected_mecha.take_damage(attack_damage * AREA_ATTACK_DAMAGE_MECHA_MODIFIER, MELEE)
+ if(isvehicle(affected_atom))
+ var/obj/vehicle/veh_victim = affected_atom
+ veh_victim.take_damage(attack_damage * AREA_ATTACK_DAMAGE_VEHICLE_MODIFIER, MELEE)
continue
if(istype(affected_atom, /obj/structure/reagent_dispensers/fueltank))
var/obj/structure/reagent_dispensers/fueltank/affected_tank = affected_atom
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/boiler/abilities_boiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/boiler/abilities_boiler.dm
index 0b473797dd433..f25b748d80575 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/boiler/abilities_boiler.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/boiler/abilities_boiler.dm
@@ -33,7 +33,6 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_LONG_RANGE_SIGHT,
)
- use_state_flags = ABILITY_USE_ROOTED
/datum/action/ability/xeno_action/toggle_long_range/action_activate()
var/mob/living/carbon/xenomorph/boiler/X = owner
@@ -57,7 +56,7 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
name = "Toggle Bombard Type"
action_icon_state = "toggle_bomb0"
desc = "Switches Boiler Bombard type between available glob types."
- use_state_flags = ABILITY_USE_BUSY|ABILITY_USE_LYING|ABILITY_USE_ROOTED
+ use_state_flags = ABILITY_USE_BUSY|ABILITY_USE_LYING
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_TOGGLE_BOMB,
KEYBINDING_ALTERNATE = COMSIG_XENOABILITY_TOGGLE_BOMB_RADIAL,
@@ -135,7 +134,7 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
action_icon = 'icons/xeno/actions_boiler_glob.dmi'
desc = "Creates a Boiler Bombard of the type currently selected."
ability_cost = 200
- use_state_flags = ABILITY_USE_BUSY|ABILITY_USE_LYING|ABILITY_USE_ROOTED
+ use_state_flags = ABILITY_USE_BUSY|ABILITY_USE_LYING
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_CREATE_BOMB,
)
@@ -180,10 +179,8 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
desc = "Launch a glob of neurotoxin or acid. Must be rooted to use."
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_BOMBARD,
- KEYBINDING_ALTERNATE = COMSIG_XENOABILITY_ROOT,
)
target_flags = ABILITY_TURF_TARGET
- use_state_flags = ABILITY_USE_ROOTED
/datum/action/ability/activable/xeno/bombard/get_cooldown()
var/mob/living/carbon/xenomorph/boiler/X = owner
@@ -213,11 +210,6 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
var/turf/S = get_turf(owner)
var/mob/living/carbon/xenomorph/boiler/boiler_owner = owner
- if(!HAS_TRAIT_FROM(owner, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- if(!silent)
- to_chat(owner, span_warning("We need to be rooted to fire!"))
- return FALSE
-
if(istype(boiler_owner.ammo, /datum/ammo/xeno/boiler_gas/corrosive))
if(boiler_owner.corrosive_ammo <= 0)
boiler_owner.balloon_alert(boiler_owner, "No corrosive globules.")
@@ -227,10 +219,6 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
boiler_owner.balloon_alert(boiler_owner, "No neurotoxin globules.")
return FALSE
- if(!HAS_TRAIT_FROM(boiler_owner, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- boiler_owner.balloon_alert(boiler_owner, "We need to be rooted to the ground to fire!")
- return FALSE
-
if(!isturf(T) || T.z != S.z)
if(!silent)
boiler_owner.balloon_alert(boiler_owner, "Invalid target.")
@@ -278,52 +266,8 @@ GLOBAL_LIST_INIT(boiler_glob_image_list, list(
update_button_icon()
add_cooldown()
-
-/datum/action/ability/activable/xeno/bombard/alternate_action_activate()
- INVOKE_ASYNC(src, PROC_REF(root))
- return COMSIG_KB_ACTIVATED
-
-/// The alternative action of bombard, rooting. It begins the rooting/unrooting process.
-/datum/action/ability/activable/xeno/bombard/proc/root()
- if(HAS_TRAIT_FROM(owner, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- owner.balloon_alert_to_viewers("Rooting out of place...")
- if(!do_after(owner, 3 SECONDS, IGNORE_HELD_ITEM, null, BUSY_ICON_HOSTILE))
- owner.balloon_alert(owner, "Interrupted!")
- return
- owner.balloon_alert(owner, "Unrooted!")
- set_rooted(FALSE)
- return
-
- if(HAS_TRAIT_FROM(owner, TRAIT_FLOORED, RESTING_TRAIT))
- owner.balloon_alert(owner, "Cannot while lying down!")
- return
-
- owner.balloon_alert_to_viewers("Rooting into place...")
- if(!do_after(owner, 3 SECONDS, IGNORE_HELD_ITEM, null, BUSY_ICON_HOSTILE))
- owner.balloon_alert(owner, "Interrupted!")
- return
-
- owner.balloon_alert_to_viewers("Rooted into place!")
- set_rooted(TRUE)
-
-/// Proc that actually does the rooting, makes us immobile and anchors us in place. Similar to defender's fortify.
-/datum/action/ability/activable/xeno/bombard/proc/set_rooted(on)
- var/mob/living/carbon/xenomorph/boiler/boiler_owner = owner
- if(on)
- ADD_TRAIT(boiler_owner, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT)
- if(boiler_owner.client)
- boiler_owner.client.mouse_pointer_icon = 'icons/mecha/mecha_mouse.dmi'
- else
- REMOVE_TRAIT(boiler_owner, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT)
- if(boiler_owner.client)
- boiler_owner.client.mouse_pointer_icon = initial(boiler_owner.client.mouse_pointer_icon)
-
- boiler_owner.anchored = on
-
-
// ***************************************
// *********** Acid spray
// ***************************************
/datum/action/ability/activable/xeno/spray_acid/line/boiler
cooldown_duration = 9 SECONDS
- use_state_flags = ABILITY_USE_ROOTED
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/boiler/boiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/boiler/boiler.dm
index 2b06d204b63d5..c21e8bd62e805 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/boiler/boiler.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/boiler/boiler.dm
@@ -48,14 +48,6 @@
ammo = GLOB.ammo_list[/datum/ammo/xeno/boiler_gas]
update_boiler_glow()
RegisterSignal(src, COMSIG_XENOMORPH_GIBBING, PROC_REF(gib_explode))
- RegisterSignal(src, COMSIG_MOB_STAT_CHANGED, PROC_REF(on_stat_change))
- RegisterSignals(src, list(COMSIG_LIVING_STATUS_STUN,
- COMSIG_LIVING_STATUS_KNOCKDOWN,
- COMSIG_LIVING_STATUS_PARALYZE,
- COMSIG_LIVING_STATUS_IMMOBILIZE,
- COMSIG_LIVING_STATUS_UNCONSCIOUS,
- COMSIG_LIVING_STATUS_SLEEP,
- COMSIG_LIVING_STATUS_STAGGER), PROC_REF(on_stun))
// ***************************************
// *********** Gibbing behaviour
@@ -66,18 +58,3 @@
smoke.set_up(2, get_turf(src))
smoke.start()
-/// Handles boilers changing stat, you unroot yourself if you change stat, like going from conscious to unconscious
-/mob/living/carbon/xenomorph/boiler/proc/on_stat_change(datum/source, old_state, new_state)
- SIGNAL_HANDLER
- var/datum/action/ability/activable/xeno/bombard/bombard_action = actions_by_path[/datum/action/ability/activable/xeno/bombard]
- if(HAS_TRAIT_FROM(src, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- bombard_action.set_rooted(FALSE)
-
-/// Handles getting staggered, stunned and other various debuffs
-/mob/living/carbon/xenomorph/boiler/proc/on_stun(datum/source, amount)
- SIGNAL_HANDLER
- if(!(amount > 0) || !HAS_TRAIT_FROM(src, TRAIT_IMMOBILE, BOILER_ROOTED_TRAIT))
- return
- var/datum/action/ability/activable/xeno/bombard/bombard_action = actions_by_path[/datum/action/ability/activable/xeno/bombard]
- balloon_alert_to_viewers("[src] scrambles out of the ground from the impact!")
- bombard_action.set_rooted(FALSE)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/carrier/abilities_carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/carrier/abilities_carrier.dm
index 7747bdaaf7048..e9c8392eb83e5 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/carrier/abilities_carrier.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/carrier/abilities_carrier.dm
@@ -119,6 +119,7 @@ GLOBAL_LIST_INIT(hugger_images_list, list(
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_PLACE_TRAP,
)
+ use_state_flags = ABILITY_USE_LYING
/datum/action/ability/xeno_action/place_trap/can_use_action(silent = FALSE, override_flags)
. = ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/crusher/castedatum_crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/crusher/castedatum_crusher.dm
index d66020a852eae..becfd77a9fd4a 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/crusher/castedatum_crusher.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/crusher/castedatum_crusher.dm
@@ -30,11 +30,14 @@
// *** Flags *** //
can_flags = CASTE_CAN_BE_QUEEN_HEALED|CASTE_CAN_BE_GIVEN_PLASMA|CASTE_CAN_BE_LEADER
- caste_traits = null
+ caste_traits = list(TRAIT_STOPS_TANK_COLLISION)
// *** Defense *** //
soft_armor = list(MELEE = 90, BULLET = 75, LASER = 75, ENERGY = 75, BOMB = 130, BIO = 100, FIRE = 40, ACID = 100)
+ // *** Sunder *** //
+ sunder_multiplier = 0.5
+
// *** Minimap Icon *** //
minimap_icon = "crusher"
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm
index 90ea762a94f5e..d64fe6bbd9446 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm
@@ -324,6 +324,7 @@
SSblackbox.record_feedback("tally", "round_statistics", 1, "defender_fortifiy_toggles")
if(on)
ADD_TRAIT(X, TRAIT_IMMOBILE, FORTIFY_TRAIT)
+ ADD_TRAIT(X, TRAIT_STOPS_TANK_COLLISION, FORTIFY_TRAIT)
if(!silent)
to_chat(X, span_xenowarning("We tuck ourselves into a defensive stance."))
X.soft_armor = X.soft_armor.modifyAllRatings(last_fortify_bonus)
@@ -334,6 +335,7 @@
X.soft_armor = X.soft_armor.modifyAllRatings(-last_fortify_bonus)
X.soft_armor = X.soft_armor.modifyRating(BOMB = -last_fortify_bonus)
REMOVE_TRAIT(X, TRAIT_IMMOBILE, FORTIFY_TRAIT)
+ REMOVE_TRAIT(X, TRAIT_STOPS_TANK_COLLISION, FORTIFY_TRAIT)
X.fortify = on
X.anchored = on
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/drone/abilities_drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/drone/abilities_drone.dm
index 2bde8ec324b16..aec42303b3657 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/drone/abilities_drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/drone/abilities_drone.dm
@@ -23,6 +23,8 @@
KEYBINDING_NORMAL = COMSIG_XENOABILITY_ESSENCE_LINK,
KEYBINDING_ALTERNATE = COMSIG_XENOABILITY_ESSENCE_LINK_REMOVE,
)
+ use_state_flags = ABILITY_USE_LYING
+
/// Used to determine whether there is an existing Essence Link or not. Also allows access to its vars.
var/datum/status_effect/stacking/essence_link/existing_link
/// The target of an existing link, if applicable.
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/hivelord/abilities_hivelord.dm b/code/modules/mob/living/carbon/xenomorph/castes/hivelord/abilities_hivelord.dm
index aacf7d4bb904b..efd4fe1ab14e0 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/hivelord/abilities_hivelord.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/hivelord/abilities_hivelord.dm
@@ -245,6 +245,7 @@
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_PLACE_JELLY_POD,
)
+ use_state_flags = ABILITY_USE_LYING
/datum/action/ability/xeno_action/place_jelly_pod/can_use_action(silent = FALSE, override_flags)
. = ..()
@@ -285,6 +286,7 @@
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_CREATE_JELLY,
)
+ use_state_flags = ABILITY_USE_LYING
/datum/action/ability/xeno_action/create_jelly/can_use_action(silent = FALSE, override_flags)
. = ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/hivemind/hivemind.dm b/code/modules/mob/living/carbon/xenomorph/castes/hivemind/hivemind.dm
index d00138803b567..4f08bc673e8ac 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/hivemind/hivemind.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/hivemind/hivemind.dm
@@ -119,7 +119,7 @@
setDir(SOUTH)
addtimer(CALLBACK(src, PROC_REF(do_change_form)), TIME_TO_TRANSFORM)
-/mob/living/carbon/xenomorph/hivemind/set_jump_component(duration = 0.5 SECONDS, cooldown = 2 SECONDS, cost = 0, height = 16, sound = null, flags = JUMP_SHADOW, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE)
+/mob/living/carbon/xenomorph/hivemind/set_jump_component(duration = 0.5 SECONDS, cooldown = 2 SECONDS, cost = 0, height = 16, sound = null, flags = JUMP_SHADOW, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE|PASS_TANK)
return //no jumping, bad hivemind
///Finish the form changing of the hivemind and give the needed stats
@@ -363,7 +363,7 @@
X.visible_message(span_danger("[X] nudges its head against [src]."), \
span_danger("You nudge your head against [src]."))
-/obj/structure/xeno/hivemindcore/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+/obj/structure/xeno/hivemindcore/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
. = ..()
var/mob/living/carbon/xenomorph/hivemind/our_parent = get_parent()
if(isnull(our_parent))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm b/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm
index 42ae48d6d2126..cb30c7d28d7da 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm
@@ -18,7 +18,7 @@
attack_delay = 7
// *** Speed *** //
- speed = -1.4
+ speed = -1.5
weeds_speed_mod = -0.1
// *** Plasma *** //
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/king/abilities_king.dm b/code/modules/mob/living/carbon/xenomorph/castes/king/abilities_king.dm
index 3b4303d2e88f7..6b948c7c4324c 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/king/abilities_king.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/king/abilities_king.dm
@@ -258,9 +258,9 @@
shake_camera(carbon_victim, 3 * severity, 3 * severity)
carbon_victim.apply_effect(1 SECONDS, WEAKEN)
to_chat(carbon_victim, "You are smashed to the ground!")
- else if(ismecha(victim))
- var/obj/vehicle/sealed/mecha/mecha_victim = victim
- mecha_victim.take_damage(SHATTERING_ROAR_DAMAGE * 5 * severity, BRUTE, MELEE)
+ else if(isvehicle(victim))
+ var/obj/vehicle/veh_victim = victim
+ veh_victim.take_damage(SHATTERING_ROAR_DAMAGE * 5 * severity, BRUTE, MELEE)
else if(istype(victim, /obj/structure/window))
var/obj/structure/window/window_victim = victim
if(window_victim.damageable)
@@ -396,9 +396,9 @@
human_victim.take_overall_damage(15, BURN, updating_health = TRUE)
human_victim.flash_weak_pain()
animation_flash_color(human_victim)
- else if(ismecha(victim))
- var/obj/vehicle/sealed/mecha/mech_victim = victim
- mech_victim.take_damage(75, BURN, ENERGY, armour_penetration = 60)
+ else if(isvehicle(victim))
+ var/obj/vehicle/veh_victim = victim
+ veh_victim.take_damage(75, BURN, ENERGY, armour_penetration = 60)
timer_ref = addtimer(CALLBACK(src, PROC_REF(execute_attack)), ZEROFORM_TICK_RATE, TIMER_STOPPABLE)
///ends and cleans up beam
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/praetorian/abilities_praetorian.dm b/code/modules/mob/living/carbon/xenomorph/castes/praetorian/abilities_praetorian.dm
index 7f4af840556cc..75450dc997e40 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/praetorian/abilities_praetorian.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/praetorian/abilities_praetorian.dm
@@ -57,7 +57,7 @@
return FALSE
return TRUE
-GLOBAL_LIST_INIT(acid_spray_hit, typecacheof(list(/obj/structure/barricade, /obj/vehicle/multitile/root/cm_armored, /obj/structure/razorwire)))
+GLOBAL_LIST_INIT(acid_spray_hit, typecacheof(list(/obj/structure/barricade, /obj/hitbox, /obj/structure/razorwire)))
#define CONE_PART_MIDDLE (1<<0)
#define CONE_PART_LEFT (1<<1)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/puppet/castedatum_puppet.dm b/code/modules/mob/living/carbon/xenomorph/castes/puppet/castedatum_puppet.dm
index 2f392f948a9f3..3971824a2115f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/puppet/castedatum_puppet.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/puppet/castedatum_puppet.dm
@@ -10,6 +10,7 @@
tier = XENO_TIER_MINION
upgrade = XENO_UPGRADE_BASETYPE
melee_damage = 15
+ accuracy_malus = 65
speed = -0.8
plasma_max = 2
plasma_gain = 0
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/abilities_puppeteer.dm b/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/abilities_puppeteer.dm
index 4e422ca0ee751..d3e956fd00378 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/abilities_puppeteer.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/abilities_puppeteer.dm
@@ -193,7 +193,7 @@
var/mob/living/carbon/xenomorph/owner_xeno = owner
if(target.stat == DEAD)
return
- owner_xeno.plasma_stored = min(owner_xeno.plasma_stored + round(damage / 0.9), owner_xeno.xeno_caste.plasma_max)
+ owner_xeno.gain_plasma(floor(damage / 0.9))
// ***************************************
// *********** Stitch Puppet
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/puppeteer.dm b/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/puppeteer.dm
index 8cb288e62b46c..b599484c312f2 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/puppeteer.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/puppeteer/puppeteer.dm
@@ -23,5 +23,5 @@
SIGNAL_HANDLER
if(target.stat == DEAD)
return
- plasma_stored = min(plasma_stored + round(damage / 0.8), xeno_caste.plasma_max)
+ gain_plasma(floor(damage / 0.8))
SEND_SIGNAL(src, COMSIG_PUPPET_CHANGE_ALL_ORDER, PUPPET_ATTACK, target) //we are on harm intent so it probably means we want to kill the target
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/queen/abilities_queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/queen/abilities_queen.dm
index 623e185697a64..da422c2d216c6 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/queen/abilities_queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/queen/abilities_queen.dm
@@ -97,14 +97,16 @@
GLOB.round_statistics.queen_screech++
SSblackbox.record_feedback("tally", "round_statistics", 1, "queen_screech")
X.create_shriekwave() //Adds the visual effect. Wom wom wom
- //stop_momentum(charge_dir) //Screech kills a charge
var/list/nearby_living = list()
for(var/mob/living/L in hearers(WORLD_VIEW, X))
nearby_living.Add(L)
+ for(var/obj/vehicle/sealed/armored/tank AS in GLOB.tank_list)
+ if(get_dist(tank, X) > WORLD_VIEW_NUM)
+ continue
+ nearby_living += tank.occupants
- for(var/i in GLOB.mob_living_list)
- var/mob/living/L = i
+ for(var/mob/living/L AS in GLOB.mob_living_list)
if(get_dist(L, X) > WORLD_VIEW_NUM)
continue
L.screech_act(X, WORLD_VIEW_NUM, L in nearby_living)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/queen/castedatum_queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/queen/castedatum_queen.dm
index f28eb8f293027..a565c605574dc 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/queen/castedatum_queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/queen/castedatum_queen.dm
@@ -23,6 +23,9 @@
// *** Health *** //
max_health = 500
+ // *** Sunder *** //
+ sunder_multiplier = 0.8
+
// *** Evolution *** //
upgrade_threshold = TIER_THREE_THRESHOLD
evolve_min_xenos = 8
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/shrike/abilities_shrike.dm b/code/modules/mob/living/carbon/xenomorph/castes/shrike/abilities_shrike.dm
index f49bf7eed8514..6ed87b43f6e70 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/shrike/abilities_shrike.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/shrike/abilities_shrike.dm
@@ -318,6 +318,7 @@
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_PLACE_ACID_WELL,
)
+ use_state_flags = ABILITY_USE_LYING
/datum/action/ability/xeno_action/place_acidwell/can_use_action(silent = FALSE, override_flags)
. = ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/spiderling/castedatum_spiderling.dm b/code/modules/mob/living/carbon/xenomorph/castes/spiderling/castedatum_spiderling.dm
index b94fb962caf35..4f51ba3d19628 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/spiderling/castedatum_spiderling.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/spiderling/castedatum_spiderling.dm
@@ -12,6 +12,7 @@
// *** Melee Attacks *** //
melee_damage = 8
+ accuracy_malus = 65
// *** Speed *** //
speed = -0.6
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/warlock/abilities_warlock.dm b/code/modules/mob/living/carbon/xenomorph/castes/warlock/abilities_warlock.dm
index c90a795867610..ccc2fa71e695d 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/warlock/abilities_warlock.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/warlock/abilities_warlock.dm
@@ -221,7 +221,7 @@
release_projectiles()
owner.apply_effect(1 SECONDS, WEAKEN)
-/obj/effect/xeno/shield/obj_destruction()
+/obj/effect/xeno/shield/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
release_projectiles()
owner.apply_effect(1 SECONDS, WEAKEN)
return ..()
@@ -404,9 +404,9 @@
carbon_victim.apply_damage(xeno_owner.xeno_caste.crush_strength * 1.5, STAMINA, blocked = BOMB)
carbon_victim.adjust_stagger(5 SECONDS)
carbon_victim.add_slowdown(6)
- else if(ismecha(victim))
- var/obj/vehicle/sealed/mecha/mecha_victim = victim
- mecha_victim.take_damage(xeno_owner.xeno_caste.crush_strength * 5, BRUTE, BOMB)
+ else if(isvehicle(victim))
+ var/obj/vehicle/veh_victim = victim
+ veh_victim.take_damage(xeno_owner.xeno_caste.crush_strength * 5, BRUTE, BOMB)
stop_crush()
/// stops channeling and unregisters all listeners, resetting the ability
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/warrior/abilities_warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/warrior/abilities_warrior.dm
index 6591e7d8bb95a..ee2471a22a048 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/warrior/abilities_warrior.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/warrior/abilities_warrior.dm
@@ -1,261 +1,371 @@
+// ***************************************
+// *********** Empower
+// ***************************************
+#define WARRIOR_EMPOWER_COMBO_THRESHOLD 2 // After how many abilities should a Warrior get an empowered cast (2 means the 3rd one is empowered).
+#define WARRIOR_EMPOWER_COMBO_FADE_TIME 10 SECONDS // The duration of a combo, after which it will disappear by itself.
+
+/datum/action/ability/xeno_action/empower
+ name = "Empower"
+ /// Holds the fade-out timer.
+ var/fade_timer
+ /// The amount of abilities we've chained together.
+ var/combo_count = 0
+ /// List of abilities that can be empowered.
+ var/list/empowerable_actions = list(
+ /datum/action/ability/activable/xeno/warrior/fling,
+ /datum/action/ability/activable/xeno/warrior/grapple_toss,
+ /datum/action/ability/activable/xeno/warrior/punch,
+ /datum/action/ability/activable/xeno/warrior/punch/flurry,
+ )
+
+/datum/action/ability/xeno_action/empower/should_show()
+ return FALSE
+
+/// Checks if Empower is capped and gives bonuses if so, otherwise increases combo count.
+/datum/action/ability/xeno_action/empower/proc/check_empower(atom/target)
+ if(isliving(target))
+ var/mob/living/living_target = target
+ if(living_target.stat == DEAD || living_target.issamexenohive(owner))
+ return FALSE
+ if(combo_count >= WARRIOR_EMPOWER_COMBO_THRESHOLD)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ xeno_owner.emote("roar")
+ clear_empower()
+ return TRUE
+ activate_empower()
+ return FALSE
+
+/// Handles empowering, and gives visual feedback if applicable.
+/datum/action/ability/xeno_action/empower/proc/activate_empower()
+ combo_count++
+ if(combo_count >= WARRIOR_EMPOWER_COMBO_THRESHOLD)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ for(var/datum/action/ability/activable/xeno/warrior/warrior_action AS in xeno_owner.actions)
+ if(warrior_action.type in empowerable_actions)
+ warrior_action.add_empowered_frame()
+ warrior_action.update_button_icon()
+ fade_timer = addtimer(CALLBACK(src, PROC_REF(empower_fade)), WARRIOR_EMPOWER_COMBO_FADE_TIME, TIMER_OVERRIDE|TIMER_UNIQUE|TIMER_STOPPABLE)
+
+/// Clears empowering, as well as visual feedback and combo count.
+/datum/action/ability/xeno_action/empower/proc/clear_empower()
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ for(var/datum/action/ability/activable/xeno/warrior/warrior_action AS in xeno_owner.actions)
+ if(warrior_action.type in empowerable_actions)
+ warrior_action.remove_empowered_frame()
+ warrior_action.update_button_icon()
+ combo_count = initial(combo_count)
+ deltimer(fade_timer)
+
+/// Happens when Empower fades.
+/datum/action/ability/xeno_action/empower/proc/empower_fade()
+ owner.playsound_local(owner, 'sound/voice/hiss4.ogg', 25, 0, 1)
+ clear_empower()
+
+
// ***************************************
// *********** Agility
// ***************************************
+#define WARRIOR_AGILITY_SPEED_MODIFIER -0.6
+#define WARRIOR_AGILITY_ARMOR_MODIFIER 30
+
/datum/action/ability/xeno_action/toggle_agility
- name = "Toggle Agility"
+ name = "Agility"
action_icon_state = "agility_on"
- desc = "Move an all fours for greater speed. Cannot use abilities while in this mode."
- cooldown_duration = 0.5 SECONDS
- use_state_flags = ABILITY_USE_AGILITY
+ cooldown_duration = 0.4 SECONDS
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_TOGGLE_AGILITY,
)
- var/last_agility_bonus = 0
+ action_type = ACTION_TOGGLE
+ /// Whether the ability is active or not.
+ var/ability_active = FALSE
-/datum/action/ability/xeno_action/toggle_agility/give_action()
+/datum/action/ability/xeno_action/toggle_agility/New(Target)
. = ..()
- var/mob/living/carbon/xenomorph/X = owner
- last_agility_bonus = X.xeno_caste.agility_speed_armor
-
-/datum/action/ability/xeno_action/toggle_agility/on_xeno_upgrade()
- var/mob/living/carbon/xenomorph/X = owner
- if(X.agility)
- X.soft_armor = X.soft_armor.modifyAllRatings(-last_agility_bonus)
- last_agility_bonus = X.xeno_caste.agility_speed_armor
- X.soft_armor = X.soft_armor.modifyAllRatings(last_agility_bonus)
- X.add_movespeed_modifier(MOVESPEED_ID_WARRIOR_AGILITY , TRUE, 0, NONE, TRUE, X.xeno_caste.agility_speed_increase)
- else
- last_agility_bonus = X.xeno_caste.agility_speed_armor
-
-/datum/action/ability/xeno_action/toggle_agility/on_cooldown_finish()
- var/mob/living/carbon/xenomorph/X = owner
- to_chat(X, span_notice("We can [X.agility ? "raise ourselves back up" : "lower ourselves back down"] again."))
- return ..()
+ desc = "Move on all fours and loosen our scales. Increases movement speed by [abs(WARRIOR_AGILITY_SPEED_MODIFIER)], but reduces all soft armor by [WARRIOR_AGILITY_ARMOR_MODIFIER]. Automatically disabled after using an ability."
/datum/action/ability/xeno_action/toggle_agility/action_activate()
- var/mob/living/carbon/xenomorph/X = owner
-
- X.agility = !X.agility
-
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
GLOB.round_statistics.warrior_agility_toggles++
SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_agility_toggles")
-
- if(X.agility)
- to_chat(X, span_xenowarning("We lower ourselves to all fours and loosen our armored scales to ease our movement."))
- X.add_movespeed_modifier(MOVESPEED_ID_WARRIOR_AGILITY , TRUE, 0, NONE, TRUE, X.xeno_caste.agility_speed_increase)
- X.soft_armor = X.soft_armor.modifyAllRatings(last_agility_bonus)
- owner.toggle_move_intent(MOVE_INTENT_RUN) //By default we swap to running when activating agility
- else
- to_chat(X, span_xenowarning("We raise ourselves to stand on two feet, hard scales setting back into place."))
- X.remove_movespeed_modifier(MOVESPEED_ID_WARRIOR_AGILITY)
- X.soft_armor = X.soft_armor.modifyAllRatings(-last_agility_bonus)
-
- X.update_icons()
+ ability_active = !ability_active
+ set_toggle(ability_active ? TRUE : FALSE)
+ xeno_owner.update_icons()
add_cooldown()
- return succeed_activate()
+ if(!ability_active)
+ xeno_owner.remove_movespeed_modifier(MOVESPEED_ID_WARRIOR_AGILITY)
+ xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(WARRIOR_AGILITY_ARMOR_MODIFIER)
+ return
+ xeno_owner.add_movespeed_modifier(MOVESPEED_ID_WARRIOR_AGILITY, TRUE, 0, NONE, TRUE, WARRIOR_AGILITY_SPEED_MODIFIER)
+ xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(-WARRIOR_AGILITY_ARMOR_MODIFIER)
+ xeno_owner.toggle_move_intent(MOVE_INTENT_RUN)
+
+
+// ***************************************
+// *********** Parent Ability
+// ***************************************
+#define WARRIOR_IMPACT_DAMAGE_MULTIPLIER 0.5
+#define WARRIOR_DISPLACE_KNOCKDOWN 0.4 SECONDS
+
+/datum/action/ability/activable/xeno/warrior/use_ability(atom/A)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ var/datum/action/ability/xeno_action/toggle_agility/agility_action = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/toggle_agility]
+ if(agility_action?.ability_active)
+ agility_action.action_activate()
+
+/// Adds an outline around the ability button to represent Empower.
+/datum/action/ability/activable/xeno/warrior/proc/add_empowered_frame()
+ button.add_overlay(visual_references[VREF_MUTABLE_EMPOWERED_FRAME])
+
+/// Removes the Empower outline.
+/datum/action/ability/activable/xeno/warrior/proc/remove_empowered_frame()
+ button.cut_overlay(visual_references[VREF_MUTABLE_EMPOWERED_FRAME])
+
+/// Handles anything that would happen when a target is thrown into an atom using an ability.
+/datum/action/ability/activable/xeno/warrior/proc/thrown_into(datum/source, atom/hit_atom, impact_speed)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
+ var/mob/living/living_target = source
+ INVOKE_ASYNC(living_target, TYPE_PROC_REF(/mob, emote), "scream")
+ living_target.Knockdown(WARRIOR_DISPLACE_KNOCKDOWN)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ new /obj/effect/temp_visual/warrior/impact(get_turf(living_target), get_dir(living_target, xeno_owner))
+ // mob/living/turf_collision() does speed * 5 damage on impact with a turf, and we don't want to go overboard, so we deduce that here.
+ var/thrown_damage = ((xeno_owner.xeno_caste.melee_damage * xeno_owner.xeno_melee_damage_modifier) - (impact_speed * 5)) * WARRIOR_IMPACT_DAMAGE_MULTIPLIER
+ living_target.apply_damage(thrown_damage, BRUTE, blocked = MELEE)
+ if(isliving(hit_atom))
+ var/mob/living/hit_living = hit_atom
+ if(hit_living.issamexenohive(xeno_owner))
+ return
+ INVOKE_ASYNC(hit_living, TYPE_PROC_REF(/mob, emote), "scream")
+ hit_living.apply_damage(thrown_damage, BRUTE, blocked = MELEE)
+ hit_living.Knockdown(WARRIOR_DISPLACE_KNOCKDOWN)
+ step_away(hit_living, living_target, 1, 1)
+ if(isobj(hit_atom))
+ var/obj/hit_object = hit_atom
+ if(istype(hit_object, /obj/structure/xeno))
+ return
+ hit_object.take_damage(thrown_damage, BRUTE)
+ if(iswallturf(hit_atom))
+ var/turf/closed/wall/hit_wall = hit_atom
+ if(!(hit_wall.resistance_flags & INDESTRUCTIBLE))
+ hit_wall.take_damage(thrown_damage, BRUTE)
+
+/// Ends the target's throw.
+/datum/action/ability/activable/xeno/warrior/proc/throw_ended(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, COMSIG_MOVABLE_POST_THROW)
+ /* So the reason why we do not flat out unregister this is because, when an atom makes impact with something, it calls throw_impact(). Calling it this way causes
+ stop_throw() to be called in most cases, because impacts can cause a bounce effect and ending the throw makes it happen. Given the way we have signals setup, unregistering
+ it at that point would cause thrown_into() to never get called, and that is exactly the reason why the line of code below exists. */
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, UnregisterSignal), source, COMSIG_MOVABLE_IMPACT, COMSIG_MOVABLE_POST_THROW), 1)
+ var/mob/living/living_target = source
+ living_target.Knockdown(0.5 SECONDS)
+ if(living_target.pass_flags & PASS_XENO)
+ living_target.pass_flags &= ~PASS_XENO
+
+/obj/effect/temp_visual/warrior/impact
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "throw_impact"
+ duration = 3.5
+ layer = ABOVE_ALL_MOB_LAYER
+ pixel_x = -32
+ pixel_y = -32
+
+/obj/effect/temp_visual/warrior/impact/Initialize(mapload, direction)
+ . = ..()
+ animate(src, alpha = 0, time = duration - 1.5)
+ // directions refuse to work naturally so i improvised, suck it byond
+ direction = closest_cardinal_dir(direction)
+ switch(direction)
+ if(NORTH)
+ icon_state = "[initial(icon_state)]_n"
+ pixel_y -= 20
+ if(SOUTH)
+ icon_state = "[initial(icon_state)]_s"
+ pixel_y += 20
+ if(WEST)
+ icon_state = "[initial(icon_state)]_w"
+ pixel_x += 20
+ if(EAST)
+ icon_state = "[initial(icon_state)]_e"
+ pixel_x -= 20
+
// ***************************************
// *********** Lunge
// ***************************************
-/datum/action/ability/activable/xeno/lunge
+#define WARRIOR_LUNGE_RANGE 4 // in tiles
+
+/datum/action/ability/activable/xeno/warrior/lunge
name = "Lunge"
action_icon_state = "lunge"
- desc = "Pounce up to 5 tiles and grab a target, knocking them down and putting them in your grasp."
- ability_cost = 25
+ ability_cost = 30
cooldown_duration = 20 SECONDS
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_LUNGE,
)
target_flags = ABILITY_MOB_TARGET
- /// The target of our lunge, we keep it to check if we are adjacent everytime we move
+ /// The target of our lunge, we keep it to check if we are adjacent every time we move.
var/atom/lunge_target
-/datum/action/ability/activable/xeno/lunge/on_cooldown_finish()
- to_chat(owner, span_xenodanger("We ready ourselves to lunge again."))
- owner.playsound_local(owner, 'sound/effects/xeno_newlarva.ogg', 25, 0, 1)
+/datum/action/ability/activable/xeno/warrior/lunge/New(Target)
+ . = ..()
+ desc = "Lunge towards a target within [WARRIOR_LUNGE_RANGE] tiles, putting them in our grasp. Usable on allies."
+
+/datum/action/ability/activable/xeno/warrior/lunge/on_cooldown_finish()
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ xeno_owner.balloon_alert(xeno_owner, "[initial(name)] ready")
return ..()
-/datum/action/ability/activable/xeno/lunge/can_use_ability(atom/A, silent = FALSE, override_flags)
+/datum/action/ability/activable/xeno/warrior/lunge/can_use_ability(atom/A, silent = FALSE, override_flags)
. = ..()
if(!.)
return FALSE
-
- if(get_dist_euclide_square(A, owner) > 20)
- if(!silent)
- to_chat(owner, span_xenonotice("You are too far!"))
- return FALSE
-
- if(!isliving(A)) //We can only lunge at the living; expanded to xenos in order to allow for supportive applications; lunging > throwing to safety
+ if(!isliving(A))
if(!silent)
- to_chat(owner, span_xenodanger("We can't [initial(name)] at that!"))
+ owner.balloon_alert(owner, "Invalid target")
return FALSE
-
var/mob/living/living_target = A
- if(living_target.stat == DEAD)
+ if(living_target.stat == DEAD && !living_target.issamexenohive(owner))
if(!silent)
- to_chat(owner, span_xenodanger("We can't [initial(name)] at that!"))
+ owner.balloon_alert(owner, "Dead")
return FALSE
-
-/datum/action/ability/activable/xeno/lunge/ai_should_start_consider()
- return TRUE
-
-/datum/action/ability/activable/xeno/lunge/ai_should_use(atom/target)
- if(!iscarbon(target))
- return FALSE
- if(!line_of_sight(owner, target, 2))
- return FALSE
- if(!can_use_ability(target, override_flags = ABILITY_IGNORE_SELECTED_ABILITY))
- return FALSE
- if(target.get_xeno_hivenumber() == owner.get_xeno_hivenumber())
+ if(get_dist_euclide_square(living_target, owner) > WARRIOR_LUNGE_RANGE * 5)
+ if(!silent)
+ owner.balloon_alert(owner, "Too far")
return FALSE
- return TRUE
-
-/datum/action/ability/activable/xeno/lunge/use_ability(atom/A)
- var/mob/living/carbon/xenomorph/X = owner
+/datum/action/ability/activable/xeno/warrior/lunge/use_ability(atom/A)
+ . = ..()
GLOB.round_statistics.warrior_lunges++
SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_lunges")
- X.visible_message(span_xenowarning("\The [X] lunges towards [A]!"), \
- span_xenowarning("We lunge at [A]!"))
-
- X.add_filter("warrior_lunge", 2, gauss_blur_filter(3))
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ xeno_owner.add_filter("warrior_lunge", 2, gauss_blur_filter(3))
lunge_target = A
-
- RegisterSignal(lunge_target, COMSIG_QDELETING, PROC_REF(clean_lunge_target))
- RegisterSignal(X, COMSIG_MOVABLE_MOVED, PROC_REF(check_if_lunge_possible))
- RegisterSignal(X, COMSIG_MOVABLE_POST_THROW, PROC_REF(clean_lunge_target))
- if(lunge_target.Adjacent(X)) //They're already in range, neck grab without lunging.
- lunge_grab(X, lunge_target)
- else
- X.throw_at(get_step_towards(A, X), 6, 2, X)
-
- if(X.pulling && !isxeno(X.pulling)) //If we grabbed something give us combo.
- X.empower(empowerable = FALSE) //Doesn't have a special interaction
succeed_activate()
add_cooldown()
- return TRUE
-
-///Check if we are close enough to lunge, and if yes, grab neck
-/datum/action/ability/activable/xeno/lunge/proc/check_if_lunge_possible(datum/source)
- SIGNAL_HANDLER
- if(!lunge_target.Adjacent(source))
+ if(lunge_target.Adjacent(xeno_owner)) // They're already in range, neck grab without lunging.
+ lunge_grab(lunge_target)
return
- INVOKE_ASYNC(src, PROC_REF(lunge_grab), source, lunge_target)
+ RegisterSignal(lunge_target, COMSIG_QDELETING, PROC_REF(clean_lunge_target))
+ RegisterSignal(xeno_owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_if_lunge_possible))
+ RegisterSignal(xeno_owner, COMSIG_MOVABLE_POST_THROW, PROC_REF(clean_lunge_target))
+ xeno_owner.throw_at(get_step_towards(A, xeno_owner), WARRIOR_LUNGE_RANGE, 2, xeno_owner)
-///Do a last check to see if we can grab the target, and then clean up after the throw. Handles an in-place lunge.
-/datum/action/ability/activable/xeno/lunge/proc/finish_lunge(datum/source)
+/// Check if we are close enough to grab.
+/datum/action/ability/activable/xeno/warrior/lunge/proc/check_if_lunge_possible(datum/source)
SIGNAL_HANDLER
- check_if_lunge_possible(source)
- if(lunge_target) //Still couldn't get them.
- clean_lunge_target()
+ if(lunge_target.Adjacent(source))
+ INVOKE_ASYNC(src, PROC_REF(lunge_grab), lunge_target)
-/// Null lunge target and reset throw vars
-/datum/action/ability/activable/xeno/lunge/proc/clean_lunge_target()
+/// Null lunge target and reset related vars.
+/datum/action/ability/activable/xeno/warrior/lunge/proc/clean_lunge_target()
SIGNAL_HANDLER
UnregisterSignal(lunge_target, COMSIG_QDELETING)
- UnregisterSignal(owner, COMSIG_MOVABLE_POST_THROW)
+ UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_POST_THROW))
lunge_target = null
- UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
- owner.remove_filter("warrior_lunge")
owner.stop_throw()
+ owner.remove_filter("warrior_lunge")
-///Do the grab on the target, and clean all previous vars
-/datum/action/ability/activable/xeno/lunge/proc/lunge_grab(mob/living/carbon/xenomorph/warrior/X, atom/A)
+/// Do the grab on the target, and clean all previous vars
+/datum/action/ability/activable/xeno/warrior/lunge/proc/lunge_grab(atom/A)
clean_lunge_target()
- X.swap_hand()
- X.start_pulling(A, lunge = TRUE)
- X.swap_hand()
+ var/mob/living/carbon/xenomorph/warrior/warrior_owner = owner
+ warrior_owner.swap_hand()
+ warrior_owner.start_pulling(A, lunge = TRUE)
+ warrior_owner.swap_hand()
+ var/datum/action/ability/xeno_action/empower/empower_action = warrior_owner.actions_by_path[/datum/action/ability/xeno_action/empower]
+ if(empower_action?.combo_count < WARRIOR_EMPOWER_COMBO_THRESHOLD)
+ empower_action?.activate_empower()
+
+/datum/action/ability/activable/xeno/warrior/lunge/ai_should_start_consider()
+ return TRUE
+
+/datum/action/ability/activable/xeno/warrior/lunge/ai_should_use(atom/target)
+ if(!iscarbon(target))
+ return FALSE
+ if(!line_of_sight(owner, target, 2))
+ return FALSE
+ if(!can_use_ability(target, override_flags = ABILITY_IGNORE_SELECTED_ABILITY))
+ return FALSE
+ if(target.get_xeno_hivenumber() == owner.get_xeno_hivenumber())
+ return FALSE
+ return TRUE
+
// ***************************************
// *********** Fling
// ***************************************
-/datum/action/ability/activable/xeno/fling
+#define WARRIOR_FLING_TOSS_COOLDOWN 20 SECONDS
+#define WARRIOR_FLING_DISTANCE 4 // in tiles
+#define WARRIOR_FLING_EMPOWER_MULTIPLIER 2
+
+/datum/action/ability/activable/xeno/warrior/fling
name = "Fling"
action_icon_state = "fling"
- desc = "Knock a target flying up to 5 tiles away."
- ability_cost = 18
- cooldown_duration = 20 SECONDS //Shared cooldown with Grapple Toss
+ ability_cost = 20
+ cooldown_duration = WARRIOR_FLING_TOSS_COOLDOWN
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_FLING,
)
target_flags = ABILITY_MOB_TARGET
-/datum/action/ability/activable/xeno/fling/on_cooldown_finish()
- to_chat(owner, span_xenodanger("We gather enough strength to fling something again."))
- owner.playsound_local(owner, 'sound/effects/xeno_newlarva.ogg', 25, 0, 1)
- return ..()
+/datum/action/ability/activable/xeno/warrior/fling/New(Target)
+ . = ..()
+ desc = "Send a target flying up to [WARRIOR_FLING_DISTANCE] tiles away. Distance reduced for bigger targets. Usable on allies."
-/datum/action/ability/activable/xeno/fling/can_use_ability(atom/A, silent = FALSE, override_flags)
+/datum/action/ability/activable/xeno/warrior/fling/can_use_ability(atom/A, silent = FALSE, override_flags)
. = ..()
if(!.)
return FALSE
if(!A)
return FALSE
-
- if(!owner.Adjacent(A))
- return FALSE
-
if(!isliving(A))
+ if(!silent)
+ owner.balloon_alert(owner, "Invalid target")
return FALSE
- var/mob/living/L = A
- if(L.stat == DEAD)
+ var/mob/living/living_target = A
+ if(living_target.stat == DEAD && !living_target.issamexenohive(owner))
+ if(!silent)
+ owner.balloon_alert(owner, "Dead")
+ return FALSE
+ if(!living_target.Adjacent(owner))
+ if(!silent)
+ owner.balloon_alert(owner, "Not adjacent")
return FALSE
-/datum/action/ability/activable/xeno/fling/use_ability(atom/A)
- var/mob/living/carbon/xenomorph/X = owner
- var/mob/living/victim = A
- var/facing = get_dir(X, victim)
- var/fling_distance = 4
-
- X.face_atom(victim) //Face towards the victim
-
+/datum/action/ability/activable/xeno/warrior/fling/use_ability(atom/A)
+ . = ..()
GLOB.round_statistics.warrior_flings++
SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_flings")
-
- X.visible_message(span_xenowarning("\The [X] effortlessly flings [victim] away!"), \
- span_xenowarning("We effortlessly fling [victim] away!"))
- playsound(victim,'sound/weapons/alien_claw_block.ogg', 75, 1)
-
- if(victim.mob_size >= MOB_SIZE_BIG) //Penalize fling distance for big creatures
- fling_distance = FLOOR(fling_distance * 0.5, 1)
-
- var/turf/T = X.loc
- var/turf/temp = X.loc
- var/empowered = X.empower() //Should it knockdown everyone down its path ?
-
- for (var/x in 1 to fling_distance)
- temp = get_step(T, facing)
- if (!temp)
- break
- if(empowered)
- for(var/mob/living/carbon/human/human in temp)
- human.KnockdownNoChain(2 SECONDS) //Bowling pins
- to_chat(human, span_highdanger("[victim] crashes into us!"))
- T = temp
-
- X.do_attack_animation(victim, ATTACK_EFFECT_DISARM2)
- victim.throw_at(T, fling_distance, 1, X, 1)
-
- shake_camera(victim, 2, 1)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ var/mob/living/living_target = A
+ xeno_owner.face_atom(living_target)
+ playsound(living_target, 'sound/weapons/alien_claw_block.ogg', 75, 1)
+ shake_camera(living_target, 1, 1)
+ xeno_owner.do_attack_animation(living_target, ATTACK_EFFECT_DISARM2)
+ var/fling_distance = WARRIOR_FLING_DISTANCE
+ if(living_target.mob_size >= MOB_SIZE_BIG) // Penalize fling distance for big creatures.
+ fling_distance--
+ var/datum/action/ability/xeno_action/empower/empower_action = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/empower]
+ if(empower_action?.check_empower(living_target))
+ fling_distance *= WARRIOR_FLING_EMPOWER_MULTIPLIER
+ if(!living_target.issamexenohive(xeno_owner))
+ RegisterSignal(living_target, COMSIG_MOVABLE_IMPACT, PROC_REF(thrown_into))
+ RegisterSignal(living_target, COMSIG_MOVABLE_POST_THROW, PROC_REF(throw_ended))
+ if(!(living_target.pass_flags & PASS_XENO))
+ living_target.pass_flags |= PASS_XENO
+ var/fling_direction = get_dir(xeno_owner, living_target)
+ living_target.throw_at(get_ranged_target_turf(xeno_owner, fling_direction ? fling_direction : xeno_owner.dir, fling_distance), fling_distance, 1, xeno_owner, TRUE)
succeed_activate()
add_cooldown()
+ var/datum/action/ability/activable/xeno/warrior/grapple_toss/toss_action = xeno_owner.actions_by_path[/datum/action/ability/activable/xeno/warrior/grapple_toss]
+ toss_action?.add_cooldown()
- var/datum/action/ability/xeno_action/toss = X.actions_by_path[/datum/action/ability/activable/xeno/toss]
- if(toss)
- toss.add_cooldown()
-
- if(isxeno(victim))
- var/mob/living/carbon/xenomorph/x_victim = victim
- if(X.issamexenohive(x_victim)) //We don't fuck up friendlies
- return
-
- victim.ParalyzeNoChain(0.5 SECONDS)
-
-
-/datum/action/ability/activable/xeno/fling/ai_should_start_consider()
+/datum/action/ability/activable/xeno/warrior/fling/ai_should_start_consider()
return TRUE
-/datum/action/ability/activable/xeno/fling/ai_should_use(atom/target)
+/datum/action/ability/activable/xeno/warrior/fling/ai_should_use(atom/target)
if(!iscarbon(target))
return FALSE
if(get_dist(target, owner) > 1)
@@ -266,330 +376,356 @@
return FALSE
return TRUE
+
// ***************************************
// *********** Grapple Toss
// ***************************************
-/datum/action/ability/activable/xeno/toss
+#define WARRIOR_GRAPPLE_TOSS_DISTANCE 4 // in tiles
+#define WARRIOR_GRAPPLE_TOSS_STAGGER 3 SECONDS
+#define WARRIOR_GRAPPLE_TOSS_SLOWDOWN 3
+#define WARRIOR_GRAPPLE_TOSS_EMPOWER_MULTIPLIER 2
+#define WARRIOR_GRAPPLE_TOSS_THROW_PARALYZE 0.5 SECONDS
+
+/datum/action/ability/activable/xeno/warrior/grapple_toss
name = "Grapple Toss"
action_icon_state = "grapple_toss"
- desc = "Throw a creature you're grappling up to 3 tiles away."
- ability_cost = 18
- cooldown_duration = 20 SECONDS //Shared cooldown with Fling
+ ability_cost = 20
+ cooldown_duration = WARRIOR_FLING_TOSS_COOLDOWN
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_GRAPPLE_TOSS,
)
target_flags = ABILITY_TURF_TARGET
-/datum/action/ability/activable/xeno/toss/on_cooldown_finish()
- to_chat(owner, span_xenodanger("We gather enough strength to toss something again."))
- owner.playsound_local(owner, 'sound/effects/xeno_newlarva.ogg', 25, 0, 1)
+/datum/action/ability/activable/xeno/warrior/grapple_toss/New(Target)
+ . = ..()
+ desc = "Throw a creature under our grasp up to [WARRIOR_GRAPPLE_TOSS_DISTANCE] tiles away. Distance reduced on larger targets. Usable on allies."
+
+/datum/action/ability/activable/xeno/warrior/grapple_toss/on_cooldown_finish()
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ var/datum/action/ability/activable/xeno/warrior/fling/fling_action = xeno_owner.actions_by_path[/datum/action/ability/activable/xeno/warrior/fling]
+ xeno_owner.balloon_alert(xeno_owner, "[fling_action ? "[initial(fling_action.name)] / " : ""][initial(name)] ready")
return ..()
-/datum/action/ability/activable/xeno/toss/can_use_ability(atom/A, silent = FALSE, override_flags)
+/datum/action/ability/activable/xeno/warrior/grapple_toss/can_use_ability(atom/A, silent = FALSE, override_flags)
. = ..()
if(!.)
- return
-
- if(!owner.pulling) //If we're not grappling something, we should be flinging something living and adjacent
+ return FALSE
+ if(!owner.pulling)
if(!silent)
- to_chat(owner, span_xenodanger("We have nothing to toss!"))
+ owner.balloon_alert(owner, "Nothing to toss")
+ return FALSE
+ if(!owner.Adjacent(owner.pulling))
+ if(!silent)
+ owner.balloon_alert(owner, "Target not adjacent")
return FALSE
-/datum/action/ability/activable/xeno/toss/use_ability(atom/A)
- var/mob/living/carbon/xenomorph/X = owner
- var/atom/movable/target = owner.pulling
- var/fling_distance = 4
- var/stagger_slow_stacks = 3
- var/stun_duration = 0.5 SECONDS
- var/big_mob_message
-
- X.face_atom(A)
- GLOB.round_statistics.warrior_flings++ //I'm going to consider this a fling for the purpose of statistics
- SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_flings")
-
- playsound(target,'sound/weapons/alien_claw_block.ogg', 75, 1)
-
- if(isliving(target))
- var/mob/living/victim = target
-
- if(victim.mob_size >= MOB_SIZE_BIG) //Penalize fling distance for big creatures
- fling_distance = FLOOR(fling_distance * 0.5, 1)
- big_mob_message = ", struggling mightily to heft its bulk"
-
- if(isxeno(victim))
- var/mob/living/carbon/xenomorph/x_victim = victim
- if(X.issamexenohive(x_victim)) //We don't fuck up friendlies
- stagger_slow_stacks = 0
- stun_duration = 0
-
- victim.adjust_stagger(stagger_slow_stacks SECONDS)
- victim.add_slowdown(stagger_slow_stacks)
- victim.adjust_blurriness(stagger_slow_stacks) //Cosmetic eye blur SFX
- victim.ParalyzeNoChain(stun_duration)
- shake_camera(victim, 2, 1)
-
- if(X.empower())
- fling_distance *= 2
- target.forceMove(get_turf(X)) //First force them into our space so we can toss them behind us without problems
- X.do_attack_animation(target, ATTACK_EFFECT_DISARM2)
- target.throw_at(get_turf(A), fling_distance, 1, X, 1)
- X.visible_message(span_xenowarning("\The [X] throws [target] away[big_mob_message]!"), \
- span_xenowarning("We throw [target] away[big_mob_message]!"))
-
+/datum/action/ability/activable/xeno/warrior/grapple_toss/use_ability(atom/A)
+ . = ..()
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ var/atom/movable/atom_target = xeno_owner.pulling
+ var/fling_distance = WARRIOR_GRAPPLE_TOSS_DISTANCE
+ var/datum/action/ability/xeno_action/empower/empower_action = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/empower]
+ if(empower_action?.check_empower(atom_target))
+ fling_distance *= WARRIOR_GRAPPLE_TOSS_EMPOWER_MULTIPLIER
+ if(isliving(atom_target))
+ var/mob/living/living_target = atom_target
+ if(living_target.mob_size >= MOB_SIZE_BIG)
+ fling_distance--
+ if(!living_target.issamexenohive(xeno_owner))
+ if(!(living_target.pass_flags & PASS_XENO))
+ living_target.pass_flags |= PASS_XENO
+ shake_camera(living_target, 1, 1)
+ living_target.adjust_stagger(WARRIOR_GRAPPLE_TOSS_STAGGER)
+ living_target.add_slowdown(WARRIOR_GRAPPLE_TOSS_SLOWDOWN)
+ living_target.adjust_blurriness(WARRIOR_GRAPPLE_TOSS_SLOWDOWN)
+ living_target.Paralyze(WARRIOR_GRAPPLE_TOSS_THROW_PARALYZE) // very important otherwise the guy can move right as you throw them
+ RegisterSignal(living_target, COMSIG_MOVABLE_IMPACT, PROC_REF(thrown_into))
+ RegisterSignal(living_target, COMSIG_MOVABLE_POST_THROW, PROC_REF(throw_ended))
+ xeno_owner.face_atom(atom_target)
+ atom_target.forceMove(get_turf(xeno_owner))
+ xeno_owner.do_attack_animation(atom_target, ATTACK_EFFECT_DISARM2)
+ playsound(atom_target, 'sound/weapons/alien_claw_block.ogg', 75, 1)
+ atom_target.throw_at(get_turf(A), fling_distance, 1, xeno_owner, TRUE)
succeed_activate()
add_cooldown()
+ var/datum/action/ability/activable/xeno/warrior/fling/fling_action = xeno_owner.actions_by_path[/datum/action/ability/activable/xeno/warrior/fling]
+ fling_action?.add_cooldown()
- var/datum/action/ability/xeno_action/fling = X.actions_by_path[/datum/action/ability/activable/xeno/fling]
- if(fling)
- fling.add_cooldown()
// ***************************************
// *********** Punch
// ***************************************
-/datum/action/ability/activable/xeno/punch
+#define WARRIOR_PUNCH_SLOWDOWN 3
+#define WARRIOR_PUNCH_STAGGER 3
+#define WARRIOR_PUNCH_EMPOWER_MULTIPLIER 1.5
+#define WARRIOR_PUNCH_GRAPPLED_DAMAGE_MULTIPLIER 1.5
+#define WARRIOR_PUNCH_GRAPPLED_DEBUFF_MULTIPLIER 1.5
+#define WARRIOR_PUNCH_GRAPPLED_PARALYZE 0.5 SECONDS
+#define WARRIOR_PUNCH_KNOCKBACK_DISTANCE 1 // in tiles
+#define WARRIOR_PUNCH_KNOCKBACK_SPEED 1
+
+/datum/action/ability/activable/xeno/warrior/punch
name = "Punch"
action_icon_state = "punch"
desc = "Strike a target, inflicting stamina damage, stagger and slowdown. Deals double damage, stagger and slowdown to grappled targets. Deals quadruple damage to structures and machinery."
- ability_cost = 12
+ ability_cost = 15
cooldown_duration = 10 SECONDS
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_PUNCH,
)
target_flags = ABILITY_MOB_TARGET
- ///The punch range, 1 would be adjacent.
- var/range = 1
-/datum/action/ability/activable/xeno/punch/on_cooldown_finish()
- var/mob/living/carbon/xenomorph/X = owner
- to_chat(X, span_xenodanger("We gather enough strength to punch again."))
- owner.playsound_local(owner, 'sound/effects/xeno_newlarva.ogg', 25, 0, 1)
+/datum/action/ability/activable/xeno/warrior/punch/on_cooldown_finish()
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ xeno_owner.balloon_alert(xeno_owner, "[initial(name)] ready")
return ..()
-/datum/action/ability/activable/xeno/punch/can_use_ability(atom/A, silent = FALSE, override_flags)
+/datum/action/ability/activable/xeno/warrior/punch/can_use_ability(atom/A, silent = FALSE, override_flags)
. = ..()
if(!.)
return
-
- if(A.resistance_flags & (INDESTRUCTIBLE|CRUSHER_IMMUNE)) //no bolting down indestructible airlocks
- if(!silent)
- to_chat(owner, span_xenodanger("We cannot damage this target!"))
- return FALSE
-
- if(isxeno(A))
+ if(!isliving(A) && !isstructure(A) && !ismachinery(A) && !isvehicle(A))
if(!silent)
- to_chat(owner, span_xenodanger("We can't harm our sister!"))
+ owner.balloon_alert(owner, "Cannot punch")
return FALSE
-
- if(!isliving(A) && !isstructure(A) && !ismachinery(A) && !isvehicle(A))
+ if(A.resistance_flags & (INDESTRUCTIBLE|CRUSHER_IMMUNE))
if(!silent)
- to_chat(owner, span_xenodanger("We can't punch this target!"))
+ owner.balloon_alert(owner, "Cannot damage")
return FALSE
-
if(isliving(A))
- var/mob/living/L = A
- if(L.stat == DEAD)
+ var/mob/living/living_target = A
+ if(living_target.issamexenohive(owner))
if(!silent)
- to_chat(owner, span_xenodanger("We don't care about the dead!"))
+ owner.balloon_alert(owner, "Cannot punch")
return FALSE
-
- if(!line_of_sight(owner, A, range))
+ if(living_target.stat == DEAD)
+ if(!silent)
+ owner.balloon_alert(owner, "Dead")
+ return FALSE
+ if(!A.Adjacent(owner))
if(!silent)
- to_chat(owner, span_xenodanger("Our target must be closer!"))
+ owner.balloon_alert(owner, "Not adjacent")
return FALSE
-
-/datum/action/ability/activable/xeno/punch/use_ability(atom/A)
- var/mob/living/carbon/xenomorph/X = owner
- var/damage = X.xeno_caste.melee_damage * X.xeno_melee_damage_modifier
- var/target_zone = check_zone(X.zone_selected)
-
- if(X.empower())
- damage *= 1.5
- if(!A.punch_act(X, damage, target_zone))
- return fail_activate()
-
+/datum/action/ability/activable/xeno/warrior/punch/use_ability(atom/A)
+ . = ..()
GLOB.round_statistics.warrior_punches++
SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_punches")
-
+ do_ability(A)
+
+/// Does the ability. Exists because Punch is the parent of another ability, so this lets us separate functionality and avoid repeating a few lines of code.
+/datum/action/ability/activable/xeno/warrior/punch/proc/do_ability(atom/A)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ var/punch_damage = xeno_owner.xeno_caste.melee_damage * xeno_owner.xeno_melee_damage_modifier
+ var/datum/action/ability/xeno_action/empower/empower_action = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/empower]
+ if(empower_action?.check_empower(A))
+ punch_damage *= WARRIOR_PUNCH_EMPOWER_MULTIPLIER
+ if(!A.punch_act(xeno_owner, punch_damage))
+ return fail_activate()
succeed_activate()
add_cooldown()
-/atom/proc/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/datum/action/ability/activable/xeno/warrior/punch/ai_should_start_consider()
return TRUE
-/obj/machinery/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3) //Break open the machine
- X.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
- X.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
- if(!CHECK_BITFIELD(resistance_flags, UNACIDABLE) || resistance_flags == (UNACIDABLE|XENO_DAMAGEABLE)) //If it's acidable or we can't acid it but it has the xeno damagable flag, we can damage it
- attack_generic(X, damage * 4, BRUTE, "", FALSE) //Deals 4 times regular damage to machines
- X.visible_message(span_xenodanger("\The [X] smashes [src] with a [punch_description] punch!"), \
- span_xenodanger("We smash [src] with a [punch_description] punch!"), visible_message_flags = COMBAT_MESSAGE)
- playsound(src, pick('sound/effects/bang.ogg','sound/effects/metal_crash.ogg','sound/effects/meteorimpact.ogg'), 50, 1)
- Shake(duration = 0.5 SECONDS)
+/datum/action/ability/activable/xeno/warrior/punch/ai_should_use(atom/target)
+ if(!iscarbon(target))
+ return FALSE
+ if(get_dist(target, owner) > 1)
+ return FALSE
+ if(!can_use_ability(target, override_flags = ABILITY_IGNORE_SELECTED_ABILITY))
+ return FALSE
+ if(target.get_xeno_hivenumber() == owner.get_xeno_hivenumber())
+ return FALSE
+ return TRUE
- if(!CHECK_BITFIELD(machine_stat, PANEL_OPEN))
- ENABLE_BITFIELD(machine_stat, PANEL_OPEN)
+/// Handles anything that should happen when the Warrior's punch hits any atom.
+/atom/proc/punch_act(mob/living/carbon/xenomorph/xeno, punch_damage, push = TRUE)
+ return TRUE
- if(wires) //If it has wires, break em
+/obj/machinery/punch_act(mob/living/carbon/xenomorph/xeno, punch_damage, ...)
+ xeno.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
+ xeno.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
+ if(!(resistance_flags & UNACIDABLE) || resistance_flags & XENO_DAMAGEABLE) // If it's acidable or we can't acid it but it has the xeno damagable flag, we can damage it
+ attack_generic(xeno, punch_damage * 4, BRUTE, effects = FALSE)
+ playsound(src, pick('sound/effects/bang.ogg','sound/effects/metal_crash.ogg','sound/effects/meteorimpact.ogg'), 50, 1)
+ Shake(duration = 0.5 SECONDS)
+ if(!(machine_stat & PANEL_OPEN))
+ machine_stat |= PANEL_OPEN
+ if(wires)
var/allcut = wires.is_all_cut()
- if(!allcut) //Considered prohibiting this vs airlocks, but tbh, I can see clever warriors using this to keep airlocks bolted open or closed as is most advantageous
+ if(!allcut)
wires.cut_all()
- visible_message(span_danger("\The [src]'s wires snap apart in a rain of sparks!"), null, null, 5)
-
update_appearance()
return TRUE
-/obj/machinery/computer/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3) //Break open the machine
- set_disabled() //Currently only computers use this; falcon punch away its density
+/obj/machinery/computer/punch_act(...)
+ set_disabled() // Currently only computers use this; falcon punch away its density.
return ..()
-/obj/machinery/light/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/obj/machinery/light/punch_act(mob/living/carbon/xenomorph/xeno, ...)
. = ..()
- attack_alien(X) //Smash it
+ attack_alien(xeno)
-/obj/machinery/camera/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/obj/machinery/camera/punch_act(...)
. = ..()
- var/datum/effect_system/spark_spread/sparks = new //Avoid the slash text, go direct to sparks
+ var/datum/effect_system/spark_spread/sparks = new
sparks.set_up(2, 0, src)
sparks.attach(src)
sparks.start()
-
deactivate()
- visible_message(span_danger("\The [src]'s wires snap apart in a rain of sparks!")) //Smash it
-/obj/machinery/power/apc/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/obj/machinery/power/apc/punch_act(...)
. = ..()
- beenhit += 4 //Break it open instantly
+ beenhit += 4 // Break it open instantly.
update_appearance()
-/obj/machinery/vending/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/obj/machinery/vending/punch_act(...)
. = ..()
- if(tipped_level < 2) //Knock it down if it isn't
- X.visible_message(span_danger("\The [X] knocks \the [src] down!"), \
- span_danger("You knock \the [src] down!"), null, 5)
+ if(tipped_level < 2)
tip_over()
-/obj/structure/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3) //Smash structures
+/obj/structure/punch_act(mob/living/carbon/xenomorph/xeno, punch_damage, ...)
. = ..()
- X.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
- X.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
- attack_alien(X, damage * 4, BRUTE, "", FALSE) //Deals 4 times regular damage to structures
- X.visible_message(span_xenodanger("\The [X] smashes [src] with a devastating punch!"), \
- span_xenodanger("We smash [src] with a devastating punch!"), visible_message_flags = COMBAT_MESSAGE)
+ xeno.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
+ xeno.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
+ attack_alien(xeno, punch_damage * 4, BRUTE, effects = FALSE)
playsound(src, pick('sound/effects/bang.ogg','sound/effects/metal_crash.ogg','sound/effects/meteorimpact.ogg'), 50, 1)
Shake(duration = 0.5 SECONDS)
-/obj/vehicle/punch_act(mob/living/carbon/xenomorph/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/obj/vehicle/punch_act(mob/living/carbon/xenomorph/xeno, punch_damage, ...)
. = ..()
- X.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
- X.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
- attack_generic(X, damage * 4, BRUTE, "", FALSE) //Deals 4 times regular damage to vehicles
- X.visible_message(span_xenodanger("\The [X] smashes [src] with a devastating punch!"), \
- span_xenodanger("We smash [src] with a devastating punch!"), visible_message_flags = COMBAT_MESSAGE)
+ xeno.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
+ xeno.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
+ attack_generic(xeno, punch_damage * 4, BRUTE, effects = FALSE)
playsound(src, pick('sound/effects/bang.ogg','sound/effects/metal_crash.ogg','sound/effects/meteorimpact.ogg'), 50, 1)
Shake(duration = 0.5 SECONDS)
return TRUE
-/mob/living/punch_act(mob/living/carbon/xenomorph/warrior/X, damage, target_zone, push = TRUE, punch_description = "powerful", stagger_stacks = 3, slowdown_stacks = 3)
+/mob/living/punch_act(mob/living/carbon/xenomorph/warrior/xeno, punch_damage, push = TRUE)
. = ..()
- if(pulledby == X) //If we're being grappled by the Warrior punching us, it's gonna do extra damage and debuffs; combolicious
- damage *= 1.5
- slowdown_stacks *= 2
- stagger_stacks *= 2
- ParalyzeNoChain(0.5 SECONDS)
- X.stop_pulling()
- punch_description = "devastating"
-
- if(iscarbon(src))
- var/mob/living/carbon/carbon_victim = src
- var/datum/limb/L = carbon_victim.get_limb(target_zone)
-
- if (!L || (L.limb_status & LIMB_DESTROYED))
- L = carbon_victim.get_limb(BODY_ZONE_CHEST)
- apply_damage(damage, BRUTE, L, MELEE)
- else
- apply_damage(damage, BRUTE, blocked = MELEE)
-
- if(push)
- var/facing = get_dir(X, src)
-
- if(loc == X.loc) //If they're sharing our location we still want to punch them away
- facing = X.dir
-
- var/turf/T = X.loc
- var/turf/temp = X.loc
-
- for (var/x in 1 to 2)
- temp = get_step(T, facing)
- if (!temp)
- break
- T = temp
-
- throw_at(T, 2, 1, X, 1) //Punch em away
-
- var/target_location_feedback = get_living_limb_descriptive_name(target_zone)
- X.visible_message(span_xenodanger("\The [X] hits [src] in the [target_location_feedback] with a [punch_description] punch!"), \
- span_xenodanger("We hit [src] in the [target_location_feedback] with a [punch_description] punch!"), visible_message_flags = COMBAT_MESSAGE)
- playsound(src, pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg'), 50, 1)
- X.face_atom(src) //Face the target so you don't look like an idiot
- X.do_attack_animation(src, ATTACK_EFFECT_YELLOWPUNCH)
- X.do_attack_animation(src, ATTACK_EFFECT_DISARM2)
-
- adjust_stagger(stagger_stacks SECONDS)
+ var/slowdown_stacks = WARRIOR_PUNCH_SLOWDOWN
+ var/stagger_stacks = WARRIOR_PUNCH_STAGGER
+ var/visual_effect = /obj/effect/temp_visual/warrior/punch/weak
+ var/sound_effect = 'sound/weapons/punch1.ogg'
+ if(pulledby == xeno)
+ xeno.stop_pulling()
+ punch_damage *= WARRIOR_PUNCH_GRAPPLED_DAMAGE_MULTIPLIER
+ slowdown_stacks *= WARRIOR_PUNCH_GRAPPLED_DEBUFF_MULTIPLIER
+ stagger_stacks *= WARRIOR_PUNCH_GRAPPLED_DEBUFF_MULTIPLIER
+ visual_effect = /obj/effect/temp_visual/warrior/punch/strong
+ sound_effect = 'sound/weapons/punch2.ogg'
+ Paralyze(WARRIOR_PUNCH_GRAPPLED_PARALYZE)
+ Shake(duration = 0.5 SECONDS)
+ var/datum/limb/target_limb
+ if(!iscarbon(src))
+ var/mob/living/carbon/carbon_target = src
+ target_limb = carbon_target.get_limb(xeno.zone_selected)
+ if(!target_limb || (target_limb.limb_status & LIMB_DESTROYED))
+ target_limb = carbon_target.get_limb(BODY_ZONE_CHEST)
+ xeno.face_atom(src)
+ xeno.do_attack_animation(src)
+ new visual_effect(get_turf(src))
+ playsound(src, sound_effect, 50, 1)
+ shake_camera(src, 1, 1)
add_slowdown(slowdown_stacks)
- adjust_blurriness(slowdown_stacks) //Cosmetic eye blur SFX
+ adjust_stagger(stagger_stacks SECONDS)
+ adjust_blurriness(slowdown_stacks)
+ apply_damage(punch_damage, BRUTE, target_limb ? target_limb : 0, MELEE)
+ apply_damage(punch_damage, STAMINA, updating_health = TRUE)
+ var/turf_behind = get_step(src, REVERSE_DIR(get_dir(src, xeno)))
+ if(!push)
+ return
+ if(LinkBlocked(get_turf(src), turf_behind))
+ do_attack_animation(turf_behind)
+ return
+ knockback(xeno, WARRIOR_PUNCH_KNOCKBACK_DISTANCE, WARRIOR_PUNCH_KNOCKBACK_SPEED)
- apply_damage(damage, STAMINA, updating_health = TRUE) //Armor penetrating stamina also applies.
- shake_camera(src, 2, 1)
- Shake(duration = 0.5 SECONDS)
+/obj/effect/temp_visual/warrior/punch
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "weak_punch"
+ duration = 2.5
+ layer = ABOVE_ALL_MOB_LAYER
-/datum/action/ability/activable/xeno/punch/ai_should_start_consider()
- return TRUE
+/obj/effect/temp_visual/warrior/punch/weak/Initialize(mapload)
+ . = ..()
+ animate(src, time = duration + 1, alpha = 0)
+
+/obj/effect/temp_visual/warrior/punch/strong
+ icon = 'icons/effects/64x64.dmi'
+ icon_state = "strong_punch"
+ duration = 3
+ pixel_x = -16
+ pixel_y = -16
-/datum/action/ability/activable/xeno/punch/ai_should_use(atom/target)
- if(!iscarbon(target))
- return FALSE
- if(get_dist(target, owner) > 1)
- return FALSE
- if(!can_use_ability(target, override_flags = ABILITY_IGNORE_SELECTED_ABILITY))
- return FALSE
- if(target.get_xeno_hivenumber() == owner.get_xeno_hivenumber())
- return FALSE
- return TRUE
// ***************************************
-// *********** Jab
+// *********** Flurry
// ***************************************
-/datum/action/ability/activable/xeno/punch/jab
- name = "Jab"
- action_icon_state = "jab"
- desc = "Precisely strike your target from further away, heavily slowing them."
+#define WARRIOR_JAB_DAMAGE_MULTIPLIER 0.25
+#define WARRIOR_JAB_BLIND 3
+#define WARRIOR_JAB_BLUR 6
+#define WARRIOR_JAB_CONFUSION_DURATION 3 SECONDS
+
+/datum/action/ability/activable/xeno/warrior/punch/flurry
+ name = "Flurry"
+ action_icon_state = "flurry"
+ desc = "Strike at your target with blinding speed."
ability_cost = 10
- range = 2
+ cooldown_duration = 7 SECONDS
keybinding_signals = list(
KEYBINDING_NORMAL = COMSIG_XENOABILITY_JAB,
)
+ /// The amount of charges we currently have. Initial value is assumed to be the maximum.
+ var/current_charges = 3
+
+/datum/action/ability/activable/xeno/warrior/punch/flurry/give_action(mob/living/L)
+ . = ..()
+ var/mutable_appearance/counter_maptext = mutable_appearance(icon = null, icon_state = null, layer = ACTION_LAYER_MAPTEXT)
+ counter_maptext.pixel_x = 16
+ counter_maptext.pixel_y = -4
+ counter_maptext.maptext = MAPTEXT("[current_charges]/[initial(current_charges)]")
+ visual_references[VREF_MUTABLE_JAB] = counter_maptext
+
+/datum/action/ability/activable/xeno/warrior/punch/flurry/remove_action(mob/living/carbon/xenomorph/X)
+ . = ..()
+ button.cut_overlay(visual_references[VREF_MUTABLE_JAB])
+ visual_references[VREF_MUTABLE_JAB] = null
+
+/datum/action/ability/activable/xeno/warrior/punch/flurry/update_button_icon()
+ button.cut_overlay(visual_references[VREF_MUTABLE_JAB])
+ var/mutable_appearance/number = visual_references[VREF_MUTABLE_JAB]
+ number?.maptext = MAPTEXT("[current_charges]/[initial(current_charges)]")
+ visual_references[VREF_MUTABLE_JAB] = number
+ button.add_overlay(visual_references[VREF_MUTABLE_JAB])
+ return ..()
+
+/datum/action/ability/activable/xeno/warrior/punch/flurry/on_cooldown_finish()
+ current_charges = clamp(current_charges+1, 0, initial(current_charges))
+ owner.balloon_alert(owner, "[initial(name)] ready[current_charges > 1 ? " ([current_charges]/[initial(current_charges)])" : ""]")
+ update_button_icon()
+ if(current_charges < initial(current_charges))
+ cooldown_timer = addtimer(CALLBACK(src, PROC_REF(on_cooldown_finish)), cooldown_duration, TIMER_STOPPABLE)
+ return
+ return ..()
-/datum/action/ability/activable/xeno/punch/jab/use_ability(atom/A)
- var/mob/living/carbon/xenomorph/X = owner
- var/mob/living/carbon/human/target = A
- var/target_zone = check_zone(X.zone_selected)
- var/damage = X.xeno_caste.melee_damage * X.xeno_melee_damage_modifier
+/datum/action/ability/activable/xeno/warrior/punch/flurry/can_use_action(silent, override_flags)
+ . = ..()
+ if(cooldown_timer && current_charges > 0)
+ return TRUE
- if(!target.punch_act(X, damage, target_zone, push = FALSE, punch_description = "precise", stagger_stacks = 3, slowdown_stacks = 6))
+/datum/action/ability/activable/xeno/warrior/punch/flurry/do_ability(atom/A)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ var/mob/living/living_target = A
+ var/jab_damage = round((xeno_owner.xeno_caste.melee_damage * xeno_owner.xeno_melee_damage_modifier) * WARRIOR_JAB_DAMAGE_MULTIPLIER)
+ if(!living_target.punch_act(xeno_owner, jab_damage, FALSE))
return fail_activate()
- if(X.empower() && ishuman(target))
- target.blind_eyes(3)
- target.blur_eyes(6)
- to_chat(target, span_highdanger("The concussion from the [X]'s blow blinds us!"))
- target.apply_status_effect(STATUS_EFFECT_CONFUSED, 3 SECONDS)
- GLOB.round_statistics.warrior_punches++
- SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_punches")
+ current_charges--
succeed_activate()
add_cooldown()
-
-/datum/action/ability/activable/xeno/punch/jab/on_cooldown_finish()
- var/mob/living/carbon/xenomorph/X = owner
- to_chat(X, span_xenodanger("We gather enough strength to jab again."))
- owner.playsound_local(owner, 'sound/effects/xeno_newlarva.ogg', 25, 0, 1)
- return ..()
+ var/datum/action/ability/xeno_action/empower/empower_action = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/empower]
+ if(!empower_action?.check_empower(living_target))
+ return
+ living_target.adjust_blindness(WARRIOR_JAB_BLIND)
+ living_target.adjust_blurriness(WARRIOR_JAB_BLUR)
+ living_target.apply_status_effect(STATUS_EFFECT_CONFUSED, WARRIOR_JAB_CONFUSION_DURATION)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/warrior/castedatum_warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/warrior/castedatum_warrior.dm
index e7d4e497d628d..140d9a40005d9 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/warrior/castedatum_warrior.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/warrior/castedatum_warrior.dm
@@ -9,14 +9,14 @@
wound_type = "warrior" //used to match appropriate wound overlays
// *** Melee Attacks *** //
- melee_damage = 23
+ melee_damage = 20
// *** Speed *** //
speed = -0.5
// *** Plasma *** //
- plasma_max = 120
- plasma_gain = 12
+ plasma_max = 100
+ plasma_gain = 10
// *** Health *** //
max_health = 350
@@ -38,19 +38,15 @@
// *** Minimap Icon *** //
minimap_icon = "warrior"
- // *** Warrior Abilities *** //
- agility_speed_increase = -0.6
- agility_speed_armor = -30
-
actions = list(
/datum/action/ability/xeno_action/xeno_resting,
/datum/action/ability/xeno_action/watch_xeno,
/datum/action/ability/activable/xeno/psydrain,
/datum/action/ability/xeno_action/toggle_agility,
- /datum/action/ability/activable/xeno/lunge,
- /datum/action/ability/activable/xeno/fling,
- /datum/action/ability/activable/xeno/toss,
- /datum/action/ability/activable/xeno/punch,
+ /datum/action/ability/activable/xeno/warrior/lunge,
+ /datum/action/ability/activable/xeno/warrior/fling,
+ /datum/action/ability/activable/xeno/warrior/grapple_toss,
+ /datum/action/ability/activable/xeno/warrior/punch,
)
/datum/xeno_caste/warrior/normal
@@ -66,10 +62,11 @@
/datum/action/ability/xeno_action/xeno_resting,
/datum/action/ability/xeno_action/watch_xeno,
/datum/action/ability/activable/xeno/psydrain,
+ /datum/action/ability/xeno_action/empower,
/datum/action/ability/xeno_action/toggle_agility,
- /datum/action/ability/activable/xeno/lunge,
- /datum/action/ability/activable/xeno/fling,
- /datum/action/ability/activable/xeno/toss,
- /datum/action/ability/activable/xeno/punch,
- /datum/action/ability/activable/xeno/punch/jab,
+ /datum/action/ability/activable/xeno/warrior/lunge,
+ /datum/action/ability/activable/xeno/warrior/fling,
+ /datum/action/ability/activable/xeno/warrior/grapple_toss,
+ /datum/action/ability/activable/xeno/warrior/punch,
+ /datum/action/ability/activable/xeno/warrior/punch/flurry,
)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/warrior/warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/warrior/warrior.dm
index 67871b940c1ef..f0b570655fe09 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/warrior/warrior.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/warrior/warrior.dm
@@ -13,112 +13,58 @@
tier = XENO_TIER_TWO
upgrade = XENO_UPGRADE_NORMAL
bubble_icon = "alienroyal"
- ///How many stacks of combo do we have ? Interacts with every ability.
- var/combo = 0
- ///Abilities with empowered interactions
- var/list/empowerable_actions = list(
- /datum/action/ability/activable/xeno/fling,
- /datum/action/ability/activable/xeno/toss,
- /datum/action/ability/activable/xeno/punch,
- /datum/action/ability/activable/xeno/punch/jab,
- )
+
+
// ***************************************
// *********** Icons
// ***************************************
/mob/living/carbon/xenomorph/warrior/handle_special_state()
- if(agility)
+ var/datum/action/ability/xeno_action/toggle_agility/agility_action = actions_by_path[/datum/action/ability/xeno_action/toggle_agility]
+ if(agility_action?.ability_active)
icon_state = "[xeno_caste.caste_name][is_a_rouny ? " rouny" : ""] Agility"
return TRUE
return FALSE
/mob/living/carbon/xenomorph/warrior/handle_special_wound_states(severity)
. = ..()
- if(agility)
+ var/datum/action/ability/xeno_action/toggle_agility/agility_action = actions_by_path[/datum/action/ability/xeno_action/toggle_agility]
+ if(agility_action?.ability_active)
return "wounded_agility_[severity]"
+
// ***************************************
// *********** Mob overrides
// ***************************************
/mob/living/carbon/xenomorph/warrior/stop_pulling()
if(isliving(pulling) && !isxeno(pulling))
- var/mob/living/L = pulling
+ var/mob/living/living_target = pulling
grab_resist_level = 0 //zero it out
- L.SetStun(0)
- UnregisterSignal(L, COMSIG_LIVING_DO_RESIST)
+ living_target.SetStun(0)
+ UnregisterSignal(living_target, COMSIG_LIVING_DO_RESIST)
..()
/mob/living/carbon/xenomorph/warrior/start_pulling(atom/movable/AM, force = move_force, suppress_message = TRUE, lunge = FALSE)
if(!check_state())
return FALSE
-
- if(agility)
- balloon_alert(src, "Cannot in agility mode")
- return FALSE
-
- var/mob/living/L = AM
-
- if(isxeno(L))
- var/mob/living/carbon/xenomorph/X = L
- if(issamexenohive(X)) //No neckgrabbing of allies.
- return ..()
-
+ var/mob/living/living_target = AM
+ if(isxeno(living_target) && issamexenohive(living_target))
+ return ..()
if(lunge && ..())
- return neck_grab(L)
-
- . = ..(L, force, suppress_message)
+ return neck_grab(living_target)
+ . = ..(living_target, force, suppress_message)
-/mob/living/carbon/xenomorph/warrior/proc/neck_grab(mob/living/L)
+/// Puts the target on an upgraded grab and handles related effects.
+/mob/living/carbon/xenomorph/warrior/proc/neck_grab(mob/living/living_target)
GLOB.round_statistics.warrior_grabs++
SSblackbox.record_feedback("tally", "round_statistics", 1, "warrior_grabs")
setGrabState(GRAB_NECK)
- ENABLE_BITFIELD(L.restrained_flags, RESTRAINED_NECKGRAB)
- RegisterSignal(L, COMSIG_LIVING_DO_RESIST, TYPE_PROC_REF(/atom/movable, resisted_against))
- L.drop_all_held_items()
- L.Paralyze(0.1 SECONDS)
- visible_message(span_xenowarning("\The [src] grabs [L] by the throat!"), \
- span_xenowarning("We grab [L] by the throat!"))
+ living_target.resistance_flags |= RESTRAINED_NECKGRAB
+ RegisterSignal(living_target, COMSIG_LIVING_DO_RESIST, TYPE_PROC_REF(/atom/movable, resisted_against))
+ living_target.drop_all_held_items()
+ living_target.Paralyze(0.1 SECONDS)
+ living_target.balloon_alert(src, "Grabbed [living_target]")
return TRUE
-
/mob/living/carbon/xenomorph/warrior/resisted_against(datum/source)
- var/mob/living/victim = source
- victim.do_resist_grab()
-
-// ***************************************
-// *********** Primordial procs
-// ***************************************
-///Handles primordial warrior empowered abilities, returns TRUE if the ability should be empowered.
-/mob/living/carbon/xenomorph/warrior/empower(empowerable = TRUE)
- . = ..()
- if(!empowerable) //gives combo but doesn't combo but doesn't consume it.
- give_combo()
- return FALSE
- if(upgrade != XENO_UPGRADE_PRIMO)
- return FALSE
- if(combo >= WARRIOR_COMBO_THRESHOLD) //Fully stacked, clear all the stacks and return TRUE.
- emote("roar")
- clear_combo()
- return TRUE
- give_combo()
- return FALSE
-
-///Primordial warriors empowered ability trigger when they get 3 combo stacks, handles visuals aswell.
-/mob/living/carbon/xenomorph/warrior/proc/give_combo()
- if(upgrade != XENO_UPGRADE_PRIMO)
- return FALSE
- combo++
- if(combo >= WARRIOR_COMBO_THRESHOLD)
- for(var/datum/action/ability/xeno_action/A AS in actions)
- if(A.type in empowerable_actions)
- A.add_empowered_frame()
- A.update_button_icon()
- addtimer(CALLBACK(src, PROC_REF(clear_combo)), WARRIOR_COMBO_FADEOUT_TIME, TIMER_OVERRIDE|TIMER_UNIQUE)
- return TRUE
-
-///Removes all combo stacks from the warrior, removes the frame around the ability buttons.
-/mob/living/carbon/xenomorph/warrior/proc/clear_combo()
- for(var/datum/action/ability/xeno_action/A AS in actions)
- if(A.type in empowerable_actions)
- A.remove_empowered_frame()
- A.update_button_icon()
- combo = 0
+ var/mob/living/living_target = source
+ living_target.do_resist_grab()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/widow/abilities_widow.dm b/code/modules/mob/living/carbon/xenomorph/castes/widow/abilities_widow.dm
index a92059971a9a6..b9ed1be8f37d0 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/widow/abilities_widow.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/widow/abilities_widow.dm
@@ -134,6 +134,7 @@
KEYBINDING_NORMAL = COMSIG_XENOABILITY_CREATE_SPIDERLING,
KEYBINDING_ALTERNATE = COMSIG_XENOABILITY_CREATE_SPIDERLING_USING_CC,
)
+ use_state_flags = ABILITY_USE_LYING
/// List of all our spiderlings
var/list/mob/living/carbon/xenomorph/spiderling/spiderlings = list()
diff --git a/code/modules/mob/living/carbon/xenomorph/charge_crush.dm b/code/modules/mob/living/carbon/xenomorph/charge_crush.dm
index fbfdd98deb660..b15b12e7ca05f 100644
--- a/code/modules/mob/living/carbon/xenomorph/charge_crush.dm
+++ b/code/modules/mob/living/carbon/xenomorph/charge_crush.dm
@@ -423,6 +423,9 @@
/obj/vehicle/sealed/mecha/pre_crush_act(mob/living/carbon/xenomorph/charger, datum/action/ability/xeno_action/ready_charge/charge_datum)
return (CHARGE_SPEED(charge_datum) * 375)
+/obj/hitbox/pre_crush_act(mob/living/carbon/xenomorph/charger, datum/action/ability/xeno_action/ready_charge/charge_datum)
+ return (CHARGE_SPEED(charge_datum) * 20)
+
/obj/structure/razorwire/pre_crush_act(mob/living/carbon/xenomorph/charger, datum/action/ability/xeno_action/ready_charge/charge_datum)
if(CHECK_BITFIELD(resistance_flags, INDESTRUCTIBLE) || charger.is_charging < CHARGE_ON)
charge_datum.do_stop_momentum()
diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm
index 690a9e52cb920..1dbf92caba003 100644
--- a/code/modules/mob/living/carbon/xenomorph/death.dm
+++ b/code/modules/mob/living/carbon/xenomorph/death.dm
@@ -4,10 +4,7 @@
/mob/living/carbon/xenomorph/death(gibbing, deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw.", silent)
- if(stat == DEAD)
- return ..()
- return ..() //Just a different standard deathmessage
-
+ return ..() //we're just changing the death message
/mob/living/carbon/xenomorph/on_death()
GLOB.alive_xeno_list -= src
@@ -39,6 +36,7 @@
if(hud_used.alien_plasma_display)
hud_used.alien_plasma_display.icon_state = "power_display_empty"
update_icons()
+ hud_set_plasma()
death_cry()
diff --git a/code/modules/mob/living/carbon/xenomorph/embryo.dm b/code/modules/mob/living/carbon/xenomorph/embryo.dm
index 3979faccf6e2c..0987107e49734 100644
--- a/code/modules/mob/living/carbon/xenomorph/embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/embryo.dm
@@ -184,9 +184,9 @@
victim.update_burst()
- if(istype(victim.loc, /obj/vehicle/multitile/root))
- var/obj/vehicle/multitile/root/V = victim.loc
- V.handle_player_exit(src)
+ if(istype(victim.loc, /obj/vehicle/sealed))
+ var/obj/vehicle/sealed/armored/veh = victim.loc
+ forceMove(veh.exit_location(src))
else
forceMove(get_turf(victim)) //moved to the turf directly so we don't get stuck inside a cryopod or another mob container.
playsound(src, pick('sound/voice/alien_chestburst.ogg','sound/voice/alien_chestburst2.ogg'), 25)
diff --git a/code/modules/mob/living/carbon/xenomorph/hive_datum.dm b/code/modules/mob/living/carbon/xenomorph/hive_datum.dm
index da9ca1deae4d5..1e030b8684ff5 100644
--- a/code/modules/mob/living/carbon/xenomorph/hive_datum.dm
+++ b/code/modules/mob/living/carbon/xenomorph/hive_datum.dm
@@ -990,6 +990,11 @@ to_chat will check for valid clients itself already so no need to double check f
if(QDELETED(chosen_silo) || isnull(xeno_candidate))
return FALSE
+ var/datum/job/xeno_job = SSjob.GetJobType(/datum/job/xenomorph)
+ if((xeno_job.total_positions - xeno_job.current_positions) <= 0)
+ to_chat(xeno_candidate.mob, span_warning("There are no longer burrowed larvas available."))
+ return FALSE
+
xeno_candidate.mob.reset_perspective(null)
return do_spawn_larva(xeno_candidate, chosen_silo.loc, larva_already_reserved)
diff --git a/code/modules/mob/living/carbon/xenomorph/life.dm b/code/modules/mob/living/carbon/xenomorph/life.dm
index 191688ba27574..934c84e0fa5e9 100644
--- a/code/modules/mob/living/carbon/xenomorph/life.dm
+++ b/code/modules/mob/living/carbon/xenomorph/life.dm
@@ -113,25 +113,23 @@
/mob/living/carbon/xenomorph/proc/handle_living_plasma_updates()
var/turf/T = loc
- if(!T || !istype(T))
+ if(!istype(T)) //This means plasma doesn't update while you're in things like a vent, but since you don't have weeds in a vent or can actually take advantage of pheros, this is fine
return
- if(plasma_stored >= xeno_caste.plasma_max * xeno_caste.plasma_regen_limit)
+
+ if(!current_aura && (plasma_stored >= xeno_caste.plasma_max * xeno_caste.plasma_regen_limit)) //no loss or gain
return
if(current_aura)
if(plasma_stored < pheromone_cost)
- use_plasma(plasma_stored)
+ use_plasma(plasma_stored, FALSE)
QDEL_NULL(current_aura)
src.balloon_alert(src, "Stop emitting, no plasma")
else
- use_plasma(pheromone_cost)
-
- if(HAS_TRAIT(src, TRAIT_NOPLASMAREGEN))
- hud_set_plasma()
- return
+ use_plasma(pheromone_cost, FALSE)
- if(!loc_weeds_type && !(xeno_caste.caste_flags & CASTE_INNATE_PLASMA_REGEN))
- hud_set_plasma() // since we used some plasma via the aura
+ if(HAS_TRAIT(src, TRAIT_NOPLASMAREGEN) || !loc_weeds_type && !(xeno_caste.caste_flags & CASTE_INNATE_PLASMA_REGEN))
+ if(current_aura) //we only need to update if we actually used plasma from pheros
+ hud_set_plasma()
return
var/plasma_gain = xeno_caste.plasma_gain
@@ -146,7 +144,6 @@
SEND_SIGNAL(src, COMSIG_XENOMORPH_PLASMA_REGEN, plasma_mod)
gain_plasma(plasma_mod[1])
- hud_set_plasma() //update plasma amount on the plasma mob_hud
/mob/living/carbon/xenomorph/can_receive_aura(aura_type, atom/source, datum/aura_bearer/bearer)
. = ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/update_icons.dm b/code/modules/mob/living/carbon/xenomorph/update_icons.dm
index fbc9abde4d5cf..b01a4ede927e2 100644
--- a/code/modules/mob/living/carbon/xenomorph/update_icons.dm
+++ b/code/modules/mob/living/carbon/xenomorph/update_icons.dm
@@ -40,7 +40,6 @@
update_fire() //the fire overlay depends on the xeno's stance, so we must update it.
update_wounds()
- hud_set_plasma()
med_hud_set_health()
hud_set_sunder()
hud_set_firestacks()
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
index e9a36893cf011..06fafb0c9239a 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
@@ -92,6 +92,8 @@
var/sunder_recover = 0.5
///What is the max amount of sunder that can be applied to a xeno (100 = 100%)
var/sunder_max = 100
+ ///Multiplier on the weapons sunder, e.g 10 sunder on a projectile is reduced to 5 with a 0.5 multiplier.
+ var/sunder_multiplier = 1
// *** Ranged Attack *** //
///Delay timer for spitting
@@ -122,12 +124,6 @@
// *** Defiler Abilities *** //
var/list/available_reagents_define = list() //reagents available for select reagent
- // *** Warrior Abilities *** //
- ///speed increase afforded to the warrior caste when in 'agiility' mode. negative number means faster movement.
- var/agility_speed_increase = 0 // this opens up possibilities for balancing
- ///amount of soft armor adjusted when in agility mode for the warrior caste. Flat integer amounts only.
- var/agility_speed_armor = 0 //Same as above
-
// *** Boiler Abilities *** //
///maximum number of 'globs' of boiler ammunition that can be stored by the boiler caste.
var/max_ammo = 0
@@ -231,6 +227,8 @@
var/evolve_min_xenos = 0
// How many of this caste may be alive at once
var/maximum_active_caste = INFINITY
+ // Accuracy malus, 0 by default. Should NOT go over 70.
+ var/accuracy_malus = 0
///Add needed component to the xeno
/datum/xeno_caste/proc/on_caste_applied(mob/xenomorph)
diff --git a/code/modules/mob/living/carbon/xenomorph/xenoattacks.dm b/code/modules/mob/living/carbon/xenomorph/xenoattacks.dm
index ed7536748edb0..f763e985a0608 100644
--- a/code/modules/mob/living/carbon/xenomorph/xenoattacks.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xenoattacks.dm
@@ -94,9 +94,6 @@
ExtinguishMob()
return TRUE
- if(interaction_emote(src))
- return TRUE
-
X.visible_message(span_notice("\The [X] caresses \the [src] with its scythe-like arm."), \
span_notice("We caress \the [src] with our scythe-like arm."), null, 5)
diff --git a/code/modules/mob/living/carbon/xenomorph/xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/xenomorph.dm
index 99198510d0593..c9a85f94381ef 100644
--- a/code/modules/mob/living/carbon/xenomorph/xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xenomorph.dm
@@ -109,7 +109,7 @@
maxHealth = xeno_caste.max_health * GLOB.xeno_stat_multiplicator_buff
if(restore_health_and_plasma)
// xenos that manage plasma through special means shouldn't gain it for free on aging
- plasma_stored = max(plasma_stored, xeno_caste.plasma_max * xeno_caste.plasma_regen_limit)
+ set_plasma(max(plasma_stored, xeno_caste.plasma_max * xeno_caste.plasma_regen_limit))
health = maxHealth
setXenoCasteSpeed(xeno_caste.speed)
@@ -488,7 +488,7 @@ Returns TRUE when loc_weeds_type changes. Returns FALSE when it doesn’t change
else
COOLDOWN_START(src, xeno_unresting_cooldown, XENO_UNRESTING_COOLDOWN)
-/mob/living/carbon/xenomorph/set_jump_component(duration = 0.5 SECONDS, cooldown = 2 SECONDS, cost = 0, height = 16, sound = null, flags = JUMP_SHADOW, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE)
+/mob/living/carbon/xenomorph/set_jump_component(duration = 0.5 SECONDS, cooldown = 2 SECONDS, cost = 0, height = 16, sound = null, flags = JUMP_SHADOW, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE|PASS_TANK)
var/gravity = get_gravity()
if(gravity < 1) //low grav
duration *= 2.5 - gravity
diff --git a/code/modules/mob/living/carbon/xenomorph/xenoprocs.dm b/code/modules/mob/living/carbon/xenomorph/xenoprocs.dm
index 3c87fe655b22b..f09133d34faec 100644
--- a/code/modules/mob/living/carbon/xenomorph/xenoprocs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xenoprocs.dm
@@ -184,16 +184,25 @@
return FALSE
return TRUE
-/mob/living/carbon/xenomorph/proc/use_plasma(value)
+/mob/living/carbon/xenomorph/proc/set_plasma(value, update_plasma = TRUE)
+ plasma_stored = clamp(value, 0, xeno_caste.plasma_max)
+ if(!update_plasma)
+ return
+ hud_set_plasma()
+
+/mob/living/carbon/xenomorph/proc/use_plasma(value, update_plasma = TRUE)
plasma_stored = max(plasma_stored - value, 0)
update_action_button_icons()
+ if(!update_plasma)
+ return
+ hud_set_plasma()
-/mob/living/carbon/xenomorph/proc/gain_plasma(value)
+/mob/living/carbon/xenomorph/proc/gain_plasma(value, update_plasma = TRUE)
plasma_stored = min(plasma_stored + value, xeno_caste.plasma_max)
update_action_button_icons()
-
-
-
+ if(!update_plasma)
+ return
+ hud_set_plasma()
//Strip all inherent xeno verbs from your caste. Used in evolution.
/mob/living/carbon/xenomorph/proc/remove_inherent_verbs()
@@ -391,11 +400,6 @@
take_damage(2 * X.xeno_caste.acid_spray_structure_damage, BURN, ACID)
return FALSE // not normal density flag
-/obj/vehicle/multitile/root/cm_armored/acid_spray_act(mob/living/carbon/xenomorph/X)
- take_damage_type(X.xeno_caste.acid_spray_structure_damage, ACID, src)
- healthcheck()
- return TRUE
-
/mob/living/carbon/acid_spray_act(mob/living/carbon/xenomorph/X)
ExtinguishMob()
if(isnestedhost(src))
@@ -425,7 +429,11 @@
ExtinguishMob()
/obj/flamer_fire/acid_spray_act(mob/living/carbon/xenomorph/X)
- Destroy()
+ qdel(src)
+
+/obj/hitbox/acid_spray_act(mob/living/carbon/xenomorph/X)
+ take_damage(X.xeno_caste.acid_spray_structure_damage, BURN, ACID)
+ return TRUE
// Vent Crawl
/mob/living/carbon/xenomorph/proc/vent_crawl()
@@ -493,7 +501,8 @@
. = ..()
if(.)
return
- sunder = clamp(sunder + adjustment, 0, xeno_caste.sunder_max)
+ sunder = clamp(sunder + (adjustment > 0 ? adjustment * xeno_caste.sunder_multiplier : adjustment), 0, xeno_caste.sunder_max)
+//Applying sunder is an adjustment value above 0, healing sunder is an adjustment value below 0. Use multiplier when taking sunder, not when healing.
/mob/living/carbon/xenomorph/set_sunder(new_sunder)
. = ..()
@@ -542,10 +551,6 @@
SIGNAL_HANDLER
tracked = null
-///Handles empowered abilities, should return TRUE if the ability should be empowered. Empowerable should be FALSE if the ability cannot itself be empowered but has interactions with empowerable abilities
-/mob/living/carbon/xenomorph/proc/empower(empowerable = TRUE)
- return FALSE
-
///Handles icon updates when leadered/unleadered. Evolution.dm also uses this
/mob/living/carbon/xenomorph/proc/update_leader_icon(makeleader = TRUE)
// Xenos with specialized icons (Queen, King, Shrike) do not get their icon changed
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index cae6c56863720..c27c6e603c3be 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -98,6 +98,7 @@
set_armor_datum()
AddElement(/datum/element/gesture)
AddElement(/datum/element/keybinding_update)
+ AddElement(/datum/element/directional_attack)
/mob/living/Destroy()
for(var/datum/status_effect/effect AS in status_effects)
@@ -874,7 +875,7 @@ below 100 is not dizzy
get_up()
///Sets up the jump component for the mob. Proc args can be altered so different mobs have different 'default' jump settings
-/mob/living/proc/set_jump_component(duration = 0.5 SECONDS, cooldown = 1 SECONDS, cost = 8, height = 16, sound = null, flags = JUMP_SHADOW, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE)
+/mob/living/proc/set_jump_component(duration = 0.5 SECONDS, cooldown = 1 SECONDS, cost = 8, height = 16, sound = null, flags = JUMP_SHADOW, flags_pass = PASS_LOW_STRUCTURE|PASS_FIRE|PASS_TANK)
var/gravity = get_gravity()
if(gravity < 1) //low grav
duration *= 2.5 - gravity
diff --git a/code/modules/mob/living/living_health_procs.dm b/code/modules/mob/living/living_health_procs.dm
index 91933f4be450c..ab61f61ee6e60 100644
--- a/code/modules/mob/living/living_health_procs.dm
+++ b/code/modules/mob/living/living_health_procs.dm
@@ -374,7 +374,7 @@
/mob/living/carbon/xenomorph/revive(admin_revive = FALSE)
- plasma_stored = xeno_caste.plasma_max
+ set_plasma(xeno_caste.plasma_max)
sunder = 0
if(stat == DEAD)
hive?.on_xeno_revive(src)
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index db7e8e950f486..a06eeb891e206 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -114,7 +114,7 @@
log_game("[key_name(src)] made a vocal announcement with the following message: [message].")
log_talk(message, LOG_SAY, tag="VOX Announcement")
- to_chat(src, span_notice("The following vocal announcement has been made: [message]."))
+ minor_announce(capitalize(message), "[name] announces:", should_play_sound = FALSE)
for(var/word in words) //play vox sounds to the rest of our zlevel
play_vox_word(word, src.z, null)
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 29cdd5f7463c8..6f794ba9efb54 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -124,6 +124,9 @@
if("manifest")
view_manifest()
+ if("xenomanifest")
+ view_xeno_manifest()
+
if("lore")
view_lore()
@@ -233,6 +236,14 @@
popup.set_content(dat)
popup.open(FALSE)
+/// Proc for lobby button "View Hive" to see current leader/queen status.
+/mob/new_player/proc/view_xeno_manifest()
+ var/dat = GLOB.datacore.get_xeno_manifest()
+
+ var/datum/browser/popup = new(src, "xenomanifest", "Xeno Manifest
", 400, 420)
+ popup.set_content(dat)
+ popup.open(FALSE)
+
/mob/new_player/proc/view_lore()
var/output = ""
output += "
TerraGov Marine CorpsXenomorph HiveSons of Mars"
diff --git a/code/modules/projectiles/ammo_datums.dm b/code/modules/projectiles/ammo_datums.dm
index 2c7f5e7db0534..f4d39682eba5b 100644
--- a/code/modules/projectiles/ammo_datums.dm
+++ b/code/modules/projectiles/ammo_datums.dm
@@ -945,6 +945,22 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
/datum/ammo/bullet/shotgun/buckshot/on_hit_mob(mob/M,obj/projectile/P)
staggerstun(M, P, weaken = 2 SECONDS, stagger = 2 SECONDS, knockback = 2, slowdown = 0.5, max_range = 3)
+/datum/ammo/bullet/hefa_buckshot
+ name = "hefa fragment"
+ handful_icon_state = "shotgun buckshot shell"
+ icon_state = "buckshot"
+ hud_state = "shotgun_buckshot"
+ accuracy_var_low = 9
+ accuracy_var_high = 9
+ accurate_range = 3
+ max_range = 10
+ shrapnel_chance = 15
+ damage = 30
+ damage_falloff = 3
+
+/datum/ammo/bullet/hefa_buckshot/on_hit_mob(mob/mob_hit, obj/projectile/projectile)
+ staggerstun(mob_hit, projectile, knockback = 2, max_range = 4)
+
/datum/ammo/bullet/shotgun/spread
name = "additional buckshot"
icon_state = "buckshot"
@@ -1324,6 +1340,17 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
accurate_range = 25
accurate_range_min = 3
+/datum/ammo/bullet/cupola
+ name = "cupola bullet"
+ bullet_color = COLOR_SOFT_RED //Red bullets to indicate friendly fire restriction
+ hud_state = "smartgun"
+ hud_state_empty = "smartgun_empty"
+ flags_ammo_behavior = AMMO_BALLISTIC|AMMO_SUNDERING|AMMO_IFF
+ accurate_range = 12
+ damage = 25
+ penetration = 5
+ sundering = 1
+
/datum/ammo/bullet/spottingrifle
name = "smart spotting bullet"
bullet_color = COLOR_SOFT_RED //Red bullets to indicate friendly fire restriction
@@ -1463,7 +1490,7 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
sundering = 1
max_range = 35
///Bonus flat damage to walls, balanced around resin walls.
- var/autocannon_wall_bonus = 20
+ var/autocannon_wall_bonus = 50
/datum/ammo/bullet/auto_cannon/on_hit_turf(turf/T, obj/projectile/P)
P.proj_max_range -= 20
@@ -1488,7 +1515,7 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
sundering = 5
max_range = 30
airburst_multiplier = 1
- autocannon_wall_bonus = 5
+ autocannon_wall_bonus = 25
/datum/ammo/bullet/auto_cannon/flak/on_hit_mob(mob/victim, obj/projectile/proj)
airburst(victim, proj)
@@ -2150,11 +2177,12 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
flags_ammo_behavior = AMMO_EXPLOSIVE|AMMO_ROCKET
accurate_range = 15
max_range = 40
- penetration = 200
- damage = 300
+ penetration = 50
+ damage = 200
+ hud_state = "bigshell_he"
/datum/ammo/rocket/ltb/drop_nade(turf/T)
- explosion(T, 0, 4, 6, 0, 7)
+ explosion(T, 1, 2, 5, 0, 5)
/datum/ammo/rocket/mech
name = "large high-explosive rocket"
@@ -3447,9 +3475,9 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
glow_color = "#CB0166"
/datum/ammo/energy/xeno/psy_blast/psy_lance/on_hit_obj(obj/O, obj/projectile/P)
- if(ismecha(O))
- var/obj/vehicle/sealed/mecha/mech_victim = O
- mech_victim.take_damage(200, BURN, ENERGY, TRUE, armour_penetration = penetration)
+ if(isvehicle(O))
+ var/obj/vehicle/veh_victim = O
+ veh_victim.take_damage(200, BURN, ENERGY, TRUE, armour_penetration = penetration)
/datum/ammo/energy/xeno/psy_blast/psy_lance/on_hit_mob(mob/M, obj/projectile/P)
if(isxeno(M))
@@ -3762,6 +3790,9 @@ GLOBAL_LIST_INIT(no_sticky_resin, typecacheof(list(/obj/item/clothing/mask/faceh
/datum/ammo/xeno/sticky/on_hit_obj(obj/O, obj/projectile/P)
+ if(isarmoredvehicle(O))
+ var/obj/vehicle/sealed/armored/tank = O
+ COOLDOWN_START(tank, cooldown_vehicle_move, tank.move_delay)
var/turf/T = get_turf(O)
drop_resin(T.density ? P.loc : T)
diff --git a/code/modules/projectiles/gun_system.dm b/code/modules/projectiles/gun_system.dm
index 478b504f450c7..d0adb7ed07f2b 100644
--- a/code/modules/projectiles/gun_system.dm
+++ b/code/modules/projectiles/gun_system.dm
@@ -260,7 +260,7 @@
///Slowdown for wielding
var/aim_slowdown = 0
///How long between wielding and firing in tenths of seconds
- var/wield_delay = 0.4 SECONDS
+ var/wield_delay = 0.6 SECONDS
///Extra wield delay for untrained operators
var/wield_penalty = 0.2 SECONDS
///Storing value for above
@@ -677,10 +677,10 @@
wdelay += wield_penalty
else
var/skill_value = user.skills.getRating(gun_skill_category)
+ if(skill_value < 0)
+ wdelay += wield_penalty
if(skill_value > 0)
wdelay -= skill_value * 2
- else
- wdelay += wield_penalty
wield_time = world.time + wdelay
do_wield(user, wdelay)
if(HAS_TRAIT(src, TRAIT_GUN_AUTO_AIM_MODE))
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index f322f479e4129..c0142ebe95db2 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -93,7 +93,7 @@
flags_gun_features = GUN_CAN_POINTBLANK|GUN_ENERGY|GUN_AMMO_COUNTER|GUN_AMMO_COUNT_BY_SHOTS_REMAINING|GUN_NO_PITCH_SHIFT_NEAR_EMPTY|GUN_SHOWS_AMMO_REMAINING
reciever_flags = AMMO_RECIEVER_MAGAZINES|AMMO_RECIEVER_AUTO_EJECT|AMMO_RECIEVER_DO_NOT_EJECT_HANDFULS|AMMO_RECIEVER_CYCLE_ONLY_BEFORE_FIRE
aim_slowdown = 0.75
- wield_delay = 1 SECONDS
+ wield_delay = 1.2 SECONDS
gun_skill_category = SKILL_RIFLES
muzzle_flash_color = COLOR_LASER_RED
@@ -466,7 +466,7 @@
attachable_offset = list("muzzle_x" = 40, "muzzle_y" = 17,"rail_x" = 22, "rail_y" = 21, "under_x" = 29, "under_y" = 10, "stock_x" = 22, "stock_y" = 12)
aim_slowdown = 0.4
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
scatter = 0
scatter_unwielded = 10
fire_delay = 0.2 SECONDS
@@ -565,7 +565,7 @@
attachable_offset = list("muzzle_x" = 23, "muzzle_y" = 22,"rail_x" = 12, "rail_y" = 22, "under_x" = 16, "under_y" = 14, "stock_x" = 22, "stock_y" = 12)
akimbo_additional_delay = 0.9
- wield_delay = 0.2 SECONDS
+ wield_delay = 0.4 SECONDS
scatter = 2
scatter_unwielded = 4
fire_delay = 0.15 SECONDS
@@ -653,7 +653,7 @@
attachable_offset = list("muzzle_x" = 32, "muzzle_y" = 18,"rail_x" = 17, "rail_y" = 21, "under_x" = 23, "under_y" = 10, "stock_x" = 22, "stock_y" = 12)
aim_slowdown = 0.2
- wield_delay = 0.3 SECONDS
+ wield_delay = 0.5 SECONDS
scatter = 1
scatter_unwielded = 10
fire_delay = 1.5 SECONDS
@@ -788,7 +788,7 @@
starting_attachment_types = list(/obj/item/attachable/scope/unremovable/laser_sniper_scope)
aim_slowdown = 0.7
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
scatter = -4
scatter_unwielded = 10
fire_delay = 0.8 SECONDS
@@ -887,7 +887,7 @@
attachable_offset = list("muzzle_x" = 41, "muzzle_y" = 15,"rail_x" = 22, "rail_y" = 24, "under_x" = 30, "under_y" = 8, "stock_x" = 22, "stock_y" = 12)
aim_slowdown = 0.7
- wield_delay = 0.8 SECONDS
+ wield_delay = 1 SECONDS
scatter = 1
fire_delay = 0.2 SECONDS
burst_delay = 0.25 SECONDS
@@ -992,7 +992,7 @@
attachable_offset = list("muzzle_x" = 40, "muzzle_y" = 19,"rail_x" = 20, "rail_y" = 21, "under_x" = 30, "under_y" = 13, "stock_x" = 22, "stock_y" = 14)
ammo_level_icon = "tex"
aim_slowdown = 0.4
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
scatter = 0
scatter_unwielded = 10
fire_delay = 0.5 SECONDS
@@ -1053,8 +1053,7 @@
recoil_unwielded = 3
aim_slowdown = 0.35
- wield_delay = 0.4 SECONDS
- wield_penalty = 0.2 SECONDS
+ wield_delay = 0.6 SECONDS
damage_falloff_mult = 0.9
fire_delay = 0.2 SECONDS
@@ -1104,7 +1103,7 @@
recoil_unwielded = 0
movement_acc_penalty_mult = 2
aim_slowdown = 0.1
- wield_delay = 0.2 SECONDS
+ wield_delay = 0.4 SECONDS
/obj/item/weapon/gun/energy/lasgun/lasrifle/volkite/serpenta/custom
name = "\improper VX-12c Serpenta"
@@ -1193,7 +1192,7 @@
accuracy_mult = 1.1
aim_slowdown = 0.65
damage_falloff_mult = 0.4
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
fire_delay = 0.25 SECONDS
/obj/item/weapon/gun/energy/lasgun/lasrifle/volkite/caliver/magharness
@@ -1233,7 +1232,7 @@
reciever_flags = AMMO_RECIEVER_MAGAZINES|AMMO_RECIEVER_DO_NOT_EJECT_HANDFULS|AMMO_RECIEVER_CYCLE_ONLY_BEFORE_FIRE
attachable_offset = list("muzzle_x" = 34, "muzzle_y" = 14,"rail_x" = 11, "rail_y" = 29, "under_x" = 23, "under_y" = 10, "stock_x" = 22, "stock_y" = 12)
aim_slowdown = 1
- wield_delay = 1.2 SECONDS
+ wield_delay = 1.4 SECONDS
fire_delay = 0.15 SECONDS
scatter = 3
accuracy_mult_unwielded = 0.4
diff --git a/code/modules/projectiles/guns/flamer.dm b/code/modules/projectiles/guns/flamer.dm
index 0ba7999819730..14474cae1dd35 100644
--- a/code/modules/projectiles/guns/flamer.dm
+++ b/code/modules/projectiles/guns/flamer.dm
@@ -18,6 +18,7 @@
reload_sound = 'sound/weapons/guns/interact/flamethrower_reload.ogg'
muzzle_flash = null
aim_slowdown = 1.75
+ wield_delay = 0.4 SECONDS
general_codex_key = "flame weapons"
attachable_allowed = list( //give it some flexibility.
/obj/item/attachable/flashlight,
diff --git a/code/modules/projectiles/guns/grenade_launchers.dm b/code/modules/projectiles/guns/grenade_launchers.dm
index 7ce19b0141d4e..d5fb0e8e6dee9 100644
--- a/code/modules/projectiles/guns/grenade_launchers.dm
+++ b/code/modules/projectiles/guns/grenade_launchers.dm
@@ -13,6 +13,7 @@ The Grenade Launchers
throw_speed = 2
throw_range = 10
force = 5
+ wield_delay = 0.4 SECONDS
caliber = CALIBER_40MM //codex
load_method = SINGLE_CASING //codex
icon = 'icons/obj/items/guns/special.dmi'
@@ -62,6 +63,7 @@ The Grenade Launchers
/obj/item/explosive/grenade/chem_grenade/teargas,
/obj/item/explosive/grenade/flashbang/stun,
/obj/item/explosive/grenade/bullet/laser,
+ /obj/item/explosive/grenade/bullet/hefa,
)
reciever_flags = NONE
diff --git a/code/modules/projectiles/guns/mounted.dm b/code/modules/projectiles/guns/mounted.dm
index 79f9376b41d80..59df4c0dd34f6 100644
--- a/code/modules/projectiles/guns/mounted.dm
+++ b/code/modules/projectiles/guns/mounted.dm
@@ -422,7 +422,7 @@
max_shells = 150 //codex
force = 40
aim_slowdown = 1.2
- wield_delay = 2 SECONDS
+ wield_delay = 2.2 SECONDS
fire_sound = 'sound/weapons/guns/fire/tgmc/kinetic/gun_mg27.ogg'
fire_rattle = 'sound/weapons/guns/fire/tgmc/kinetic/gun_mg27_low.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
diff --git a/code/modules/projectiles/guns/pistols.dm b/code/modules/projectiles/guns/pistols.dm
index bcca46ed53d3e..b4d8dc5eebb39 100644
--- a/code/modules/projectiles/guns/pistols.dm
+++ b/code/modules/projectiles/guns/pistols.dm
@@ -18,7 +18,7 @@
w_class = WEIGHT_CLASS_NORMAL
force = 6
movement_acc_penalty_mult = 2
- wield_delay = 0.2 SECONDS //If you modify your pistol to be two-handed, it will still be fast to aim
+ wield_delay = 0.4 SECONDS
type_of_casings = "bullet"
gun_skill_category = SKILL_PISTOLS
attachable_allowed = list(
@@ -108,7 +108,7 @@
flags_gun_features = GUN_CAN_POINTBLANK|GUN_ENERGY|GUN_AMMO_COUNTER|GUN_IS_ATTACHMENT|GUN_SMOKE_PARTICLES
reciever_flags = AMMO_RECIEVER_MAGAZINES|AMMO_RECIEVER_AUTO_EJECT|AMMO_RECIEVER_DO_NOT_EJECT_HANDFULS|AMMO_RECIEVER_CYCLE_ONLY_BEFORE_FIRE
actions_types = list()
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
muzzle_flash_color = COLOR_GREEN
fire_delay = 1.5 SECONDS
@@ -783,7 +783,7 @@ It is a modified Beretta 93R, and can fire three round burst or single fire. Whe
attachable_offset = list("muzzle_x" = 29, "muzzle_y" = 20,"rail_x" = 13, "rail_y" = 23, "under_x" = 19, "under_y" = 13, "stock_x" = 21, "stock_y" = 17)
aim_slowdown = 0.2
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
fire_delay = 0.2 SECONDS
accuracy_mult = 1.2
accuracy_mult_unwielded = 0.85
diff --git a/code/modules/projectiles/guns/plasma.dm b/code/modules/projectiles/guns/plasma.dm
index adaae967bc6b3..18ad79c862776 100644
--- a/code/modules/projectiles/guns/plasma.dm
+++ b/code/modules/projectiles/guns/plasma.dm
@@ -29,7 +29,7 @@
unload_sound = 'sound/weapons/guns/interact/plasma_unload_3.ogg'
reload_sound = 'sound/weapons/guns/interact/plasma_reload_2.ogg'
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
aim_slowdown = 0.5
accuracy_mult = 1.1
@@ -108,9 +108,10 @@
flags_equip_slot = ITEM_SLOT_BACK
flags_gun_features = GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER|GUN_AMMO_COUNT_BY_SHOTS_REMAINING|GUN_WIELDED_FIRING_ONLY
w_class = WEIGHT_CLASS_BULKY
+ gun_skill_category = SKILL_HEAVY_WEAPONS
aim_slowdown = 1.2
- wield_delay = 1.5 SECONDS
+ wield_delay = 1.7 SECONDS
movement_acc_penalty_mult = 6
accuracy_mult = 1
diff --git a/code/modules/projectiles/guns/revolvers.dm b/code/modules/projectiles/guns/revolvers.dm
index 9a5d50112bbc1..4c683c5e3572e 100644
--- a/code/modules/projectiles/guns/revolvers.dm
+++ b/code/modules/projectiles/guns/revolvers.dm
@@ -21,7 +21,7 @@
actions_types = list(/datum/action/item_action/aim_mode)
aim_speed_modifier = 0.75
aim_fire_delay = 0.25 SECONDS
- wield_delay = 0.2 SECONDS //If you modify your revolver to be two-handed, it will still be fast to aim
+ wield_delay = 0.4 SECONDS
gun_skill_category = SKILL_PISTOLS
reciever_flags = AMMO_RECIEVER_HANDFULS|AMMO_RECIEVER_ROTATES_CHAMBER|AMMO_RECIEVER_TOGGLES_OPEN|AMMO_RECIEVER_TOGGLES_OPEN_EJECTS
diff --git a/code/modules/projectiles/guns/rifles.dm b/code/modules/projectiles/guns/rifles.dm
index 2b5845ed165db..fdb3f4fa5ee63 100644
--- a/code/modules/projectiles/guns/rifles.dm
+++ b/code/modules/projectiles/guns/rifles.dm
@@ -10,7 +10,7 @@
flags_gun_features = GUN_CAN_POINTBLANK||GUN_AMMO_COUNTER|GUN_SMOKE_PARTICLES
load_method = MAGAZINE //codex
aim_slowdown = 0.35
- wield_delay = 0.6 SECONDS
+ wield_delay = 0.8 SECONDS
gun_skill_category = SKILL_RIFLES
burst_amount = 3
@@ -173,7 +173,7 @@
extra_delay = 0.05 SECONDS
accuracy_mult = 1.1
scatter = -2
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
burst_amount = 3
aim_slowdown = 0.4
damage_falloff_mult = 0.5
@@ -213,7 +213,7 @@
reload_sound = 'sound/weapons/guns/interact/m41a_reload.ogg'
caliber = CALIBER_10x27_CASELESS //codex
aim_slowdown = 0.75
- wield_delay = 0.8 SECONDS
+ wield_delay = 1 SECONDS
force = 20
max_shells = 20 //codex
default_ammo_type = /obj/item/ammo_magazine/rifle/standard_dmr
@@ -293,7 +293,7 @@
reload_sound = 'sound/weapons/guns/interact/m41a_reload.ogg'
caliber = CALIBER_10x265_CASELESS //codex
aim_slowdown = 0.55
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
force = 20
max_shells = 36 //codex
default_ammo_type = /obj/item/ammo_magazine/rifle/standard_br
@@ -406,7 +406,7 @@
burst_delay = 0.15 SECONDS
accuracy_mult = 1.1
scatter = -1
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
burst_amount = 3
aim_slowdown = 0.4
damage_mult = 1.05 //Has smaller magazines
@@ -479,7 +479,7 @@
unload_sound = 'sound/weapons/guns/interact/m41a_unload.ogg'
reload_sound = 'sound/weapons/guns/interact/m41a_reload.ogg'
aim_slowdown = 0.5
- wield_delay = 1.35 SECONDS
+ wield_delay = 1.55 SECONDS
max_shells = 95 //codex
default_ammo_type = /obj/item/ammo_magazine/rifle/m41a
allowed_ammo_types = list(/obj/item/ammo_magazine/rifle/m41a)
@@ -580,7 +580,7 @@
burst_amount = 1
fire_delay = 0.25 SECONDS
scatter = 0
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
placed_overlay_iconstate = "ak47"
@@ -658,7 +658,7 @@
caliber = CALIBER_762X39 //codex
muzzleflash_iconstate = "muzzle_flash_medium"
max_shells = 100 //codex
- wield_delay = 1.2 SECONDS
+ wield_delay = 1.4 SECONDS
aim_slowdown = 0.95
fire_sound = 'sound/weapons/guns/fire/ak47.ogg'
unload_sound = 'sound/weapons/guns/interact/ak47_unload.ogg'
@@ -749,7 +749,7 @@
accuracy_mult = 1
scatter = 2
recoil = -1
- wield_delay = 0.9 SECONDS
+ wield_delay = 1.1 SECONDS
aim_slowdown = 0.85
movement_acc_penalty_mult = 4
@@ -812,7 +812,7 @@
extra_delay = -0.05 SECONDS
burst_delay = 0.15 SECONDS
accuracy_mult = 1.1
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
damage_mult = 1.2
/obj/item/weapon/gun/rifle/m16/freelancer
@@ -872,7 +872,7 @@
fire_delay = 0.15 SECONDS
burst_delay = 0.15 SECONDS
accuracy_mult = 1.15
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
damage_mult = 1.2
scatter = 1
movement_acc_penalty_mult = 4
@@ -901,7 +901,7 @@
max_shells = 120 //codex
force = 30
aim_slowdown = 0.8
- wield_delay = 1 SECONDS
+ wield_delay = 1.2 SECONDS
fire_sound = 'sound/weapons/guns/fire/tgmc/kinetic/gun_mg42.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/T42_unload.ogg'
@@ -982,7 +982,7 @@
max_shells = 200 //codex
force = 35
aim_slowdown = 1.2
- wield_delay = 1.5 SECONDS
+ wield_delay = 1.7 SECONDS
fire_sound = 'sound/weapons/guns/fire/tgmc/kinetic/gun_mg60.ogg'
fire_rattle = 'sound/weapons/guns/fire/tgmc/kinetic/gun_mg60_low.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
@@ -1050,7 +1050,7 @@
caliber = CALIBER_10X24_CASELESS //codex
max_shells = 200 //codex
aim_slowdown = 0.8
- wield_delay = 2 SECONDS
+ wield_delay = 2.2 SECONDS
fire_sound = 'sound/weapons/guns/fire/hmg.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/m41a_unload.ogg'
@@ -1124,7 +1124,7 @@
default_ammo_type = /obj/item/ammo_magazine/rifle/type71
allowed_ammo_types = list(/obj/item/ammo_magazine/rifle/type71)
aim_slowdown = 0.6
- wield_delay = 0.7 SECONDS
+ wield_delay = 0.9 SECONDS
attachable_allowed = list(
/obj/item/attachable/reddot,
/obj/item/attachable/verticalgrip,
@@ -1203,7 +1203,7 @@
desc = "An much rarer variant of the standard Type 71, this version contains an integrated supressor, a scope, and lots of fine-tuning. Many parts have been replaced, filed down, and improved upon. As a result, this variant is rarely seen outside of elite units."
icon_state = "type71"
item_state = "type71"
- wield_delay = 0 //Ends up being .5 seconds due to scope
+ wield_delay = 0.2 SECONDS
attachable_offset = list("muzzle_x" = 30, "muzzle_y" = 19,"rail_x" = 10, "rail_y" = 22, "under_x" = 21, "under_y" = 18, "stock_x" = 21, "stock_y" = 18)
starting_attachment_types = list(/obj/item/attachable/suppressor/unremovable/invisible, /obj/item/attachable/scope/unremovable)
@@ -1299,7 +1299,7 @@
max_shells = 300 //codex
force = 30
aim_slowdown = 0.95
- wield_delay = 1.3 SECONDS
+ wield_delay = 1.5 SECONDS
fire_sound = "gun_smartgun"
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/T42_unload.ogg'
@@ -1361,7 +1361,7 @@
caliber = CALIBER_10x27_CASELESS //codex
max_shells = 40 //codex
aim_slowdown = 0.85
- wield_delay = 0.65 SECONDS
+ wield_delay = 0.85 SECONDS
fire_sound = 'sound/weapons/guns/fire/t62.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/T42_unload.ogg'
@@ -1452,7 +1452,7 @@
muzzleflash_iconstate = "muzzle_flash_pulse"
default_ammo_type = /obj/item/ammo_magazine/rifle/sectoid_rifle
allowed_ammo_types = list(/obj/item/ammo_magazine/rifle/sectoid_rifle)
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
flags_gun_features = GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER|GUN_ENERGY|GUN_SMOKE_PARTICLES
gun_firemode_list = list(GUN_FIREMODE_SEMIAUTO, GUN_FIREMODE_BURSTFIRE, GUN_FIREMODE_AUTOBURST)
@@ -1550,7 +1550,7 @@
recoil = 0
recoil_unwielded = 4
aim_slowdown = 1
- wield_delay = 1.3 SECONDS
+ wield_delay = 1.5 SECONDS
cock_delay = 0.7 SECONDS
movement_acc_penalty_mult = 6
@@ -1601,7 +1601,7 @@
recoil = 0
recoil_unwielded = 4
aim_slowdown = 1
- wield_delay = 1.3 SECONDS
+ wield_delay = 1.5 SECONDS
movement_acc_penalty_mult = 6
//-------------------------------------------------------
@@ -1614,7 +1614,7 @@
item_state = "tx11"
caliber = CALIBER_492X34_CASELESS //codex
max_shells = 70 //codex
- wield_delay = 0.65 SECONDS
+ wield_delay = 0.85 SECONDS
fire_sound = 'sound/weapons/guns/fire/tgmc/kinetic/gun_ar11.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/T42_unload.ogg'
@@ -1731,7 +1731,7 @@
burst_delay = 0.15 SECONDS
accuracy_mult = 1.2
scatter = -2
- wield_delay = 0.6 SECONDS
+ wield_delay = 0.8 SECONDS
aim_slowdown = 0.5
damage_falloff_mult = 0.5
@@ -1787,7 +1787,7 @@
scatter = 4
burst_amount = 4
aim_slowdown = 0.3
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
damage_falloff_mult = 3
movement_acc_penalty_mult = 4
@@ -1845,7 +1845,7 @@
burst_amount = 1
fire_delay = 0.2 SECONDS
scatter = 1
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
movement_acc_penalty_mult = 4
//-------------------------------------------------------
@@ -1898,7 +1898,7 @@
gun_firemode_list = list(GUN_FIREMODE_SEMIAUTO)
attachable_offset = list("muzzle_x" = 31, "muzzle_y" = 17,"rail_x" = 12, "rail_y" = 20, "under_x" = 28, "under_y" = 13, "stock_x" = -1, "stock_y" = 17)
aim_slowdown = 0.8
- wield_delay = 0.8 SECONDS
+ wield_delay = 1 SECONDS
burst_amount = 1
accuracy_mult = 1.15
scatter = -2
@@ -1942,7 +1942,7 @@
reload_sound = 'sound/weapons/guns/interact/t18_reload.ogg'
caliber = CALIBER_10X24_CASELESS //codex
max_shells = 36 //codex
- wield_delay = 1 SECONDS
+ wield_delay = 1.2 SECONDS
default_ammo_type = /obj/item/ammo_magazine/rifle/standard_carbine
allowed_ammo_types = list(/obj/item/ammo_magazine/rifle/standard_carbine)
attachable_allowed = list(
@@ -2065,7 +2065,7 @@
recoil = 0
recoil_unwielded = 4
aim_slowdown = 0.75
- wield_delay = 1 SECONDS
+ wield_delay = 1.2 SECONDS
movement_acc_penalty_mult = 6
//-------------------------------------------------------
@@ -2116,7 +2116,7 @@
attachable_offset = list("muzzle_x" = 45, "muzzle_y" = 16,"rail_x" = 23, "rail_y" = 24, "under_x" = 33, "under_y" = 11, "stock_x" = -1, "stock_y" = 17)
actions_types = list(/datum/action/item_action/aim_mode)
- wield_delay = 0.6 SECONDS
+ wield_delay = 0.8 SECONDS
aim_fire_delay = 0.1 SECONDS
aim_speed_modifier = 2
@@ -2210,7 +2210,7 @@
accuracy_mult = 0.75
scatter = 12
recoil = 1.5
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
aim_slowdown = 0.4
movement_acc_penalty_mult = 4
damage_falloff_mult = 1.4
@@ -2258,7 +2258,7 @@
caliber = CALIBER_10x26_CASELESS
max_shells = 200
force = 35
- wield_delay = 1.5 SECONDS
+ wield_delay = 1.7 SECONDS
fire_sound = 'sound/weapons/guns/fire/v41.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/T42_unload.ogg'
@@ -2329,7 +2329,7 @@
reload_sound = 'sound/weapons/guns/interact/fal_reload.ogg'
caliber = CALIBER_10x27_CASELESS //codex
aim_slowdown = 0.8
- wield_delay = 0.85 SECONDS
+ wield_delay = 1.05 SECONDS
force = 20
max_shells = 20 //codex
default_ammo_type = /obj/item/ammo_magazine/rifle/icc_sharpshooter
@@ -2443,7 +2443,7 @@
fire_delay = 0.2 SECONDS
scatter = 3
aim_slowdown = 0.35
- wield_delay = 0.35 SECONDS
+ wield_delay = 0.55 SECONDS
damage_falloff_mult = 2.5
movement_acc_penalty_mult = 4
@@ -2505,7 +2505,7 @@
burst_amount = 1
fire_delay = 0.45 SECONDS
aim_slowdown = 0.55
- wield_delay = 0.65 SECONDS
+ wield_delay = 0.85 SECONDS
damage_falloff_mult = 2
movement_acc_penalty_mult = 6.5
@@ -2567,7 +2567,7 @@
accuracy_mult = 1.15
damage_mult = 0.5
aim_slowdown = 0.6
- wield_delay = 0.55 SECONDS
+ wield_delay = 0.75 SECONDS
burst_amount = 1
scatter = 8
movement_acc_penalty_mult = 2
@@ -2616,7 +2616,7 @@
accuracy_mult = 1.15
damage_mult = 1.2
damage_falloff_mult = 1.5
- wield_delay = 0.65 SECONDS
+ wield_delay = 0.85 SECONDS
aim_slowdown = 0.2
scatter = 0
@@ -2647,7 +2647,7 @@
max_shells = 200 //codex
force = 30
aim_slowdown = 0.85
- wield_delay = 0.75 SECONDS
+ wield_delay = 0.95 SECONDS
fire_sound = 'sound/weapons/guns/fire/tgmc/kinetic/gun_mg60.ogg'
dry_fire_sound = 'sound/weapons/guns/fire/m41a_empty.ogg'
unload_sound = 'sound/weapons/guns/interact/T42_unload.ogg'
diff --git a/code/modules/projectiles/guns/shotguns.dm b/code/modules/projectiles/guns/shotguns.dm
index fd9e5a501e3c3..629f2a4973c52 100644
--- a/code/modules/projectiles/guns/shotguns.dm
+++ b/code/modules/projectiles/guns/shotguns.dm
@@ -20,7 +20,7 @@
type_of_casings = "shell"
allowed_ammo_types = list()
aim_slowdown = 0.35
- wield_delay = 0.6 SECONDS //Shotguns are really easy to put up to fire, since they are designed for CQC (at least compared to a rifle)
+ wield_delay = 0.8 SECONDS //Shotguns are really easy to put up to fire, since they are designed for CQC (at least compared to a rifle)
gun_skill_category = SKILL_SHOTGUNS
flags_item_map_variant = NONE
@@ -115,7 +115,7 @@
damage_mult = 0.7 //30% less damage. Faster firerate.
recoil = 2
recoil_unwielded = 4
- wield_delay = 0.8 SECONDS
+ wield_delay = 1 SECONDS
akimbo_additional_delay = 0.9
/obj/item/weapon/gun/shotgun/combat/standardmarine/beginner
@@ -445,7 +445,7 @@
recoil_unwielded = 4
cock_delay = 12
aim_slowdown = 1
- wield_delay = 1.2 SECONDS
+ wield_delay = 1.4 SECONDS
movement_acc_penalty_mult = 4.5
placed_overlay_iconstate = "wood"
@@ -509,7 +509,7 @@
recoil_unwielded = 4
aim_slowdown = 1
- wield_delay = 1 SECONDS
+ wield_delay = 1.2 SECONDS
movement_acc_penalty_mult = 5
placed_overlay_iconstate = "wood"
@@ -544,7 +544,7 @@
recoil = 0
recoil_unwielded = 0
aim_slowdown = 0
- wield_delay = 0.1 SECONDS
+ wield_delay = 0.3 SECONDS
damage_mult = 1
/obj/item/weapon/gun/shotgun/double/derringer/Initialize(mapload)
@@ -791,7 +791,7 @@
fire_delay = 1.75 SECONDS
damage_mult = 0.9
- wield_delay = 0.75 SECONDS
+ wield_delay = 0.95 SECONDS
burst_amount = 2
burst_delay = 0.01 SECONDS //basically instantaneous two shots
extra_delay = 0.5 SECONDS
@@ -843,7 +843,7 @@
recoil = 1
recoil_unwielded = 4
aim_slowdown = 0.35
- wield_delay = 0.65 SECONDS
+ wield_delay = 0.85 SECONDS
/obj/item/weapon/gun/shotgun/som/pointman
starting_attachment_types = list(/obj/item/attachable/bayonet, /obj/item/attachable/motiondetector)
diff --git a/code/modules/projectiles/guns/smgs.dm b/code/modules/projectiles/guns/smgs.dm
index 9c660fd2d9bb1..619f34353d05a 100644
--- a/code/modules/projectiles/guns/smgs.dm
+++ b/code/modules/projectiles/guns/smgs.dm
@@ -13,7 +13,7 @@
load_method = MAGAZINE //codex
force = 8
w_class = WEIGHT_CLASS_BULKY
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
attachable_allowed = list(
/obj/item/attachable/suppressor,
/obj/item/attachable/reddot,
@@ -107,7 +107,7 @@
caliber = CALIBER_10X20_CASELESS //codex
max_shells = 50 //codex
flags_equip_slot = ITEM_SLOT_BACK
- wield_delay = 0.5 SECONDS
+ wield_delay = 0.7 SECONDS
force = 20
type_of_casings = null
default_ammo_type = /obj/item/ammo_magazine/smg/standard_smg
@@ -383,7 +383,7 @@
scatter_unwielded = 6
fire_delay = 0.1 SECONDS
aim_slowdown = 0.3
- wield_delay = 0.3 SECONDS
+ wield_delay = 0.5 SECONDS
/obj/item/weapon/gun/smg/skorpion/mag_harness
starting_attachment_types = list(/obj/item/attachable/foldable/skorpion_stock, /obj/item/attachable/magnetic_harness)
@@ -475,7 +475,7 @@
scatter = 0
scatter_unwielded = 4
aim_slowdown = 0.15
- wield_delay = 0.2 SECONDS
+ wield_delay = 0.4 SECONDS
/obj/item/weapon/gun/smg/uzi/mag_harness
default_ammo_type = /obj/item/ammo_magazine/smg/uzi/extended
@@ -523,7 +523,7 @@
fire_delay = 0.15 SECONDS
aim_slowdown = 0.15
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
accuracy_mult = 1.05
accuracy_mult_unwielded = 0.9
@@ -639,7 +639,7 @@
scatter = 2
scatter_unwielded = 5
aim_slowdown = 0.2
- wield_delay = 0.35 SECONDS
+ wield_delay = 0.55 SECONDS
upper_akimbo_accuracy = 5
lower_akimbo_accuracy = 3
@@ -702,7 +702,7 @@
burst_amount = 1
fire_delay = 0.15 SECONDS
aim_slowdown = 0.25
- wield_delay = 0.4 SECONDS
+ wield_delay = 0.6 SECONDS
accuracy_mult = 1.05
accuracy_mult_unwielded = 0.9
diff --git a/code/modules/projectiles/guns/specialist.dm b/code/modules/projectiles/guns/specialist.dm
index a313385dc4519..cd208aa6ecd01 100644
--- a/code/modules/projectiles/guns/specialist.dm
+++ b/code/modules/projectiles/guns/specialist.dm
@@ -20,7 +20,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
icon = 'icons/obj/items/guns/marksman.dmi'
aim_slowdown = 1
gun_skill_category = SKILL_RIFLES
- wield_delay = 1 SECONDS
+ wield_delay = 1.2 SECONDS
//Pow! Headshot
@@ -47,7 +47,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
/obj/item/ammo_magazine/sniper/flak,
)
force = 12
- wield_delay = 12 //Ends up being 1.6 seconds due to scope
+ wield_delay = 1.4 SECONDS
attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 18,"rail_x" = 12, "rail_y" = 20, "under_x" = 19, "under_y" = 14, "stock_x" = 19, "stock_y" = 14)
var/targetmarker_on = FALSE
var/targetmarker_primed = FALSE
@@ -319,7 +319,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
accuracy_mult = 1
scatter = -5
recoil = -1
- wield_delay = 1.8 SECONDS
+ wield_delay = 2 SECONDS
movement_acc_penalty_mult = 6
@@ -419,7 +419,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
allowed_ammo_types = list(/obj/item/ammo_magazine/minigun_powerpack)
w_class = WEIGHT_CLASS_HUGE
force = 20
- wield_delay = 12
+ wield_delay = 1.2 SECONDS
gun_skill_category = SKILL_HEAVY_WEAPONS
aim_slowdown = 0.8
flags_gun_features = GUN_WIELDED_FIRING_ONLY|GUN_AMMO_COUNTER|GUN_SMOKE_PARTICLES
@@ -493,7 +493,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
max_shells = 1000 //codex
caliber = CALIBER_10x26_CASELESS //codex
allowed_ammo_types = list(/obj/item/ammo_magazine/minigun_powerpack/smartgun)
- wield_delay = 1.5 SECONDS
+ wield_delay = 1.7 SECONDS
flags_gun_features = GUN_AMMO_COUNTER|GUN_WIELDED_FIRING_ONLY|GUN_IFF|GUN_SMOKE_PARTICLES
gun_skill_category = SKILL_SMARTGUN
attachable_allowed = list(/obj/item/attachable/flashlight, /obj/item/attachable/magnetic_harness, /obj/item/attachable/motiondetector)
@@ -532,7 +532,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
default_ammo_type = /obj/item/ammo_magazine/rifle/pepperball
allowed_ammo_types = list(/obj/item/ammo_magazine/rifle/pepperball)
force = 30 // two shots weeds as it has no bayonet
- wield_delay = 0.5 SECONDS // Very fast to put up.
+ wield_delay = 0.7 SECONDS // Very fast to put up.
attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 18,"rail_x" = 12, "rail_y" = 20, "under_x" = 19, "under_y" = 14, "stock_x" = 19, "stock_y" = 14)
attachable_allowed = list(
/obj/item/attachable/buildasentry,
@@ -626,7 +626,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
flags_equip_slot = NONE
w_class = WEIGHT_CLASS_HUGE
force = 15
- wield_delay = 12
+ wield_delay = 1.2 SECONDS
wield_penalty = 1.6 SECONDS
aim_slowdown = 1.75
general_codex_key = "explosive weapons"
@@ -709,7 +709,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
flags_equip_slot = NONE
w_class = WEIGHT_CLASS_HUGE
force = 15
- wield_delay = 12
+ wield_delay = 1.2 SECONDS
wield_penalty = 1.6 SECONDS
aim_slowdown = 1.75
general_codex_key = "explosive weapons"
@@ -838,7 +838,6 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
/obj/item/attachable/shoulder_mount,
)
- gun_skill_category = SKILL_HEAVY_WEAPONS
attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 18,"rail_x" = 15, "rail_y" = 19, "under_x" = 19, "under_y" = 14, "stock_x" = 19, "stock_y" = 14)
fire_delay = 1 SECONDS
@@ -1024,7 +1023,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
/obj/item/ammo_magazine/railgun/hvap,
)
force = 40
- wield_delay = 1 SECONDS //You're not quick drawing this.
+ wield_delay = 1.2 SECONDS
attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 18,"rail_x" = 31, "rail_y" = 23, "under_x" = 19, "under_y" = 14, "stock_x" = 19, "stock_y" = 14)
attachable_allowed = list(
/obj/item/attachable/magnetic_harness,
@@ -1069,7 +1068,7 @@ Note that this means that snipers will have a slowdown of 3, due to the scope
/obj/item/ammo_magazine/rifle/icc_coilgun,
)
force = 40
- wield_delay = 1 SECONDS //You're not quick drawing this.
+ wield_delay = 1.2 SECONDS
attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 18,"rail_x" = 31, "rail_y" = 23, "under_x" = 19, "under_y" = 14, "stock_x" = 19, "stock_y" = 14)
attachable_allowed = list(
/obj/item/attachable/magnetic_harness,
diff --git a/code/modules/projectiles/mounted.dm b/code/modules/projectiles/mounted.dm
index 49e52959dbe1c..2efd81fba4b6b 100644
--- a/code/modules/projectiles/mounted.dm
+++ b/code/modules/projectiles/mounted.dm
@@ -4,7 +4,7 @@
anchored = TRUE
resistance_flags = XENO_DAMAGEABLE
density = TRUE
- layer = TANK_BARREL_LAYER
+ layer = ABOVE_MOB_PROP_LAYER
use_power = FALSE
hud_possible = list(MACHINE_HEALTH_HUD, MACHINE_AMMO_HUD)
allow_pass_flags = PASSABLE|PASS_LOW_STRUCTURE
diff --git a/code/modules/projectiles/sentries.dm b/code/modules/projectiles/sentries.dm
index c0102411a309e..2106edca55208 100644
--- a/code/modules/projectiles/sentries.dm
+++ b/code/modules/projectiles/sentries.dm
@@ -302,7 +302,7 @@
set_on(FALSE)
update_icon()
-/obj/machinery/deployable/mounted/sentry/take_damage(damage_amount, damage_type, damage_flag, effects, attack_dir, armour_penetration)
+/obj/machinery/deployable/mounted/sentry/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
if(damage_amount <= 0)
return
if(prob(10))
diff --git a/code/modules/reagents/reagents.dm b/code/modules/reagents/reagents.dm
index 96cd0c4592fa0..bdfc9d7e5f21b 100644
--- a/code/modules/reagents/reagents.dm
+++ b/code/modules/reagents/reagents.dm
@@ -171,7 +171,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
/// Called when addiction hits stage4, see [/datum/reagents/proc/metabolize]
/datum/reagent/proc/addiction_act_stage4(mob/living/L, metabolism)
if(prob(30))
- to_chat(L, span_boldannounce("You're not feeling good at all! You really need some [name]."))
+ to_chat(L, span_danger("You're not feeling good at all! You really need some [name]."))
///Convert reagent list to a printable string for logging etc
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index 8dacbdb9675fe..1ba472b8aadec 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -1,52 +1,42 @@
//conveyor2 is pretty much like the original, except it supports corners, but not diverters.
//note that corner pieces transfer stuff clockwise when running forward, and anti-clockwise backwards.
+
+///Max amount of items it will try move in one go
#define MAX_CONVEYOR_ITEMS_MOVE 30
+
+///It don't go
+#define CONVEYOR_OFF 0
+///It go forwards
+#define CONVEYOR_ON_FORWARDS 1
+///It go back
+#define CONVEYOR_ON_REVERSE -1
+
+///true if can operate (no broken segments in this belt run)
+#define CONVEYOR_OPERABLE (1<<0)
+///Inverts the direction the conveyor belt moves, only particularly relevant for diagonals
+#define CONVEYOR_INVERTED (1<<1)
+///Currently has things scheduled for movement. Required to reduce lag
+#define CONVEYOR_IS_CONVEYING (1<<2)
+
GLOBAL_LIST_EMPTY(conveyors_by_id)
/obj/machinery/conveyor
icon = 'icons/obj/recycling.dmi'
icon_state = "conveyor_map"
name = "conveyor belt"
- desc = "A conveyor belt. It can be rotated with a
wrench. It can be reversed with a
screwdriver. The belt can be flipped with a
wirecutter."
+ desc = "A conveyor belt. It can be rotated with a
wrench. It can be reversed with a
screwdriver."
layer = FIREDOOR_OPEN_LAYER
max_integrity = 50
resistance_flags = XENO_DAMAGEABLE
- var/operating = 0 // 1 if running forward, -1 if backwards, 0 if off
- var/operable = 1 // true if can operate (no broken segments in this belt run)
- var/forwards // this is the default (forward) direction, set by the map dir
- var/backwards // hopefully self-explanatory
- var/movedir // the actual direction to move stuff in
-
- var/list/affecting // the list of all items that will be moved this ptick
- var/id = "" // the control ID - must match controller ID
- /// Inverts the direction the conveyor belt moves when false.
- var/verted = FALSE
- /// Is the conveyor's belt flipped? Useful mostly for conveyor belt corners. It makes the belt point in the other direction, rather than just going in reverse.
- var/flipped = FALSE
- /// Are we currently conveying items?
- var/conveying = FALSE
-
-/obj/machinery/conveyor/centcom_auto
- id = "round_end_belt"
-
-/obj/machinery/conveyor/inverted //Directions inverted so you can use different corner pieces.
- icon_state = "conveyor_map_inverted"
- verted = -1
- flipped = TRUE
-
-/obj/machinery/conveyor/inverted/Initialize(mapload)
- . = ..()
- if(mapload && !(ISDIAGONALDIR(dir)))
- stack_trace("[src] at [AREACOORD(src)] spawned without using a diagonal dir. Please replace with a normal version.")
-
-
-/obj/machinery/conveyor/auto/update()
- . = ..()
- if(.)
- operating = TRUE
- update_icon()
+ ///Conveyor specific flags
+ var/conveyor_flags = CONVEYOR_OPERABLE
+ ///Operating direction
+ var/operating = CONVEYOR_OFF
+ ///Current direction of movement
+ var/movedir
+ /// the control ID - must match controller ID
+ var/id = ""
-// create a conveyor
/obj/machinery/conveyor/Initialize(mapload, newdir, newid)
. = ..()
if(newdir)
@@ -57,10 +47,6 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
update_move_direction()
update_icon()
-/obj/machinery/conveyor/auto/Initialize(mapload, newdir)
- operating = TRUE
- return ..()
-
/obj/machinery/conveyor/Destroy()
LAZYREMOVE(GLOB.conveyors_by_id[id], src)
return ..()
@@ -74,102 +60,17 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
else
return ..()
-/obj/machinery/conveyor/setDir(newdir)
- . = ..()
- update_move_direction()
-
-/obj/machinery/conveyor/proc/update_move_direction()
- switch(dir)
- if(NORTH)
- forwards = NORTH
- backwards = SOUTH
- if(SOUTH)
- forwards = SOUTH
- backwards = NORTH
- if(EAST)
- forwards = EAST
- backwards = WEST
- if(WEST)
- forwards = WEST
- backwards = EAST
- if(NORTHEAST)
- forwards = EAST
- backwards = SOUTH
- if(NORTHWEST)
- forwards = NORTH
- backwards = EAST
- if(SOUTHEAST)
- forwards = SOUTH
- backwards = WEST
- if(SOUTHWEST)
- forwards = WEST
- backwards = NORTH
- if(verted)
- var/temp = forwards
- forwards = backwards
- backwards = temp
- if(flipped)
- var/temp = forwards
- forwards = backwards
- backwards = temp
- if(operating == 1)
- movedir = forwards
- else
- movedir = backwards
- update()
-
/obj/machinery/conveyor/update_icon_state()
. = ..()
if(machine_stat & BROKEN)
icon_state = "conveyor-broken"
- else
- icon_state = "conveyor[verted ? -operating : operating ][flipped ? "-flipped" : ""]"
-
-/obj/machinery/conveyor/proc/update()
- if(machine_stat & BROKEN || !operable || machine_stat & NOPOWER)
- operating = FALSE
- update_icon()
- return FALSE
- return TRUE
-// machine process
-// move items to the target location
-/obj/machinery/conveyor/process()
- if(machine_stat & (BROKEN | NOPOWER))
- return
-
- //If the conveyor is broken or already moving items
- if(!operating || conveying)
- return
-
- //get the first 30 items in contents
- var/turf/locturf = loc
- var/list/items = locturf.contents - src
- if(!LAZYLEN(items))//Dont do anything at all if theres nothing there but the conveyor
- return
- var/list/affecting
- if(length(items) > MAX_CONVEYOR_ITEMS_MOVE)
- affecting = items.Copy(1, MAX_CONVEYOR_ITEMS_MOVE + 1)//Lists start at 1 lol
else
- affecting = items
- conveying = TRUE
-
- INVOKE_NEXT_TICK(src, PROC_REF(convey), affecting)//Movement effect
+ icon_state = "conveyor[!operating ? "_off" : operating == CONVEYOR_ON_FORWARDS ? "_forwards": "_reverse"][conveyor_flags & CONVEYOR_INVERTED ? "_inverted" : ""]"
-/obj/machinery/conveyor/proc/convey(list/affecting)
- for(var/am in affecting)
- if(!ismovable(am)) //This is like a third faster than for(var/atom/movable in affecting)
- continue
- var/atom/movable/movable_thing = am
- //Give this a chance to yield if the server is busy
- stoplag()
- if(QDELETED(movable_thing) || (movable_thing.loc != loc))
- continue
- if(iseffect(movable_thing) || isdead(movable_thing))
- continue
- if(!movable_thing.anchored)
- step(movable_thing, movedir)
- conveying = FALSE
+/obj/machinery/conveyor/setDir(newdir)
+ . = ..()
+ update_move_direction()
/obj/machinery/conveyor/crowbar_act(mob/living/user, obj/item/I)
user.visible_message(span_notice("[user] struggles to pry up \the [src] with \the [I]."), \
@@ -193,16 +94,10 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
/obj/machinery/conveyor/screwdriver_act(mob/living/user, obj/item/I)
if(machine_stat & BROKEN)
return TRUE
- verted = !verted
- update_move_direction()
- to_chat(user, span_notice("You set [src]'s direction [verted ? "backwards" : "back to default"]."))
-
-/obj/machinery/conveyor/wirecutter_act(mob/living/user, obj/item/I)
- if(machine_stat & BROKEN)
- return TRUE
- flipped = !flipped
+ conveyor_flags ^= CONVEYOR_INVERTED
update_move_direction()
- to_chat(user, span_notice("You flip [src]'s belt [flipped ? "around" : "back to normal"]."))
+ update_icon()
+ balloon_alert(user, "[conveyor_flags & CONVEYOR_INVERTED ? "backwards" : "back to default"]")
/obj/machinery/conveyor/attackby(obj/item/I, mob/living/user, def_zone)
. = ..()
@@ -216,52 +111,149 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
return
user.Move_Pulled(src)
-// make the conveyor broken
-// also propagate inoperability to any connected conveyor with the same ID
-/obj/machinery/conveyor/proc/broken()
- obj_break()
+/obj/machinery/conveyor/power_change()
+ . = ..()
update()
- var/obj/machinery/conveyor/C = locate() in get_step(src, dir)
- if(C)
- C.set_operable(dir, id, 0)
+/obj/machinery/conveyor/process()
+ if(conveyor_flags & CONVEYOR_IS_CONVEYING)
+ return //you've made a lag monster
+ if(!is_operational())
+ return PROCESS_KILL
+ if(!operating)
+ return PROCESS_KILL
+ if(!isturf(loc))
+ return PROCESS_KILL //how
+
+ //get the first 30 items in contents
+ var/list/items_to_move = loc.contents - src
+ if(length(items_to_move) > MAX_CONVEYOR_ITEMS_MOVE)
+ items_to_move = items_to_move.Copy(1, MAX_CONVEYOR_ITEMS_MOVE + 1)
- C = locate() in get_step(src, REVERSE_DIR(dir))
- if(C)
- C.set_operable(REVERSE_DIR(dir), id, 0)
+ conveyor_flags |= CONVEYOR_IS_CONVEYING
+ INVOKE_NEXT_TICK(src, PROC_REF(convey), items_to_move)
+///Attempts to move a batch of AMs
+/obj/machinery/conveyor/proc/convey(list/affecting)
+ if(!is_operational())
+ return
+ if(!operating)
+ return
+ for(var/am in affecting)
+ if(!ismovable(am)) //This is like a third faster than for(var/atom/movable in affecting)
+ continue
+ var/atom/movable/movable_thing = am
+ stoplag() //Give this a chance to yield if the server is busy
+ if(QDELETED(movable_thing))
+ continue
+ if((movable_thing.loc != loc))
+ continue
+ if(iseffect(movable_thing))
+ continue
+ if(isdead(movable_thing))
+ return
+ if(movable_thing.anchored)
+ return
+ step(movable_thing, movedir)
-//set the operable var if ID matches, propagating in the given direction
+ conveyor_flags &= ~CONVEYOR_IS_CONVEYING
-/obj/machinery/conveyor/proc/set_operable(stepdir, match_id, op)
+///Sets the correct movement directions based on dir
+/obj/machinery/conveyor/proc/update_move_direction()
+ var/forwards
+ var/backwards
+ switch(dir)
+ if(NORTH)
+ forwards = NORTH
+ backwards = SOUTH
+ if(SOUTH)
+ forwards = SOUTH
+ backwards = NORTH
+ if(EAST)
+ forwards = EAST
+ backwards = WEST
+ if(WEST)
+ forwards = WEST
+ backwards = EAST
+ if(NORTHEAST)
+ forwards = EAST
+ backwards = SOUTH
+ if(NORTHWEST)
+ forwards = NORTH
+ backwards = EAST
+ if(SOUTHEAST)
+ forwards = SOUTH
+ backwards = WEST
+ if(SOUTHWEST)
+ forwards = WEST
+ backwards = NORTH
+ if(conveyor_flags & CONVEYOR_INVERTED)
+ var/temp = forwards
+ forwards = backwards
+ backwards = temp
+ if(operating == CONVEYOR_ON_FORWARDS)
+ movedir = forwards
+ else
+ movedir = backwards
+ update()
- if(id != match_id)
+///Handles setting its operating status
+/obj/machinery/conveyor/proc/set_operating(new_position)
+ if(operating == new_position)
return
- operable = op
+ operating = new_position
+ update_move_direction()
+ update_icon()
- update()
- var/obj/machinery/conveyor/C = locate() in get_step(src, stepdir)
- if(C)
- C.set_operable(stepdir, id, op)
+ if(operating)
+ start_processing()
+ else
+ stop_processing()
-/obj/machinery/conveyor/power_change()
+///Checks to see if the conveyor needs to be switched off
+/obj/machinery/conveyor/proc/update()
+ if(!is_operational() || !(conveyor_flags & CONVEYOR_OPERABLE))
+ set_operating(CONVEYOR_OFF)
+ return FALSE
+ return TRUE
+
+/obj/machinery/conveyor/centcom_auto
+ id = "round_end_belt"
+
+/obj/machinery/conveyor/inverted //Directions inverted so you can use different corner pieces.
+ icon_state = "conveyor_map_inverted"
+ conveyor_flags = CONVEYOR_OPERABLE|CONVEYOR_INVERTED
+
+/obj/machinery/conveyor/inverted/Initialize(mapload)
. = ..()
- update()
+ if(mapload && !(ISDIAGONALDIR(dir)))
+ stack_trace("[src] at [AREACOORD(src)] spawned without using a diagonal dir. Please replace with a normal version.")
-///////// the conveyor control switch
+/obj/machinery/conveyor/auto/Initialize(mapload, newdir)
+ set_operating(CONVEYOR_ON_FORWARDS)
+ return ..()
+
+/obj/machinery/conveyor/auto/update()
+ . = ..()
+ if(.)
+ set_operating(CONVEYOR_ON_FORWARDS)
+///////// the conveyor control switch
/obj/machinery/conveyor_switch
name = "conveyor switch"
desc = "A conveyor control switch."
icon = 'icons/obj/recycling.dmi'
icon_state = "switch-off"
-
- var/position = 0 // 0 off, -1 reverse, 1 forward
- var/last_pos = -1 // last direction setting
- var/oneway = FALSE // if the switch only operates the conveyor belts in a single direction.
- var/invert_icon = FALSE // If the level points the opposite direction when it's turned on.
-
- var/id = "" // must match conveyor IDs to control them
+ ///switch position
+ var/position = CONVEYOR_OFF
+ ///Previous switch position
+ var/last_pos = -1
+ ///If this only works one way
+ var/oneway = FALSE
+ ///If the level points the opposite direction when it's turned on.
+ var/invert_icon = FALSE
+ ///ID. Must match conveyor ID's to control them
+ var/id = ""
/obj/machinery/conveyor_switch/Initialize(mapload, newid)
. = ..()
@@ -287,12 +279,12 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
/obj/machinery/conveyor_switch/update_icon_state()
. = ..()
- if(position<0)
+ if(position == CONVEYOR_ON_REVERSE)
if(invert_icon)
icon_state = "switch-fwd"
else
icon_state = "switch-rev"
- else if(position>0)
+ else if(position == CONVEYOR_ON_FORWARDS)
if(invert_icon)
icon_state = "switch-rev"
else
@@ -303,13 +295,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
/// Updates all conveyor belts that are linked to this switch, and tells them to start processing.
/obj/machinery/conveyor_switch/proc/update_linked_conveyors()
for(var/obj/machinery/conveyor/C in GLOB.conveyors_by_id[id])
- C.operating = position
- C.update_move_direction()
- C.update_icon()
- if(C.operating)
- C.start_processing()
- else
- C.stop_processing()
+ C.set_operating(position)
CHECK_TICK
/// Finds any switches with same `id` as this one, and set their position and icon to match us.
@@ -327,14 +313,14 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
position = oneway
else
if(last_pos < 0)
- position = 1
- last_pos = 0
+ position = CONVEYOR_ON_FORWARDS
+ last_pos = CONVEYOR_OFF
else
- position = -1
- last_pos = 0
+ position = CONVEYOR_ON_REVERSE
+ last_pos = CONVEYOR_OFF
else
last_pos = position
- position = 0
+ position = CONVEYOR_OFF
/// Called when a user clicks on this switch with an open hand.
/obj/machinery/conveyor_switch/interact(mob/user)
diff --git a/code/modules/reqs/supplypacks.dm b/code/modules/reqs/supplypacks.dm
index dca903ea3fa16..b46d551ec5b20 100644
--- a/code/modules/reqs/supplypacks.dm
+++ b/code/modules/reqs/supplypacks.dm
@@ -848,7 +848,7 @@ EXPLOSIVES
name = "M40-AG Anti-Gas grenade box crate"
notes = "Cotains 25 grenades"
contains = list(/obj/item/storage/box/visual/grenade/antigas)
- cost = 700
+ cost = 600
/datum/supply_packs/explosives/explosives_cloak
name = "M40-2 SCDP grenade box crate"
@@ -886,6 +886,12 @@ EXPLOSIVES
contains = list(/obj/item/storage/box/visual/grenade/phosphorus)
cost = 700
+/datum/supply_packs/explosives/explosives_hefa
+ name = "M25 HEFA grenade box crate"
+ notes = "Contains 25 grenades"
+ contains = list(/obj/item/storage/box/visual/grenade/hefa)
+ cost = 550
+
/datum/supply_packs/explosives/plastique
name = "C4 plastic explosive"
contains = list(/obj/item/explosive/plastique)
diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm
index 8be96f27404a4..15622228e91fe 100644
--- a/code/modules/tgs/core/core.dm
+++ b/code/modules/tgs/core/core.dm
@@ -166,3 +166,11 @@
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
return api.Visibility()
+
+/world/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ if(!istype(parameters, /list))
+ parameters = list()
+
+ return api.TriggerEvent(event_name, parameters, wait_for_completion)
diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm
index 07ce3b684584e..898516f12486f 100644
--- a/code/modules/tgs/core/datum.dm
+++ b/code/modules/tgs/core/datum.dm
@@ -17,7 +17,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
world.sleep_offline = FALSE // https://www.byond.com/forum/post/2894866
del(world)
world.sleep_offline = FALSE // just in case, this is BYOND after all...
- sleep(1)
+ sleep(world.tick_lag)
TGS_DEBUG_LOG("BYOND DIDN'T TERMINATE THE WORLD!!! TICK IS: [world.time], sleep_offline: [world.sleep_offline]")
/datum/tgs_api/latest
@@ -69,3 +69,6 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
/datum/tgs_api/proc/Visibility()
return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/TriggerEvent(event_name, list/parameters, wait_for_completion)
+ return FALSE
diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm
index 945e2e4117671..7c87922750b9b 100644
--- a/code/modules/tgs/v4/api.dm
+++ b/code/modules/tgs/v4/api.dm
@@ -181,7 +181,7 @@
var/json = json_encode(data)
while(requesting_new_port && !override_requesting_new_port)
- sleep(1)
+ sleep(world.tick_lag)
//we need some port open at this point to facilitate return communication
if(!world.port)
@@ -209,7 +209,7 @@
requesting_new_port = FALSE
while(export_lock)
- sleep(1)
+ sleep(world.tick_lag)
export_lock = TRUE
last_interop_response = null
@@ -217,7 +217,7 @@
text2file(json, server_commands_json_path)
for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
- sleep(1)
+ sleep(world.tick_lag)
if(!last_interop_response)
TGS_ERROR_LOG("Failed to get export result for: [json]")
diff --git a/code/modules/tgs/v5/__interop_version.dm b/code/modules/tgs/v5/__interop_version.dm
index 616263098fd3e..f4806f7adb97c 100644
--- a/code/modules/tgs/v5/__interop_version.dm
+++ b/code/modules/tgs/v5/__interop_version.dm
@@ -1 +1 @@
-"5.8.0"
+"5.9.0"
diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm
index 1c7d67d20cdf6..92c7a8388a711 100644
--- a/code/modules/tgs/v5/_defines.dm
+++ b/code/modules/tgs/v5/_defines.dm
@@ -14,6 +14,7 @@
#define DMAPI5_BRIDGE_COMMAND_KILL 4
#define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5
#define DMAPI5_BRIDGE_COMMAND_CHUNK 6
+#define DMAPI5_BRIDGE_COMMAND_EVENT 7
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
@@ -34,6 +35,7 @@
#define DMAPI5_BRIDGE_PARAMETER_VERSION "version"
#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage"
#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel"
+#define DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION "eventInvocation"
#define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort"
#define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation"
@@ -81,6 +83,7 @@
#define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9
#define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10
#define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11
+#define DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT 12
#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType"
#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand"
@@ -116,3 +119,9 @@
#define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name"
#define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText"
#define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly"
+
+#define DMAPI5_EVENT_ID "eventId"
+
+#define DMAPI5_EVENT_INVOCATION_NAME "eventName"
+#define DMAPI5_EVENT_INVOCATION_PARAMETERS "parameters"
+#define DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION "notifyCompletion"
diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm
index a5c064a8eaf1e..9b64931f8f922 100644
--- a/code/modules/tgs/v5/api.dm
+++ b/code/modules/tgs/v5/api.dm
@@ -27,6 +27,8 @@
var/chunked_requests = 0
var/list/chunked_topics = list()
+ var/list/pending_events = list()
+
var/detached = FALSE
/datum/tgs_api/v5/New()
@@ -125,7 +127,7 @@
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep")
logged = TRUE
- sleep(1)
+ sleep(world.tick_lag)
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed")
@@ -249,6 +251,40 @@
WaitForReattach(TRUE)
return chat_channels.Copy()
+/datum/tgs_api/v5/TriggerEvent(event_name, list/parameters, wait_for_completion)
+ RequireInitialBridgeResponse()
+ WaitForReattach(TRUE)
+
+ if(interop_version.minor < 9)
+ TGS_WARNING_LOG("Interop version too low for custom events!")
+ return FALSE
+
+ var/str_parameters = list()
+ for(var/i in parameters)
+ str_parameters += "[i]"
+
+ var/list/response = Bridge(DMAPI5_BRIDGE_COMMAND_EVENT, list(DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION = list(DMAPI5_EVENT_INVOCATION_NAME = event_name, DMAPI5_EVENT_INVOCATION_PARAMETERS = str_parameters, DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION = wait_for_completion)))
+ if(!response)
+ return FALSE
+
+ var/event_id = response[DMAPI5_EVENT_ID]
+ if(!event_id)
+ return FALSE
+
+ TGS_DEBUG_LOG("Created event ID: [event_id]")
+ if(!wait_for_completion)
+ return TRUE
+
+ TGS_DEBUG_LOG("Waiting for completion of event ID: [event_id]")
+
+ while(!pending_events[event_id])
+ sleep(world.tick_lag)
+
+ TGS_DEBUG_LOG("Completed wait on event ID: [event_id]")
+ pending_events -= event_id
+
+ return TRUE
+
/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
TGS_DEBUG_LOG("DecodeChannels()")
var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
diff --git a/code/modules/tgs/v5/bridge.dm b/code/modules/tgs/v5/bridge.dm
index a0ab359876704..0c5e701a32b60 100644
--- a/code/modules/tgs/v5/bridge.dm
+++ b/code/modules/tgs/v5/bridge.dm
@@ -65,7 +65,7 @@
if(detached)
// Wait up to one minute
for(var/i in 1 to 600)
- sleep(1)
+ sleep(world.tick_lag)
if(!detached && (!require_channels || length(chat_channels)))
break
@@ -77,8 +77,11 @@
/datum/tgs_api/v5/proc/PerformBridgeRequest(bridge_request)
WaitForReattach(FALSE)
+ TGS_DEBUG_LOG("Bridge request start")
// This is an infinite sleep until we get a response
var/export_response = world.Export(bridge_request)
+ TGS_DEBUG_LOG("Bridge request complete")
+
if(!export_response)
TGS_ERROR_LOG("Failed bridge request: [bridge_request]")
return
@@ -88,7 +91,7 @@
TGS_ERROR_LOG("Failed bridge request, missing content!")
return
- var/response_json = file2text(content)
+ var/response_json = TGS_FILE2TEXT_NATIVE(content)
if(!response_json)
TGS_ERROR_LOG("Failed bridge request, failed to load content!")
return
diff --git a/code/modules/tgs/v5/topic.dm b/code/modules/tgs/v5/topic.dm
index 05e6c4e1b2146..e66edc27206a9 100644
--- a/code/modules/tgs/v5/topic.dm
+++ b/code/modules/tgs/v5/topic.dm
@@ -176,6 +176,9 @@
var/list/reattach_response = TopicResponse(error_message)
reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands()
reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort()
+
+ pending_events.Cut()
+
return reattach_response
if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK)
@@ -276,6 +279,15 @@
TGS_WORLD_ANNOUNCE(message)
return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT)
+ var/event_id = topic_parameters[DMAPI5_EVENT_ID]
+ if (!istext(event_id))
+ return TopicResponse("Invalid or missing [DMAPI5_EVENT_ID]")
+
+ TGS_DEBUG_LOG("Completing event ID [event_id]...")
+ pending_events[event_id] = TRUE
+ return TopicResponse()
+
return TopicResponse("Unknown command: [command]")
/datum/tgs_api/v5/proc/WorldBroadcast(message)
diff --git a/code/modules/tgs/v5/undefs.dm b/code/modules/tgs/v5/undefs.dm
index d531d4b7b9dd1..237207fdfd056 100644
--- a/code/modules/tgs/v5/undefs.dm
+++ b/code/modules/tgs/v5/undefs.dm
@@ -14,6 +14,7 @@
#undef DMAPI5_BRIDGE_COMMAND_KILL
#undef DMAPI5_BRIDGE_COMMAND_CHAT_SEND
#undef DMAPI5_BRIDGE_COMMAND_CHUNK
+#undef DMAPI5_BRIDGE_COMMAND_EVENT
#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
@@ -34,6 +35,7 @@
#undef DMAPI5_BRIDGE_PARAMETER_VERSION
#undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE
#undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL
+#undef DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION
#undef DMAPI5_BRIDGE_RESPONSE_NEW_PORT
#undef DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION
@@ -81,6 +83,7 @@
#undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST
+#undef DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT
#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE
#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND
@@ -116,3 +119,9 @@
#undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME
#undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT
#undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY
+
+#undef DMAPI5_EVENT_ID
+
+#undef DMAPI5_EVENT_INVOCATION_NAME
+#undef DMAPI5_EVENT_INVOCATION_PARAMETERS
+#undef DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 73da6d9826422..29b0991748b84 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -68,6 +68,7 @@
#include "weed_spread.dm"
#include "xeno_logical_scaling.dm"
#include "item_variant_test.dm"
+#include "xenoaccuracy.dm"
#ifdef REFERENCE_TRACKING //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
#include "find_reference_sanity.dm"
diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm
index be40f64df1192..b8b9bc011f2ad 100644
--- a/code/modules/unit_tests/create_and_destroy.dm
+++ b/code/modules/unit_tests/create_and_destroy.dm
@@ -47,6 +47,8 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE)
ignore += typesof(/obj/effect/temp_visual)
ignore += typesof(/obj/effect/landmark)
ignore += typesof(/obj/effect/baseturf_helper)
+ ///forcespawned abstract type for vehicles, will never spawn outside of it
+ ignore += typesof(/obj/hitbox)
//Screen objects don't play nicely when spawned manually.
ignore += typesof(/atom/movable/screen)
diff --git a/code/modules/unit_tests/xenoaccuracy.dm b/code/modules/unit_tests/xenoaccuracy.dm
new file mode 100644
index 0000000000000..033aa88b5a43c
--- /dev/null
+++ b/code/modules/unit_tests/xenoaccuracy.dm
@@ -0,0 +1,6 @@
+/datum/unit_test/xenoaccuracy
+
+/datum/unit_test/xenoaccuracy/Run()
+ for(var/datum/xeno_caste/caste AS in subtypesof(/datum/xeno_caste))
+ if(initial(caste.accuracy_malus) >= XENO_DEFAULT_ACCURACY)
+ Fail("A xeno accuracy malus of 70 or over was detected, negatives cannot be used in accuracy calculations.")
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/__vehicle.dm
similarity index 97%
rename from code/modules/vehicles/_vehicle.dm
rename to code/modules/vehicles/__vehicle.dm
index 2e7a681eac3d9..58950fb0a47c4 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/__vehicle.dm
@@ -20,6 +20,8 @@
var/max_drivers = 1
var/move_delay = 2
var/lastmove = 0
+ ///multitile hitbox, set to a hitbox type to make this vehicle multitile.
+ var/obj/hitbox/hitbox
/**
* If the driver needs a certain item in hand (or inserted, for vehicles) to drive this. For vehicles, this must be duplicated on their riding component subtype
* [/datum/component/riding/var/keytype] variable because only a few specific checks are handled here with this var, and the majority of it is on the riding component
@@ -42,6 +44,8 @@
/obj/vehicle/Initialize(mapload)
. = ..()
+ if(hitbox)
+ hitbox = new hitbox(loc, src)
occupants = list()
autogrant_actions_passenger = list()
autogrant_actions_controller = list()
diff --git a/code/modules/vehicles/_hitbox.dm b/code/modules/vehicles/_hitbox.dm
new file mode 100644
index 0000000000000..fdda80e9fa100
--- /dev/null
+++ b/code/modules/vehicles/_hitbox.dm
@@ -0,0 +1,223 @@
+/**
+ * HITBOX
+ * The core of multitile. Acts as a relay for damage and stops people from walking onto the multitle sprite
+ * has changed bounds and as thus must always be forcemoved so it doesnt break everything
+ * I would use pixel movement but the maptick caused by it is way too high and a fake tile based movement might work? but I want this to at least pretend to be generic
+ * Thus we just use this relay. it's an obj so we can make sure all the damage procs that work on root also work on the hitbox
+ */
+/obj/hitbox
+ density = TRUE
+ anchored = TRUE
+ invisibility = INVISIBILITY_MAXIMUM
+ bound_width = 96
+ bound_height = 96
+ bound_x = -32
+ bound_y = -32
+ max_integrity = INFINITY
+ move_resist = INFINITY // non forcemoving this could break gliding so lets just say no
+ ///people riding on this hitbox that we want to move with us
+ var/list/atom/movable/tank_desants
+ ///The "parent" that this hitbox is attached to and to whom it will relay damage
+ var/obj/vehicle/root = null
+
+/obj/hitbox/Initialize(mapload, obj/vehicle/new_root)
+ . = ..()
+ root = new_root
+ allow_pass_flags = root.allow_pass_flags
+ flags_atom = root.flags_atom
+ resistance_flags = root.resistance_flags
+ RegisterSignal(new_root, COMSIG_MOVABLE_MOVED, PROC_REF(root_move))
+ RegisterSignal(new_root, COMSIG_QDELETING, PROC_REF(root_delete))
+ RegisterSignals(new_root, list(COMSIG_RIDDEN_DRIVER_MOVE, COMSIG_VEHICLE_MOVE), PROC_REF(on_attempt_drive))
+ RegisterSignal(new_root, COMSIG_ATOM_DIR_CHANGE, PROC_REF(owner_turned))
+
+ var/static/list/connections = list(
+ COMSIG_OBJ_TRY_ALLOW_THROUGH = PROC_REF(can_cross_hitbox),
+ COMSIG_TURF_JUMP_ENDED_HERE = PROC_REF(on_jump_landed),
+ COMSIG_ATOM_EXITED = PROC_REF(on_exited),
+ )
+ AddElement(/datum/element/connect_loc, connections)
+
+/obj/hitbox/Destroy(force)
+ if(!force) // only when the parent is deleted
+ return QDEL_HINT_LETMELIVE
+ root?.hitbox = null
+ root = null
+ return ..()
+
+///signal handler for handling PASS_WALKOVER
+/obj/hitbox/proc/can_cross_hitbox(datum/source, atom/mover)
+ SIGNAL_HANDLER
+ if(locate(src) in mover.loc)
+ return TRUE
+
+///Signal handler to spin the desants as well when the tank turns
+/obj/hitbox/proc/owner_turned(datum/source, old_dir, new_dir)
+ SIGNAL_HANDLER
+ if(!new_dir || new_dir == old_dir)
+ return
+ for(var/mob/living/desant AS in tank_desants)
+ if(desant.loc == root.loc)
+ continue
+ var/turf/new_pos
+ //doesnt work with diags, but that shouldnt happen, right?
+ if(REVERSE_DIR(old_dir) == new_dir)
+ new_pos = get_step(root, REVERSE_DIR(get_dir(desant, root)))
+ else if(turn(old_dir, 90) == new_dir)
+ new_pos = get_step(root, turn(get_dir(desant, root), -90))
+ else
+ new_pos = get_step(root, turn(get_dir(desant, root), 90))
+ desant.forceMove(new_pos)
+
+///signal handler when someone jumping lands on us
+/obj/hitbox/proc/on_jump_landed(datum/source, atom/lander)
+ SIGNAL_HANDLER
+ if(HAS_TRAIT(lander, TRAIT_TANK_DESANT))
+ return
+ ADD_TRAIT(lander, TRAIT_TANK_DESANT, VEHICLE_TRAIT)
+ LAZYSET(tank_desants, lander, lander.layer)
+ RegisterSignal(lander, COMSIG_QDELETING, PROC_REF(on_desant_del))
+ lander.layer = ABOVE_MOB_PLATFORM_LAYER
+
+///signal handler when we leave a turf under the hitbox
+/obj/hitbox/proc/on_exited(atom/source, atom/movable/AM, direction)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(AM, TRAIT_TANK_DESANT))
+ return
+ if(locate(src) in AM.loc) //ÃŽ'd cut the locate but for some reason it wdoesnt work lol
+ return
+ REMOVE_TRAIT(AM, TRAIT_TANK_DESANT, VEHICLE_TRAIT)
+ AM.layer = LAZYACCESS(tank_desants, AM)
+ LAZYREMOVE(tank_desants, AM)
+ UnregisterSignal(AM, COMSIG_QDELETING)
+
+///cleanup riders on deletion
+/obj/hitbox/proc/on_desant_del(datum/source)
+ SIGNAL_HANDLER
+ LAZYREMOVE(tank_desants, source)
+
+///when root deletes is the only time we want to be deleting
+/obj/hitbox/proc/root_delete()
+ SIGNAL_HANDLER
+ qdel(src, TRUE)
+
+///when the owner moves, let's move with them!
+/obj/hitbox/proc/root_move(atom/movable/mover, atom/oldloc, direction)
+ SIGNAL_HANDLER
+ //direction is null here, so we improvise
+ direction = get_dir(oldloc, mover)
+ var/move_dist = get_dist(oldloc, mover)
+ forceMove(mover.loc)
+ for(var/mob/living/tank_desant AS in tank_desants)
+ step(tank_desant, direction, root.step_size)
+ if(isxeno(tank_desant) || move_dist > 1) //skips xenos, and
+ return
+ var/away_dir = get_dir(tank_desant, root)
+ if(!away_dir)
+ away_dir = pick(GLOB.alldirs)
+ away_dir = REVERSE_DIR(away_dir)
+ var/turf/target = get_step(get_step(root, away_dir), away_dir)
+ tank_desant.throw_at(target, 3, 3, root)
+
+///called when the tank is off movement cooldown and someone tries to move it
+/obj/hitbox/proc/on_attempt_drive(atom/movable/movable_parent, mob/living/user, direction)
+ SIGNAL_HANDLER
+ if(ISDIAGONALDIR(direction))
+ return COMPONENT_DRIVER_BLOCK_MOVE
+ if((root.dir != direction) && (root.dir != REVERSE_DIR(direction)))
+ root.setDir(direction) // tivi todo dupe on medium size kill me
+ if(isarmoredvehicle(root))
+ var/obj/vehicle/sealed/armored/armor = root
+ playsound(armor, armor.engine_sound, 100, TRUE, 20)
+ return COMPONENT_DRIVER_BLOCK_MOVE
+ //Due to this being a hot proc this part here is inlined and set on a by-hitbox-size basis
+ /////////////////////////////
+ var/turf/centerturf = get_step(get_step(root, direction), direction)
+ var/list/enteringturfs = list(centerturf)
+ enteringturfs += get_step(centerturf, turn(direction, 90))
+ enteringturfs += get_step(centerturf, turn(direction, -90))
+ /////////////////////////////
+ var/canstep = TRUE
+ for(var/turf/T AS in enteringturfs) //No break because we want to crush all the turfs before we start trying to move
+ if(!T.Enter(root, direction)) //Check if we can cross the turf first/bump the turf
+ canstep = FALSE
+
+ for(var/atom/movable/O AS in T.contents) // this is checked in turf/enter but it doesnt return false so lmao
+ if(O.CanPass(root)) // Then check for obstacles to crush
+ continue
+ root.Bump(O) //manually call bump on everything
+ canstep = FALSE
+
+ if(canstep)
+ return NONE
+ return COMPONENT_DRIVER_BLOCK_MOVE
+
+/obj/hitbox/CanAllowThrough(atom/movable/mover, turf/target)
+ . = ..()
+ if(.)
+ return
+ if((allow_pass_flags & PASS_TANK) && (mover.pass_flags & PASS_TANK))
+ return TRUE
+
+/obj/hitbox/projectile_hit(obj/projectile/proj)
+ if(proj.firer == root)
+ return FALSE
+ return root.projectile_hit(arglist(args))
+
+/obj/hitbox/bullet_act(obj/projectile/proj)
+ SHOULD_CALL_PARENT(FALSE) // this is an abstract object: we have to avoid everything on parent
+ SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, proj)
+ return root.bullet_act(proj)
+
+/obj/hitbox/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
+ return root.take_damage(arglist(args))
+
+/obj/hitbox/ex_act(severity)
+ return
+
+///2x2 hitbox version
+/obj/hitbox/medium
+ bound_width = 64
+ bound_height = 64
+ bound_x = 0
+ bound_y = -32
+
+/obj/hitbox/medium/on_attempt_drive(atom/movable/movable_parent, mob/living/user, direction)
+ if(ISDIAGONALDIR(direction))
+ return COMPONENT_DRIVER_BLOCK_MOVE
+ if((root.dir != direction) && (root.dir != REVERSE_DIR(direction)))
+ root.setDir(direction)
+ if(isarmoredvehicle(root))
+ var/obj/vehicle/sealed/armored/armor = root
+ playsound(armor, armor.engine_sound, 100, TRUE, 20)
+ return COMPONENT_DRIVER_BLOCK_MOVE
+ ///////////////////////////
+ var/turf/centerturf = get_step(root, direction)
+ var/list/enteringturfs = list()
+ switch(direction)
+ if(NORTH)
+ enteringturfs += get_step(centerturf, turn(direction, -90))
+ if(SOUTH)
+ centerturf = get_step(centerturf, direction)
+ enteringturfs += get_step(centerturf, turn(direction, 90))
+ if(EAST)
+ centerturf = get_step(centerturf, direction)
+ enteringturfs += get_step(centerturf, turn(direction, -90))
+ if(WEST)
+ enteringturfs += get_step(centerturf, turn(direction, 90))
+ enteringturfs += centerturf
+ ////////////////////////////////////
+ var/canstep = TRUE
+ for(var/turf/T AS in enteringturfs) //No break because we want to crush all the turfs before we start trying to move
+ if(!T.Enter(root, direction)) //Check if we can cross the turf first/bump the turf
+ canstep = FALSE
+
+ for(var/atom/movable/O AS in T.contents) // this is checked in turf/enter but it doesnt return false so lmao
+ if(O.CanPass(root)) // Then check for obstacles to crush
+ continue
+ root.Bump(O) //manually call bump on everything
+ canstep = FALSE
+
+ if(canstep)
+ return NONE
+ return COMPONENT_DRIVER_BLOCK_MOVE
diff --git a/code/modules/vehicles/armored/__armored.dm b/code/modules/vehicles/armored/__armored.dm
new file mode 100644
index 0000000000000..da2795a558c95
--- /dev/null
+++ b/code/modules/vehicles/armored/__armored.dm
@@ -0,0 +1,541 @@
+
+/obj/vehicle/sealed/armored
+ name = "\improper TAV - Shortstreet MK4"
+ desc = "An adorable chunk of metal with an alarming amount of firepower designed to crush, immolate, destroy and maim anything that Nanotrasen wants it to. This model contains advanced Bluespace technology which allows a TARDIS-like amount of room on the inside."
+ icon = 'icons/obj/armored/1x1/tinytank.dmi'
+ icon_state = "tank"
+ pixel_x = -16
+ pixel_y = -8
+ layer = ABOVE_MOB_LAYER
+ max_drivers = 1
+ move_resist = INFINITY
+ flags_atom = BUMP_ATTACKABLE|PREVENT_CONTENTS_EXPLOSION
+ allow_pass_flags = PASS_TANK|PASS_AIR|PASS_WALKOVER
+ resistance_flags = XENO_DAMAGEABLE|UNACIDABLE|PLASMACUTTER_IMMUNE|PORTAL_IMMUNE
+
+ // placeholder, make skill check or similar later
+ req_access = list(ACCESS_MARINE_MECH)
+ move_delay = 0.7 SECONDS
+ max_integrity = 600
+ light_range = 10
+ ///Tank bitflags
+ var/flags_armored = ARMORED_HAS_PRIMARY_WEAPON|ARMORED_HAS_HEADLIGHTS
+ ///Sound file(s) to play when we drive around
+ var/engine_sound = 'sound/ambience/tank_driving.ogg'
+ ///frequency to play the sound with
+ var/engine_sound_length = 2 SECONDS
+ /// How long it takes to rev (vrrm vrrm!) TODO: merge this up to /vehicle here and on tg because of cars
+ COOLDOWN_DECLARE(enginesound_cooldown)
+
+ ///Cool and good turret overlay that allows independently swiveling guns
+ var/atom/movable/vis_obj/turret_overlay/turret_overlay
+ ///Icon for the rotating turret icon. also should hold the icons for the weapon icons
+ var/turret_icon = 'icons/obj/armored/1x1/tinytank_gun.dmi'
+ ///Iconstate for the rotating main turret
+ var/turret_icon_state = "turret"
+ ///secondary independently rotating overlay, if we only have a secondary weapon
+ var/image/secondary_weapon_overlay
+ ///Icon for the secondary rotating turret, should contain all possible icons. iconstate is fetched from the attached weapon
+ var/secondary_turret_icon
+ ///Damage overlay for when the vehicle gets damaged
+ var/atom/movable/vis_obj/tank_damage/damage_overlay
+ ///Icon file path for the damage overlay
+ var/damage_icon_path
+ ///Overlay for larger vehicles that need under parts
+ var/image/underlay
+
+ ///What weapon we have in our primary slot
+ var/obj/item/armored_weapon/primary_weapon
+ ///What weapon we have in our secondary slot
+ var/obj/item/armored_weapon/secondary_weapon
+ ///Our driver utility module
+ var/obj/item/tank_module/driver_utility_module
+ ///Our driver utility module
+ var/obj/item/tank_module/gunner_utility_module
+ //What kind of primary tank weaponry we start with. Defaults to a tank gun.
+ var/primary_weapon_type = /obj/item/armored_weapon
+ //What kind of secondary tank weaponry we start with. Default minigun as standard.
+ var/secondary_weapon_type = /obj/item/armored_weapon/secondary_weapon
+ ///if true disables stops users from being able to shoot weapons
+ var/weapons_safety = FALSE
+ //Bool for zoom on/off
+ var/zoom_mode = FALSE
+ /// damage done by rams
+ var/ram_damage = 20
+
+/obj/vehicle/sealed/armored/Initialize(mapload)
+ . = ..()
+ if(flags_armored & ARMORED_HAS_UNDERLAY)
+ underlay = new(icon, icon_state + "_underlay", layer = layer-0.1)
+ add_overlay(underlay)
+ if(damage_icon_path)
+ damage_overlay = new()
+ damage_overlay.icon = damage_icon_path
+ damage_overlay.layer = layer+0.001
+ vis_contents += damage_overlay
+ if(flags_armored & ARMORED_HAS_PRIMARY_WEAPON)
+ turret_overlay = new()
+ turret_overlay.icon = turret_icon
+ turret_overlay.icon_state = turret_icon_state
+ turret_overlay.setDir(dir)
+ turret_overlay.layer = layer+0.002
+ if(flags_armored & ARMORED_HAS_MAP_VARIANTS)
+ switch(SSmapping.configs[GROUND_MAP].armor_style)
+ if(MAP_ARMOR_STYLE_JUNGLE)
+ turret_overlay.icon_state += "_jungle"
+ if(MAP_ARMOR_STYLE_ICE)
+ turret_overlay.icon_state += "_snow"
+ if(MAP_ARMOR_STYLE_PRISON)
+ turret_overlay.icon_state += "_urban"
+ if(MAP_ARMOR_STYLE_DESERT)
+ turret_overlay.icon_state += "_desert"
+ vis_contents += turret_overlay
+ if(primary_weapon_type)
+ var/obj/item/armored_weapon/primary = new primary_weapon_type(src)
+ primary.attach(src, TRUE)
+ if(flags_armored & ARMORED_HAS_SECONDARY_WEAPON)
+ if(secondary_weapon_type)
+ var/obj/item/armored_weapon/secondary = new secondary_weapon_type(src)
+ secondary.attach(src, FALSE)
+ if(flags_armored & ARMORED_HAS_MAP_VARIANTS)
+ switch(SSmapping.configs[GROUND_MAP].armor_style)
+ if(MAP_ARMOR_STYLE_JUNGLE)
+ icon_state += "_jungle"
+ if(MAP_ARMOR_STYLE_ICE)
+ icon_state += "_snow"
+ if(MAP_ARMOR_STYLE_PRISON)
+ icon_state += "_urban"
+ if(MAP_ARMOR_STYLE_DESERT)
+ icon_state += "_desert"
+ GLOB.tank_list += src
+
+/obj/vehicle/sealed/armored/Destroy()
+ QDEL_NULL(primary_weapon)
+ QDEL_NULL(secondary_weapon)
+ QDEL_NULL(driver_utility_module)
+ QDEL_NULL(gunner_utility_module)
+ QDEL_NULL(damage_overlay)
+ QDEL_NULL(turret_overlay)
+ underlay = null
+ GLOB.tank_list -= src
+ return ..()
+
+/obj/vehicle/sealed/armored/generate_actions()
+ initialize_passenger_action_type(/datum/action/vehicle/sealed/armored/eject)
+ initialize_passenger_action_type(/datum/action/vehicle/sealed/armored/swap_seat)
+ if(flags_armored & ARMORED_HAS_HEADLIGHTS)
+ initialize_controller_action_type(/datum/action/vehicle/sealed/armored/toggle_lights, VEHICLE_CONTROL_SETTINGS)
+
+/obj/vehicle/sealed/armored/obj_destruction(damage_amount, damage_type, damage_flag)
+ . = ..()
+ playsound(get_turf(src), 'sound/weapons/guns/fire/tank_cannon1.ogg', 100, TRUE)
+
+/obj/vehicle/sealed/armored/update_icon()
+ . = ..()
+ if(!damage_overlay)
+ return
+ switch(PERCENT(obj_integrity / max_integrity))
+ if(0 to 29)
+ damage_overlay.icon_state = "damage_veryhigh"
+ if(29 to 59)
+ damage_overlay.icon_state = "damage_high"
+ if(59 to 70)
+ damage_overlay.icon_state = "damage_medium"
+ if(70 to 90)
+ damage_overlay.icon_state = "damage_small"
+ else
+ damage_overlay.icon_state = "null"
+
+/obj/vehicle/sealed/armored/update_overlays()
+ . = ..()
+ if(secondary_weapon_overlay)
+ . += secondary_weapon_overlay
+
+/obj/vehicle/sealed/armored/setDir(newdir)
+ . = ..()
+ if(secondary_weapon_overlay)
+ cut_overlay(secondary_weapon_overlay)
+ secondary_weapon_overlay.icon_state = secondary_weapon.icon_state + "_" + "[newdir]"
+ add_overlay(secondary_weapon_overlay)
+
+/obj/vehicle/sealed/armored/examine(mob/user)
+ . = ..()
+ if(!isxeno(user))
+ . += "To fire its main cannon, left click a tile."
+ . += "To fire its secondary weapon, right click a tile."
+ . += "Middle click to toggle weapon safety."
+ . += "It's currently holding [LAZYLEN(occupants)]/[max_occupants] crew."
+ . += span_notice("There is [isnull(primary_weapon) ? "nothing" : "[primary_weapon]"] in the primary attachment point, [isnull(secondary_weapon) ? "nothing" : "[secondary_weapon]"] installed in the secondary slot, [isnull(driver_utility_module) ? "nothing" : "[driver_utility_module]"] in the driver utility slot and [isnull(gunner_utility_module) ? "nothing" : "[gunner_utility_module]"] in the gunner utility slot.")
+ if(!isxeno(user))
+ . += "
It is currently at [PERCENT(obj_integrity / max_integrity)]% integrity."
+
+/obj/vehicle/sealed/armored/vehicle_move(mob/living/user, direction)
+ . = ..()
+ if(!.)
+ return
+ if(COOLDOWN_CHECK(src, enginesound_cooldown))
+ COOLDOWN_START(src, enginesound_cooldown, engine_sound_length)
+ playsound(get_turf(src), engine_sound, 100, TRUE, 20)
+ after_move(direction)
+ forceMove(get_step(src, direction)) // still animates and calls moved() and all that stuff BUT we skip checks
+
+/obj/vehicle/sealed/armored/resisted_against(mob/living/user)
+ balloon_alert(user, "exiting...")
+ if(do_after(user, enter_delay, NONE, src))
+ balloon_alert(user, "exited")
+ mob_exit(user, TRUE)
+
+/obj/vehicle/sealed/armored/CanAllowThrough(atom/movable/mover, turf/target)
+ . = ..()
+ if(.)
+ return
+ if((allow_pass_flags & PASS_TANK) && (mover.pass_flags & PASS_TANK))
+ return TRUE
+
+/obj/vehicle/sealed/armored/Bump(atom/A)
+ . = ..()
+ if(HAS_TRAIT(A, TRAIT_STOPS_TANK_COLLISION))
+ if(!TIMER_COOLDOWN_CHECK(src, COOLDOWN_VEHICLE_CRUSHSOUND))
+ visible_message(span_danger("[src] is stopped by [A]!"))
+ playsound(src, 'sound/effects/metal_crash.ogg', 45)
+ TIMER_COOLDOWN_START(src, COOLDOWN_VEHICLE_CRUSHSOUND, 1 SECONDS)
+ return
+ var/pilot
+ var/list/drivers = return_drivers()
+ if(length(drivers))
+ pilot = drivers[1]
+ A.vehicle_collision(src, get_dir(src, A), get_turf(loc), get_turf(loc), pilot)
+
+/obj/vehicle/sealed/armored/auto_assign_occupant_flags(mob/new_occupant)
+ if(max_occupants == 1)
+ add_control_flags(new_occupant, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_SETTINGS|VEHICLE_CONTROL_MELEE|VEHICLE_CONTROL_EQUIPMENT)
+ return
+
+ if(driver_amount() < max_drivers) //movement controllers
+ add_control_flags(new_occupant, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_SETTINGS)
+ else if(length(return_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT)) < 1)
+ add_control_flags(new_occupant, VEHICLE_CONTROL_MELEE|VEHICLE_CONTROL_EQUIPMENT)
+
+/obj/vehicle/sealed/armored/exit_location(mob/M)
+ return get_step(src, REVERSE_DIR(dir))
+
+/obj/vehicle/sealed/armored/mob_try_enter(mob/M)
+ if(!ishuman(M))
+ return FALSE
+ return ..()
+
+/obj/vehicle/sealed/armored/add_occupant(mob/M, control_flags)
+ RegisterSignal(M, COMSIG_MOB_DEATH, PROC_REF(mob_exit), TRUE)
+ RegisterSignal(M, COMSIG_MOB_MOUSEDOWN, PROC_REF(on_mouseclick), TRUE)
+ RegisterSignal(M, COMSIG_LIVING_DO_RESIST, TYPE_PROC_REF(/atom/movable, resisted_against), TRUE)
+ . = ..()
+ if(primary_weapon)
+ var/list/primary_icons
+ if(primary_weapon.ammo)
+ primary_icons = list(primary_weapon.ammo.default_ammo.hud_state, primary_weapon.ammo.default_ammo.hud_state_empty)
+ else
+ primary_icons = list(primary_weapon.hud_state_empty, primary_weapon.hud_state_empty)
+ M.hud_used.add_ammo_hud(primary_weapon, primary_icons, primary_weapon.ammo.current_rounds)
+ if(secondary_weapon)
+ var/list/secondary_icons
+ if(secondary_weapon.ammo)
+ secondary_icons = list(secondary_weapon.ammo.default_ammo.hud_state, secondary_weapon.ammo.default_ammo.hud_state_empty)
+ else
+ secondary_icons = list(secondary_weapon.hud_state_empty, secondary_weapon.hud_state_empty)
+ M.hud_used.add_ammo_hud(secondary_weapon, secondary_icons, secondary_weapon.ammo.current_rounds)
+
+/obj/vehicle/sealed/armored/remove_occupant(mob/M)
+ M.hud_used.remove_ammo_hud(primary_weapon)
+ M.hud_used.remove_ammo_hud(secondary_weapon)
+ UnregisterSignal(M, COMSIG_MOB_DEATH)
+ UnregisterSignal(M, COMSIG_MOB_MOUSEDOWN)
+ UnregisterSignal(M, COMSIG_LIVING_DO_RESIST)
+ if(M.client)
+ M.update_mouse_pointer()
+ M.client.view_size.reset_to_default()
+ if(is_equipment_controller(M))
+ zoom_mode = FALSE
+ return ..()
+
+/obj/vehicle/sealed/armored/projectile_hit(obj/projectile/proj, cardinal_move, uncrossing)
+ for(var/mob/living/carbon/human/crew AS in occupants)
+ if(crew.wear_id?.iff_signal & proj.iff_signal)
+ return FALSE
+ return ..()
+
+/obj/vehicle/sealed/armored/attack_hand(mob/living/user)
+ . = ..()
+ if(!primary_weapon)
+ balloon_alert(user, "no primary")
+ return
+ if(!length(primary_weapon.ammo_magazine))
+ balloon_alert(user, "magazine empty")
+ return
+ var/choice
+ if(length(primary_weapon.ammo_magazine) == 1)
+ choice = primary_weapon.ammo_magazine[1]
+ else
+ choice = tgui_input_list(user, "Select a magazine to take out", primary_weapon.name, primary_weapon.ammo_magazine)
+ if(!choice)
+ return
+ if(!do_after(user, 1 SECONDS, NONE, src))
+ return
+ balloon_alert(user, "magazine removed")
+ primary_weapon.ammo_magazine -= choice
+ user.put_in_hands(choice)
+
+/obj/vehicle/sealed/armored/attack_hand_alternate(mob/living/user)
+ . = ..()
+ if(!secondary_weapon)
+ balloon_alert(user, "no secondary")
+ return
+ if(!length(secondary_weapon.ammo_magazine))
+ balloon_alert(user, "magazine empty")
+ return
+ var/choice
+ if(length(secondary_weapon.ammo_magazine) == 1)
+ choice = secondary_weapon.ammo_magazine[1]
+ else
+ choice = tgui_input_list(user, "Select a magazine to take out", secondary_weapon.name, secondary_weapon.ammo_magazine)
+ if(!choice)
+ return
+ if(!do_after(user, 1 SECONDS, NONE, src))
+ return
+ balloon_alert(user, "magazine removed")
+ secondary_weapon.ammo_magazine -= choice
+ user.put_in_hands(choice)
+
+/obj/vehicle/sealed/armored/attackby(obj/item/I, mob/user, params)
+ . = ..()
+ if(istype(I, /obj/item/armored_weapon))
+ var/obj/item/armored_weapon/gun = I
+ if(!(gun.weapon_slot & MODULE_PRIMARY))
+ balloon_alert(user, "not a primary weapon")
+ return
+ if(!do_after(user, 2 SECONDS, NONE, src))
+ return
+ user.temporarilyRemoveItemFromInventory(I)
+ gun.attach(src, TRUE)
+ return
+ if(istype(I, /obj/item/ammo_magazine))
+ if(!primary_weapon)
+ balloon_alert(user, "no primary weapon")
+ return
+ if(!(I.type in primary_weapon.accepted_ammo))
+ balloon_alert(user, "not accepted ammo")
+ return
+ if(length(primary_weapon.ammo_magazine) >= primary_weapon.maximum_magazines)
+ balloon_alert(user, "magazine already full")
+ return
+ user.temporarilyRemoveItemFromInventory(I)
+ I.forceMove(primary_weapon)
+ if(!primary_weapon.ammo)
+ primary_weapon.ammo = I
+ balloon_alert(user, "primary gun loaded")
+ for(var/mob/occupant AS in occupants)
+ occupant.hud_used.update_ammo_hud(src, list(primary_weapon.ammo.default_ammo.hud_state, primary_weapon.ammo.default_ammo.hud_state_empty), primary_weapon.ammo.current_rounds)
+ else
+ primary_weapon.ammo_magazine += I
+ balloon_alert(user, "magazines [length(primary_weapon.ammo_magazine)]/[primary_weapon.maximum_magazines]")
+ return
+ if(istype(I, /obj/item/tank_module))
+ var/obj/item/tank_module/mod = I
+ mod.on_equip(src, user)
+
+/obj/vehicle/sealed/armored/attackby_alternate(obj/item/I, mob/user, params)
+ . = ..()
+ if(istype(I, /obj/item/armored_weapon))
+ var/obj/item/armored_weapon/gun = I
+ if(!(gun.weapon_slot & MODULE_SECONDARY))
+ balloon_alert(user, "not a secondary weapon")
+ return
+ if(!do_after(user, 2 SECONDS, NONE, src))
+ return
+ user.temporarilyRemoveItemFromInventory(I)
+ gun.attach(src, FALSE)
+ return
+ if(istype(I, /obj/item/ammo_magazine))
+ if(!secondary_weapon)
+ balloon_alert(user, "no primary weapon")
+ return
+ if(!(I.type in secondary_weapon.accepted_ammo))
+ balloon_alert(user, "not accepted ammo")
+ return
+ if(length(secondary_weapon.ammo_magazine) >= secondary_weapon.maximum_magazines)
+ balloon_alert(user, "magazine already full")
+ return
+ user.temporarilyRemoveItemFromInventory(I)
+ I.forceMove(secondary_weapon)
+ if(!secondary_weapon.ammo)
+ secondary_weapon.ammo = I
+ balloon_alert(user, "secondary gun loaded")
+ for(var/mob/occupant AS in occupants)
+ occupant.hud_used.update_ammo_hud(src, list(secondary_weapon.ammo.default_ammo.hud_state, secondary_weapon.ammo.default_ammo.hud_state_empty), secondary_weapon.ammo.current_rounds)
+ else
+ secondary_weapon.ammo_magazine += I
+ balloon_alert(user, "magazines [length(secondary_weapon.ammo_magazine)]/[secondary_weapon.maximum_magazines]")
+ return
+ if(!isscrewdriver(I))
+ return
+ if(!gunner_utility_module)
+ balloon_alert(user, "no gunner utility module")
+ return
+ balloon_alert(user, "detaching gunner utility")
+ if(!do_after(user, 2 SECONDS, NONE, src))
+ return
+ gunner_utility_module.on_unequip(user)
+ balloon_alert(user, "detached")
+
+
+/obj/vehicle/sealed/armored/welder_act(mob/living/user, obj/item/I)
+ return welder_repair_act(user, I, 50, 5 SECONDS, 0, SKILL_ENGINEER_METAL, 5, 2 SECONDS)
+
+/obj/vehicle/sealed/armored/crowbar_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!primary_weapon)
+ balloon_alert(user, "no primary weapon")
+ return
+ balloon_alert(user, "detaching primary")
+ if(!do_after(user, 2 SECONDS, NONE, src))
+ return
+ var/obj/item/armored_weapon/gun = primary_weapon
+ primary_weapon.detach(loc)
+ user.put_in_hands(gun)
+ balloon_alert(user, "detached")
+
+/obj/vehicle/sealed/armored/wrench_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!secondary_weapon)
+ balloon_alert(user, "no secondary weapon")
+ return
+ balloon_alert(user, "detaching secondary")
+ if(!do_after(user, 2 SECONDS, NONE, src))
+ return
+ var/obj/item/armored_weapon/gun = secondary_weapon
+ secondary_weapon.detach(loc)
+ user.put_in_hands(gun)
+ balloon_alert(user, "detached")
+
+/obj/vehicle/sealed/armored/screwdriver_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!driver_utility_module)
+ balloon_alert(user, "no driver utility module")
+ return
+ balloon_alert(user, "detaching driver utility")
+ if(!do_after(user, 2 SECONDS, NONE, src))
+ return
+ driver_utility_module.on_unequip(user)
+ balloon_alert(user, "detached")
+
+/obj/vehicle/sealed/armored/plastique_act(mob/living/plastique_user)
+ ex_act(EXPLODE_LIGHT)
+
+/**
+ * Toggles Weapons Safety
+ *
+ * Handles enabling or disabling the safety function.
+ */
+/obj/vehicle/sealed/armored/proc/set_safety(mob/user)
+ weapons_safety = !weapons_safety
+ SEND_SOUND(user, sound('sound/machines/beep.ogg', volume = 25))
+ balloon_alert(user, "equipment [weapons_safety ? "safe" : "ready"]")
+ // todo maybe make tanks also update the mouse icon?
+
+///Rotates the cannon overlay
+/obj/vehicle/sealed/armored/proc/swivel_turret(atom/A)
+ var/new_weapon_dir = angle_to_cardinal_dir(Get_Angle(src, A))
+ if(turret_overlay.dir == new_weapon_dir)
+ return FALSE
+ if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TANK_SWIVEL)) //Slight cooldown to avoid spam
+ return FALSE
+ playsound(src, 'sound/effects/tankswivel.ogg', 80,1)
+ TIMER_COOLDOWN_START(src, COOLDOWN_TANK_SWIVEL, 3 SECONDS)
+ turret_overlay.setDir(new_weapon_dir)
+ return TRUE
+
+///handles mouseclicks by a user in the vehicle
+/obj/vehicle/sealed/armored/proc/on_mouseclick(mob/user, atom/target, turf/location, control, list/modifiers)
+ SIGNAL_HANDLER
+ modifiers = params2list(modifiers)
+ if(isnull(location) && target.plane == CLICKCATCHER_PLANE) //Checks if the intended target is in deep darkness and adjusts target based on params.
+ target = params2turf(modifiers["screen-loc"], get_turf(user), user.client)
+ modifiers["icon-x"] = num2text(ABS_PIXEL_TO_REL(text2num(modifiers["icon-x"])))
+ modifiers["icon-y"] = num2text(ABS_PIXEL_TO_REL(text2num(modifiers["icon-y"])))
+ if(modifiers[SHIFT_CLICK]) //Allows things to be examined.
+ return
+ if(!isturf(target) && !isturf(target.loc)) // Prevents inventory from being drilled
+ return
+ if(HAS_TRAIT(user, TRAIT_INCAPACITATED))
+ return
+ if(src == target)
+ return
+ if(!is_equipment_controller(user))
+ balloon_alert(user, "wrong seat for equipment!")
+ return COMSIG_MOB_CLICK_CANCELED
+ if(LAZYACCESS(modifiers, MIDDLE_CLICK))
+ set_safety(user)
+ return COMSIG_MOB_CLICK_CANCELED
+ var/dir_to_target = get_cardinal_dir(src, target)
+ var/obj/item/armored_weapon/selected
+ if(modifiers[BUTTON] == RIGHT_CLICK)
+ selected = secondary_weapon
+ else
+ if(!turret_overlay)
+ return COMSIG_MOB_CLICK_CANCELED
+ if(turret_overlay.dir != dir_to_target)
+ swivel_turret(target)
+ return COMSIG_MOB_CLICK_CANCELED
+ selected = primary_weapon
+ if(!selected)
+ return
+ if(weapons_safety || zoom_mode)
+ return
+ INVOKE_ASYNC(selected, TYPE_PROC_REF(/obj/item/armored_weapon, begin_fire), user, target, modifiers)
+
+/atom/movable/vis_obj/turret_overlay
+ name = "Tank gun turret"
+ desc = "The shooty bit on a tank."
+ icon = 'icons/obj/armored/3x3/tank_gun.dmi' //set by owner
+ icon_state = "turret"
+ layer = ABOVE_ALL_MOB_LAYER
+ ///overlay for the attached gun
+ var/image/gun_overlay
+ ///overlay for the shooting version of that gun
+ var/image/flash_overlay
+ ///currently using the flashing overlay
+ var/flashing = FALSE
+ ///icon state for the secondary
+ var/image/secondary_overlay
+
+/atom/movable/vis_obj/turret_overlay/proc/update_gun_overlay(gun_icon_state)
+ cut_overlays()
+ if(!gun_icon_state)
+ flash_overlay = null
+ gun_overlay = null
+ return
+ flashing = FALSE
+ flash_overlay = image(icon, gun_icon_state + "_fire", pixel_x = -70, pixel_y = -69)
+ gun_overlay = image(icon, gun_icon_state, pixel_x = -40, pixel_y = -48)
+ update_appearance(UPDATE_OVERLAYS)
+
+/atom/movable/vis_obj/turret_overlay/proc/set_flashing(new_flashing)
+ flashing = new_flashing
+ update_appearance(UPDATE_OVERLAYS)
+
+/atom/movable/vis_obj/turret_overlay/update_overlays()
+ . = ..()
+ . += (flashing ? flash_overlay : gun_overlay)
+
+/atom/movable/vis_obj/turret_overlay/setDir(newdir)
+ . = ..()
+ if(secondary_overlay)
+ cut_overlay(secondary_overlay)
+ secondary_overlay.icon_state = copytext(secondary_overlay.icon_state, 1, length(secondary_overlay.icon_state)) + "[dir]"
+ add_overlay(secondary_overlay)
+
+/atom/movable/vis_obj/tank_damage
+ name = "Tank damage overlay"
+ desc = "ow."
+ icon = 'icons/obj/armored/3x3/tank_damage.dmi' //set by owner
+ icon_state = "null" // set on demand
+ vis_flags = VIS_INHERIT_DIR
diff --git a/code/modules/vehicles/armored/_multitile.dm b/code/modules/vehicles/armored/_multitile.dm
new file mode 100644
index 0000000000000..f52e150d14ef3
--- /dev/null
+++ b/code/modules/vehicles/armored/_multitile.dm
@@ -0,0 +1,38 @@
+/obj/vehicle/sealed/armored/multitile
+ name = "TAV - Rhino"
+ desc = "A gigantic wall of metal designed for maximum Xeno destruction. Drag yourself onto it at an entrance to get inside."
+ icon = 'icons/obj/armored/3x3/tank.dmi'
+ turret_icon = 'icons/obj/armored/3x3/tank_gun.dmi'
+ secondary_turret_icon = 'icons/obj/armored/3x3/tank_secondary_gun.dmi'
+ damage_icon_path = 'icons/obj/armored/3x3/tank_damage.dmi'
+ icon_state = "tank"
+ hitbox = /obj/hitbox
+ flags_atom = DIRLOCK|BUMP_ATTACKABLE|PREVENT_CONTENTS_EXPLOSION
+ flags_armored = ARMORED_HAS_PRIMARY_WEAPON|ARMORED_HAS_SECONDARY_WEAPON|ARMORED_HAS_UNDERLAY|ARMORED_HAS_MAP_VARIANTS|ARMORED_HAS_HEADLIGHTS
+ pixel_x = -48
+ pixel_y = -48
+ max_integrity = 900
+ soft_armor = list(MELEE = 50, BULLET = 100 , LASER = 90, ENERGY = 60, BOMB = 60, BIO = 60, FIRE = 50, ACID = 50)
+ hard_armor = list(MELEE = 0, BULLET = 20, LASER = 20, ENERGY = 20, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0)
+ max_occupants = 4
+ move_delay = 0.9 SECONDS
+ ram_damage = 70
+
+///returns a list of possible locations that this vehicle may be entered from
+/obj/vehicle/sealed/armored/multitile/proc/enter_locations(mob/M)
+ return list(get_step_away(get_step(src, REVERSE_DIR(dir)), src, 2))
+
+/obj/vehicle/sealed/armored/multitile/exit_location(mob/M)
+ return pick(enter_locations(M))
+
+/obj/vehicle/sealed/armored/multitile/mob_try_enter(mob/M)
+ if(!(M.loc in enter_locations(M)))
+ balloon_alert(M, "Not at entrance")
+ return FALSE
+ return ..()
+
+/obj/vehicle/sealed/armored/multitile/enter_checks(mob/M)
+ . = ..()
+ if(!.)
+ return
+ return (M.loc in enter_locations(M))
diff --git a/code/modules/vehicles/armored/ammo_magazine.dm b/code/modules/vehicles/armored/ammo_magazine.dm
new file mode 100644
index 0000000000000..82ab75c8f2c06
--- /dev/null
+++ b/code/modules/vehicles/armored/ammo_magazine.dm
@@ -0,0 +1,88 @@
+//FEB 2024 NOTE: some of these are missing loading_sounds, fix it before using these ingame
+//Special ammo magazines for hardpoint modules. Some may not here since you can use normal magazines on them
+/obj/item/ammo_magazine/tank
+ flags_magazine = NONE
+ ///loading sound to play when
+ var/loading_sound
+
+/obj/item/ammo_magazine/tank/ltb_cannon
+ name = "LTB Cannon Magazine"
+ desc = "A primary armament cannon magazine"
+ caliber = "86mm" //Making this unique on purpose
+ icon_state = "ltbcannon_4"
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/rocket/ltb
+ max_rounds = 4
+ loading_sound = 'sound/vehicles/weapons/ltb_reload.ogg'
+
+/obj/item/ammo_magazine/tank/ltb_cannon/update_icon_state()
+ icon_state = "ltbcannon_[current_rounds]"
+
+
+/obj/item/ammo_magazine/tank/ltaap_minigun
+ name = "LTA-AP Minigun Magazine"
+ desc = "A primary armament minigun magazine"
+ caliber = "7.62x51mm" //Correlates to miniguns
+ icon_state = "painless"
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/bullet/minigun
+ max_rounds = 500
+ loading_sound = 'sound/weapons/guns/interact/working_the_bolt.ogg'
+
+
+/obj/item/ammo_magazine/tank/flamer
+ name = "Flamer Magazine"
+ desc = "A secondary armament flamethrower magazine"
+ caliber = "UT-Napthal Fuel" //correlates to flamer mags
+ icon_state = "flametank_large"
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/flamethrower/tank_flamer
+ max_rounds = 120
+
+/obj/item/ammo_magazine/tank/towlauncher
+ name = "TOW Launcher Magazine"
+ desc = "A secondary armament rocket magazine"
+ caliber = "rocket" //correlates to any rocket mags
+ icon_state = "quad_rocket"
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/rocket/ap //Fun fact, AP rockets seem to be a straight downgrade from normal rockets. Maybe I'm missing something...
+ max_rounds = 5
+
+/obj/item/ammo_magazine/tank/secondary_cupola
+ name = "M56 Cupola Magazine"
+ desc = "A secondary armament MG magazine"
+ caliber = "10x28mm" //Correlates to smartguns
+ icon_state = "big_ammo_box"
+ loading_sound = 'sound/weapons/guns/interact/working_the_bolt.ogg'
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/bullet/cupola
+ max_rounds = 50
+
+/obj/item/ammo_magazine/tank/tank_glauncher
+ name = "Grenade Launcher Magazine"
+ desc = "A secondary armament grenade magazine"
+ caliber = "grenade"//grenades
+ icon_state = "glauncher_2"
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/grenade_container
+ max_rounds = 10
+
+/obj/item/ammo_magazine/tank/tank_glauncher/update_icon_state()
+ if(current_rounds >= max_rounds)
+ icon_state = "glauncher_2"
+ else if(current_rounds <= 0)
+ icon_state = "glauncher_0"
+ else
+ icon_state = "glauncher_1"
+
+/obj/item/ammo_magazine/tank/tank_slauncher
+ name = "Smoke Launcher Magazine"
+ desc = "A support armament grenade magazine"
+ caliber = "grenade"
+ icon_state = "slauncher_1"
+ w_class = WEIGHT_CLASS_GIGANTIC
+ default_ammo = /datum/ammo/grenade_container/smoke
+ max_rounds = 6
+
+/obj/item/ammo_magazine/tank/tank_slauncher/update_icon_state()
+ icon_state = "slauncher_[current_rounds <= 0 ? "0" : "1"]"
diff --git a/code/modules/vehicles/armored/apc.dm b/code/modules/vehicles/armored/apc.dm
new file mode 100644
index 0000000000000..75fb4ad86d8b2
--- /dev/null
+++ b/code/modules/vehicles/armored/apc.dm
@@ -0,0 +1,18 @@
+/obj/vehicle/sealed/armored/multitile/apc
+ name = "TAV - Athena"
+ desc = "An unarmed command APC designed to command and transport troops in the battlefield."
+ icon = 'icons/obj/armored/3x3/apc.dmi'
+ icon_state = "apc"
+ damage_icon_path = 'icons/obj/armored/3x3/apc_damage_overlay.dmi'
+ flags_armored = ARMORED_HAS_HEADLIGHTS
+ turret_icon = null
+ secondary_turret_icon = null
+ primary_weapon_type = null
+ secondary_weapon_type = null
+ flags_armored = NONE
+ pixel_x = -48
+ pixel_y = -40
+ obj_integrity = 2000
+ max_integrity = 2000
+ max_occupants = 10 //Clown car? Clown car.
+ move_delay = 0.5 SECONDS
diff --git a/code/modules/vehicles/armored/armored_actions.dm b/code/modules/vehicles/armored/armored_actions.dm
new file mode 100644
index 0000000000000..fb48a9fd3495c
--- /dev/null
+++ b/code/modules/vehicles/armored/armored_actions.dm
@@ -0,0 +1,128 @@
+/obj/vehicle/sealed/armored/generate_action_type()
+ . = ..()
+ if(istype(., /datum/action/vehicle/sealed/armored))
+ var/datum/action/vehicle/sealed/armored/armor = .
+ armor.chassis = src
+
+///yes this is a blatant mech copypaste
+/datum/action/vehicle/sealed/armored
+ action_icon = 'icons/mob/actions/actions_mecha.dmi'
+ ///mech owner of this action
+ var/obj/vehicle/sealed/armored/chassis
+
+/datum/action/vehicle/sealed/armored/Destroy()
+ chassis = null
+ return ..()
+
+/datum/action/vehicle/sealed/armored/eject
+ name = "Eject From Mech"
+ action_icon_state = "mech_eject"
+
+/datum/action/vehicle/sealed/armored/eject/action_activate(trigger_flags)
+ if(!owner)
+ return
+ if(!chassis || !(owner in chassis.occupants))
+ return
+ chassis.resisted_against(owner)
+
+/datum/action/vehicle/sealed/armored/swap_seat
+ name = "Switch Seats"
+ action_icon_state = "mech_seat_swap"
+
+#define ARMOR_DRIVER "Driver"
+#define ARMOR_GUNNER "Gunner"
+#define ARMOR_PASSENGER "Passenger"
+
+/datum/action/vehicle/sealed/armored/swap_seat/action_activate(trigger_flags)
+ if(!transfer_checks())
+ return
+ var/list/choices = list()
+ if(!chassis.is_driver(owner))
+ choices += ARMOR_DRIVER
+ if(!chassis.is_equipment_controller(owner))
+ choices += ARMOR_GUNNER
+ choices += ARMOR_PASSENGER
+ var/choice = tgui_input_list(owner, "Select a seat", chassis.name, choices)
+ if(!choice)
+ return
+ if(!transfer_checks(choice))
+ return
+ chassis.balloon_alert(owner, "moving to other seat...")
+ if(!do_after(owner, chassis.enter_delay, target = chassis, extra_checks=CALLBACK(src, PROC_REF(transfer_checks), choice)))
+ chassis.balloon_alert(owner, "interrupted!")
+ return
+ chassis.remove_control_flags(owner, VEHICLE_CONTROL_MELEE|VEHICLE_CONTROL_EQUIPMENT|VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_SETTINGS)
+ switch(choice)
+ if(ARMOR_GUNNER)
+ chassis.balloon_alert(owner, "controlling gunner seat")
+ chassis.add_control_flags(owner, VEHICLE_CONTROL_MELEE|VEHICLE_CONTROL_EQUIPMENT)
+ if(ARMOR_DRIVER)
+ chassis.balloon_alert(owner, "controlling pilot seat")
+ chassis.add_control_flags(owner, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_SETTINGS)
+ if(ARMOR_PASSENGER)
+ chassis.balloon_alert(owner, "entered passenger seat")
+
+///checks if owner can still transfer
+/datum/action/vehicle/sealed/armored/swap_seat/proc/transfer_checks(choice)
+ if(!owner || !chassis || !(owner in chassis.occupants))
+ return FALSE
+ if(length(chassis.occupants) >= chassis.max_occupants)
+ chassis.balloon_alert(owner, "other seats occupied!")
+ return FALSE
+ switch(choice)
+ if(ARMOR_GUNNER)
+ if(length(chassis.return_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT)) >= 1)
+ chassis.balloon_alert(owner, "gunner occupied!")
+ return FALSE
+ if(ARMOR_DRIVER)
+ if(chassis.driver_amount() >= chassis.max_drivers)
+ chassis.balloon_alert(owner, "driver occupied!")
+ return FALSE
+ return TRUE
+
+#undef ARMOR_DRIVER
+#undef ARMOR_GUNNER
+#undef ARMOR_PASSENGER
+
+/datum/action/vehicle/sealed/armored/toggle_lights
+ name = "Toggle Lights"
+ action_icon_state = "mech_lights_off"
+
+/datum/action/vehicle/sealed/armored/toggle_lights/action_activate(trigger_flags)
+ if(!owner || !chassis || !(owner in chassis.occupants))
+ return
+
+ if(!(chassis.flags_armored & ARMORED_HAS_HEADLIGHTS))
+ chassis.balloon_alert(owner, "the vehicle's lights are broken!")
+ return
+ chassis.flags_armored ^= ARMORED_LIGHTS_ON
+ if(chassis.flags_armored & ARMORED_LIGHTS_ON)
+ action_icon_state = "mech_lights_on"
+ else
+ action_icon_state = "mech_lights_off"
+ chassis.set_light_on(chassis.flags_armored & ARMORED_LIGHTS_ON)
+ chassis.balloon_alert(owner, "toggled lights [chassis.flags_armored & ARMORED_LIGHTS_ON ? "on":"off"]")
+ playsound(chassis,'sound/mecha/brass_skewer.ogg', 40, TRUE)
+ chassis.log_message("Toggled lights [(chassis.flags_armored & ARMORED_LIGHTS_ON)?"on":"off"].", LOG_MECHA)
+ update_button_icon()
+
+/datum/action/vehicle/sealed/armored/zoom
+ name = "Zoom"
+ action_icon_state = "mech_zoom_off"
+ keybinding_signals = list(
+ KEYBINDING_NORMAL = COMSIG_MECHABILITY_TOGGLE_ZOOM,
+ )
+
+/datum/action/vehicle/sealed/armored/zoom/action_activate(trigger_flags)
+ if(!owner?.client || !chassis || !(owner in chassis.occupants))
+ return
+ chassis.zoom_mode = !chassis.zoom_mode
+ action_icon_state = "mech_zoom_[chassis.zoom_mode ? "on" : "off"]"
+ chassis.log_message("Toggled zoom mode.", LOG_MECHA)
+ to_chat(owner, "
Zoom mode [chassis.zoom_mode?"en":"dis"]abled.")
+ if(chassis.zoom_mode)
+ owner.client.view_size.set_view_radius_to(4.5)
+ SEND_SOUND(owner, sound('sound/mecha/imag_enh.ogg', volume=50))
+ else
+ owner.client.view_size.reset_to_default()
+ update_button_icon()
diff --git a/code/modules/vehicles/armored/armored_modules.dm b/code/modules/vehicles/armored/armored_modules.dm
new file mode 100644
index 0000000000000..79209699cc326
--- /dev/null
+++ b/code/modules/vehicles/armored/armored_modules.dm
@@ -0,0 +1,127 @@
+/**
+ *TANK MODULES
+ *
+ * Attached to the tank and provide abilities/ passive upgrades
+ */
+/obj/item/tank_module
+ name = "Tank Module"
+ desc = "Yell at the admin that spawned this in please."
+ icon = 'icons/obj/armored/hardpoint_modules.dmi'
+ icon_state = "ltb_cannon"
+ ///reference to current overlay added to owner
+ var/image/overlay
+ ///vehicle this overlay is attached to
+ var/obj/vehicle/sealed/armored/owner
+ ///Bool whether this module is a driver module or not
+ var/is_driver_module = TRUE
+
+///Called to apply modules to a vehicle
+/obj/item/tank_module/proc/on_equip(obj/vehicle/sealed/armored/vehicle, mob/living/user)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!istype(vehicle))
+ return FALSE
+ var/slot = is_driver_module ? vehicle.driver_utility_module : vehicle.gunner_utility_module
+ if(slot)
+ user?.balloon_alert(user, "module slot full")
+ return FALSE
+ user?.temporarilyRemoveItemFromInventory(src)
+ forceMove(vehicle)
+ if(is_driver_module)
+ vehicle.driver_utility_module = src
+ else
+ vehicle.gunner_utility_module = src
+ if(!vehicle.turret_overlay)
+ overlay = image(vehicle.icon, null, icon_state)
+ vehicle.add_overlay(overlay)
+ else
+ overlay = image(vehicle.turret_overlay.icon, null, icon_state)
+ vehicle.turret_overlay.add_overlay(overlay)
+ owner = vehicle
+ return TRUE
+
+///called to remove this module from its vehicle
+/obj/item/tank_module/proc/on_unequip(mob/user)
+ SHOULD_CALL_PARENT(TRUE)
+ if(owner.driver_utility_module == src)
+ owner.driver_utility_module = null
+ else
+ owner.gunner_utility_module = null
+ forceMove(owner.drop_location())
+ owner.cut_overlay(overlay)
+ owner.turret_overlay?.cut_overlay(overlay)
+ owner = null
+ overlay = null
+ user?.put_in_hands(src)
+ return TRUE
+
+/obj/item/tank_module/Destroy()
+ if(owner)
+ on_unequip()
+ return ..()
+
+
+/obj/item/tank_module/overdrive
+ name = "overdrive module"
+ desc = "A module that enhances the speed of armored combat vehicles by increasing fuel efficiency."
+ icon_state = "overdrive"
+
+/obj/item/tank_module/overdrive/on_equip(target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/vehicle/sealed/armored/vehicle = target
+ vehicle.move_delay -= 0.15 SECONDS
+
+/obj/item/tank_module/overdrive/on_unequip()
+ owner.move_delay += 0.15 SECONDS
+ return ..()
+
+/obj/item/tank_module/passenger
+ name = "passenger module"
+ desc = "A module that increases the carrying capacity of a vehicle with extra seats."
+ icon_state = "uninstalled APC frieght carriage"
+
+/obj/item/tank_module/passenger/on_equip(target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/vehicle/sealed/armored/vehicle = target
+ vehicle.max_occupants += 4
+
+/obj/item/tank_module/passenger/on_unequip(target)
+ owner.max_occupants -= 4
+ return ..()
+
+
+/obj/item/tank_module/ability
+ name = "Ability Module"
+ desc = "You shouldnt be seeing this."
+ icon_state = "overdrive"
+ ///typepaths for the ability we want to grant
+ var/ability_to_grant
+ ///if given, a single flag of who we want this ability to be granted to
+ var/flag_controller = NONE
+
+/obj/item/tank_module/ability/on_equip(obj/vehicle/sealed/armored/vehicle, attach_right)
+ . = ..()
+ if(!.)
+ return
+ if(flag_controller)
+ vehicle.initialize_controller_action_type(ability_to_grant, flag_controller)
+ else
+ vehicle.initialize_passenger_action_type(ability_to_grant)
+
+/obj/item/tank_module/ability/on_unequip(atom/moveto)
+ if(flag_controller)
+ owner.destroy_controller_action_type(ability_to_grant, flag_controller)
+ else
+ owner.destroy_passenger_action_type(ability_to_grant)
+ return ..()
+
+/obj/item/tank_module/ability/zoom
+ name = "zoom module"
+ desc = "Allows gunners to see further while looking through it. Weapons cannot be used while looking through it."
+ icon_state = "zoom"
+ is_driver_module = FALSE
+ flag_controller = VEHICLE_CONTROL_EQUIPMENT
+ ability_to_grant = /datum/action/vehicle/sealed/armored/zoom
diff --git a/code/modules/vehicles/armored/armored_weapons.dm b/code/modules/vehicles/armored/armored_weapons.dm
new file mode 100644
index 0000000000000..454284363e6ff
--- /dev/null
+++ b/code/modules/vehicles/armored/armored_weapons.dm
@@ -0,0 +1,311 @@
+/obj/item/armored_weapon
+ name = "LTB main battle tank cannon"
+ desc = "A TGMC vehicle's main turret cannon. It fires 86mm rocket propelled shells"
+ icon = 'icons/obj/armored/hardpoint_modules.dmi'
+ icon_state = "ltb_cannon"
+ ///owner this is attached to
+ var/obj/vehicle/sealed/armored/chassis
+ ///The turret icon if we equip the weapon in a secondary slot, you should null this if its unequippable as such
+ var/secondary_equipped_icon
+ ///Weapon slot this weapon fits in
+ var/weapon_slot = MODULE_PRIMARY
+
+ ///currently loaded ammo. initial value is ammo we start with
+ var/obj/item/ammo_magazine/ammo = /obj/item/ammo_magazine/tank/ltb_cannon
+ ///Current loaded magazines: top one empties into ammo
+ var/list/obj/item/ammo_magazine/ammo_magazine = list()
+ ///maximum magazines ammo_magazine can hold
+ var/maximum_magazines = 5
+ ///ammo types we'll be able to accept
+ var/list/accepted_ammo = list(
+ /obj/item/ammo_magazine/tank/tank_glauncher,
+ /obj/item/ammo_magazine/tank/ltb_cannon,
+ )
+ ///current tracked target for fire(), updated when user drags
+ var/atom/current_target
+ ///current mob firing this weapon. used for tracking for iff and etc in fire()
+ var/mob/current_firer
+
+ ///sound file to play when this weapon you know, fires
+ var/fire_sound = list('sound/weapons/guns/fire/tank_cannon1.ogg', 'sound/weapons/guns/fire/tank_cannon2.ogg')
+ ///Tracks windups
+ var/windup_checked = WEAPON_WINDUP_NOT_CHECKED
+ ///windup sound played during windup
+ var/windup_sound
+ ///windup delay for this object
+ var/windup_delay = 0
+ ///scatter of this weapon. in degrees and modified by arm this is attached to
+ var/variance = 0
+ /// since mech guns only get one firemode this is for all types of shots
+ var/projectile_delay = 5 SECONDS
+ /// time between shots in a burst
+ var/projectile_burst_delay = 2
+ ///bullets per burst if firemode is set to burst
+ var/burst_amount = 0
+ ///fire mode to use for autofire
+ var/fire_mode = GUN_FIREMODE_SEMIAUTO
+ ///how many seconds automatic reloading takes
+ var/rearm_time = 4.5 SECONDS
+ ///ammo hud icon to display when no ammo is loaded
+ var/hud_state_empty = "shell_empty"
+
+/obj/item/armored_weapon/Initialize(mapload)
+ . = ..()
+ if(ammo)
+ ammo = new ammo(src)
+ AddComponent(/datum/component/automatedfire/autofire, projectile_delay, projectile_delay, projectile_burst_delay, burst_amount, fire_mode, CALLBACK(src, PROC_REF(set_bursting)), CALLBACK(src, PROC_REF(reset_fire)), CALLBACK(src, PROC_REF(fire)))
+
+/obj/item/armored_weapon/Destroy()
+ if(chassis)
+ detach(get_turf(chassis))
+ QDEL_NULL(ammo)
+ QDEL_LIST(ammo_magazine)
+ return ..()
+
+///called by the chassis: begins firing, yes this is stolen from mech but I made both so bite me
+/obj/item/armored_weapon/proc/begin_fire(mob/source, atom/target, list/modifiers)
+ if(!ammo || ammo.current_rounds < 0)
+ playsound(source, 'sound/weapons/guns/fire/empty.ogg', 15, 1)
+ return
+ if(TIMER_COOLDOWN_CHECK(chassis, COOLDOWN_MECHA_EQUIPMENT(type)))
+ return
+ TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_EQUIPMENT(type), projectile_delay)
+
+ set_target(get_turf_on_clickcatcher(target, source, list2params(modifiers)))
+ if(!current_target)
+ return
+ RegisterSignal(source, COMSIG_MOB_MOUSEUP, PROC_REF(stop_fire))
+ RegisterSignal(source, COMSIG_MOB_MOUSEDRAG, PROC_REF(change_target))
+ if(windup_delay && windup_checked == WEAPON_WINDUP_NOT_CHECKED)
+ windup_checked = WEAPON_WINDUP_CHECKING
+ playsound(chassis.loc, windup_sound, 30, TRUE)
+ if(!do_after(source, windup_delay, IGNORE_TARGET_LOC_CHANGE, chassis, BUSY_ICON_DANGER, BUSY_ICON_DANGER, extra_checks = CALLBACK(src, PROC_REF(do_after_checks), current_target)))
+ windup_checked = WEAPON_WINDUP_NOT_CHECKED
+ return
+ windup_checked = WEAPON_WINDUP_CHECKED
+ if(QDELETED(current_target))
+ windup_checked = WEAPON_WINDUP_NOT_CHECKED
+ return
+ current_firer = source
+ if(fire_mode == GUN_FIREMODE_SEMIAUTO)
+ var/fire_return // todo fix: code expecting return values from async
+ ASYNC
+ fire_return = fire()
+ if(!fire_return || windup_checked == WEAPON_WINDUP_CHECKING)
+ return
+ reset_fire()
+ return
+ SEND_SIGNAL(src, COMSIG_ARMORED_FIRE)
+ source?.client?.mouse_pointer_icon = 'icons/effects/supplypod_target.dmi'
+
+/// do after checks for the mecha equipment do afters
+/obj/item/armored_weapon/proc/do_after_checks(atom/target)
+ if(!chassis)
+ return FALSE
+ if(QDELETED(current_target))
+ return FALSE
+ if(chassis.primary_weapon == src)
+ var/dir_target_diff = get_between_angles(Get_Angle(chassis, current_target), dir2angle(chassis.turret_overlay.dir))
+ if(dir_target_diff > (ARMORED_FIRE_CONE_ALLOWED / 2))
+ if(!chassis.swivel_turret(current_target))
+ return FALSE
+ dir_target_diff = get_between_angles(Get_Angle(chassis, current_target), dir2angle(chassis.turret_overlay.dir))
+ if(dir_target_diff > (ARMORED_FIRE_CONE_ALLOWED / 2))
+ return FALSE
+ return TRUE
+
+///callback wrapper for adding/removing trait
+/obj/item/armored_weapon/proc/set_bursting(bursting)
+ if(bursting)
+ ADD_TRAIT(src, TRAIT_GUN_BURST_FIRING, VEHICLE_TRAIT)
+ return
+ REMOVE_TRAIT(src, TRAIT_GUN_BURST_FIRING, VEHICLE_TRAIT)
+
+///Changes the current target.
+/obj/item/armored_weapon/proc/change_target(datum/source, atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params)
+ SIGNAL_HANDLER
+ set_target(get_turf_on_clickcatcher(over_object, source, params))
+
+///Sets the current target and registers for qdel to prevent hardels
+/obj/item/armored_weapon/proc/set_target(atom/object)
+ if(object == current_target || object == chassis)
+ return
+ if(current_target)
+ UnregisterSignal(current_target, COMSIG_QDELETING)
+ current_target = object
+ if(current_target)
+ RegisterSignal(current_target, COMSIG_QDELETING, PROC_REF(clean_target))
+
+///Stops the Autofire component and resets the current cursor.
+/obj/item/armored_weapon/proc/stop_fire(mob/living/source, atom/object, location, control, params)
+ SIGNAL_HANDLER
+ var/list/modifiers = params2list(params)
+ if(!((modifiers[BUTTON] == RIGHT_CLICK) && chassis.secondary_weapon == src) && !((modifiers[BUTTON] == LEFT_CLICK) && chassis.primary_weapon == src))
+ return
+ SEND_SIGNAL(src, COMSIG_ARMORED_STOP_FIRE)
+ if(!HAS_TRAIT(src, TRAIT_GUN_BURST_FIRING))
+ reset_fire()
+ UnregisterSignal(source, list(COMSIG_MOB_MOUSEDRAG, COMSIG_MOB_MOUSEUP))
+
+///Cleans the current target in case of Hardel
+/obj/item/armored_weapon/proc/clean_target()
+ SIGNAL_HANDLER
+ current_target = get_turf(current_target)
+
+///Resets the autofire component.
+/obj/item/armored_weapon/proc/reset_fire()
+ windup_checked = WEAPON_WINDUP_NOT_CHECKED
+ current_firer?.client?.mouse_pointer_icon = chassis.mouse_pointer
+ set_target(null)
+ current_firer = null
+
+///does any effects and changes to the projectile when it is fired
+/obj/item/armored_weapon/proc/apply_weapon_modifiers(obj/projectile/projectile_to_fire, mob/firer)
+ projectile_to_fire.shot_from = src
+ if(chassis.hitbox?.tank_desants)
+ projectile_to_fire.hit_atoms += chassis.hitbox.tank_desants
+ if((projectile_to_fire.ammo.flags_ammo_behavior & AMMO_IFF) && ishuman(firer))
+ var/mob/living/carbon/human/human_firer = firer
+ var/obj/item/card/id/id = human_firer.get_idcard()
+ projectile_to_fire.iff_signal = id?.iff_signal
+
+///actually executes firing when autofire asks for it, returns TRUE to keep firing FALSE to stop
+/obj/item/armored_weapon/proc/fire()
+ if(chassis.primary_weapon == src)
+ var/dir_target_diff = get_between_angles(Get_Angle(chassis, current_target), dir2angle(chassis.turret_overlay.dir))
+ if(dir_target_diff > (ARMORED_FIRE_CONE_ALLOWED / 2))
+ chassis.swivel_turret(current_target)
+ return AUTOFIRE_CONTINUE
+ else if(chassis.turret_overlay)
+ chassis.turret_overlay.cut_overlay(chassis.turret_overlay.secondary_overlay)
+ chassis.turret_overlay.secondary_overlay.dir = get_cardinal_dir(chassis, current_target)
+ chassis.turret_overlay.add_overlay(chassis.turret_overlay.secondary_overlay)
+ else
+ chassis.cut_overlay(chassis.secondary_weapon_overlay)
+ chassis.secondary_weapon_overlay.dir = get_cardinal_dir(chassis, current_target)
+ chassis.add_overlay(chassis.secondary_weapon_overlay)
+
+
+ var/type_to_spawn = CHECK_BITFIELD(initial(ammo.default_ammo.flags_ammo_behavior), AMMO_HITSCAN) ? /obj/projectile/hitscan : /obj/projectile
+ var/obj/projectile/projectile_to_fire = new type_to_spawn(get_turf(src), initial(ammo.default_ammo.hitscan_effect_icon))
+ projectile_to_fire.generate_bullet(GLOB.ammo_list[ammo.default_ammo])
+
+ apply_weapon_modifiers(projectile_to_fire, current_firer)
+ var/firing_angle = get_angle_with_scatter(chassis, current_target, projectile_to_fire.scatter, projectile_to_fire.p_x, projectile_to_fire.p_y)
+
+ playsound(chassis, islist(fire_sound) ? pick(fire_sound):fire_sound, 20, TRUE)
+ projectile_to_fire.fire_at(current_target, chassis, null, projectile_to_fire.ammo.max_range, projectile_to_fire.projectile_speed, firing_angle, suppress_light = HAS_TRAIT(src, TRAIT_GUN_SILENCED))
+
+ chassis.log_message("Fired from [name], targeting [current_target] at [AREACOORD(current_target)].", LOG_ATTACK)
+
+ if(chassis.primary_weapon == src && !chassis.turret_overlay.flashing)
+ chassis.turret_overlay.set_flashing(TRUE)
+ addtimer(CALLBACK(chassis.turret_overlay, TYPE_PROC_REF(/atom/movable/vis_obj/turret_overlay, set_flashing), FALSE), 7, TIMER_CLIENT_TIME)
+
+ ammo.current_rounds--
+ for(var/mob/occupant AS in chassis.occupants)
+ occupant.hud_used.update_ammo_hud(src, list(ammo.default_ammo.hud_state, ammo.default_ammo.hud_state_empty), ammo.current_rounds)
+ if(ammo.current_rounds > 0)
+ return AUTOFIRE_CONTINUE|AUTOFIRE_SUCCESS
+ playsound(src, 'sound/weapons/guns/misc/empty_alarm.ogg', 25, 1)
+ eject_ammo()
+ if(LAZYACCESS(current_firer.do_actions, src) || length(ammo_magazine) < 1)
+ return AUTOFIRE_SUCCESS
+ var/obj/item/ammo_magazine/tank/new_mag = ammo_magazine[1]
+ if(istype(new_mag) && new_mag.loading_sound)
+ // .5 sec delay to let other sounds play out
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), src, new_mag.loading_sound, 40), 5)
+ if(!do_after(current_firer, rearm_time, IGNORE_HELD_ITEM|IGNORE_LOC_CHANGE, chassis, BUSY_ICON_GENERIC))
+ return AUTOFIRE_SUCCESS
+ reload()
+ return AUTOFIRE_CONTINUE|AUTOFIRE_SUCCESS
+
+///eject current ammo from tank
+/obj/item/armored_weapon/proc/eject_ammo()
+ ammo.forceMove(chassis.exit_location())
+ ammo.update_appearance()
+ ammo = null
+ for(var/mob/occupant AS in chassis.occupants)
+ occupant.hud_used.update_ammo_hud(src, list(hud_state_empty, hud_state_empty), 0)
+
+///load topmost ammo magazine, if there is any
+/obj/item/armored_weapon/proc/reload()
+ if(ammo)
+ eject_ammo()
+ ammo = popleft(ammo_magazine)
+ for(var/mob/occupant AS in chassis.occupants)
+ occupant.hud_used.update_ammo_hud(src, list(ammo.default_ammo.hud_state, ammo.default_ammo.hud_state_empty), ammo.current_rounds)
+
+///attach this weapon to a chassis
+/obj/item/armored_weapon/proc/attach(obj/vehicle/sealed/armored/tank, attach_primary)
+ if(attach_primary)
+ tank.primary_weapon?.detach(tank.exit_location())
+ tank.primary_weapon = src
+ tank.turret_overlay.update_gun_overlay(icon_state)
+ else
+ tank.secondary_weapon?.detach(tank.exit_location())
+ tank.secondary_weapon = src
+ if(tank.turret_overlay)
+ // do not remove the dir = SOUTH becuase otherwise byond flips an internal flag so the dir is inherited from the turret
+ tank.turret_overlay.secondary_overlay = image(tank.turret_icon, icon_state = icon_state + "_" + "[tank.turret_overlay.dir]", dir = SOUTH)
+ tank.turret_overlay.add_overlay(tank.turret_overlay.secondary_overlay)
+ else
+ tank.secondary_weapon_overlay = image(tank.turret_icon, icon_state = icon_state + "_" + "[tank.dir]")
+ tank.update_appearance(UPDATE_OVERLAYS)
+ chassis = tank
+ forceMove(tank)
+ var/icon_list
+ if(ammo?.default_ammo)
+ icon_list = list(ammo.default_ammo.hud_state, ammo.default_ammo.hud_state_empty)
+ else
+ icon_list = list(hud_state_empty, hud_state_empty)
+ for(var/mob/occupant AS in chassis.occupants)
+ occupant.hud_used.add_ammo_hud(src, icon_list, ammo ? ammo.current_rounds : 0)
+
+///detach this weapon to a chassis
+/obj/item/armored_weapon/proc/detach(atom/moveto)
+ if(chassis.primary_weapon == src)
+ chassis.primary_weapon = null
+ chassis.turret_overlay.update_gun_overlay()
+ else
+ chassis.secondary_weapon = null
+ if(chassis.turret_overlay)
+ chassis.turret_overlay.cut_overlay(chassis.turret_overlay.secondary_overlay)
+ chassis.turret_overlay.secondary_overlay = null
+ else
+ chassis.secondary_weapon_overlay = null
+ chassis.update_appearance(UPDATE_OVERLAYS)
+ for(var/mob/occupant AS in chassis.occupants)
+ occupant.hud_used.remove_ammo_hud(src)
+ chassis = null
+ forceMove(moveto)
+
+/obj/item/armored_weapon/secondary_weapon
+ name = "secondary cupola minigun"
+ desc = "A robotically controlled minigun that spws lead. Do not stand in front of it!!"
+ icon_state = "cupola"
+ fire_sound = 'sound/weapons/guns/fire/tank_minigun_loop.ogg'
+ windup_delay = 5
+ windup_sound = 'sound/weapons/guns/fire/tank_minigun_start.ogg'
+ weapon_slot = MODULE_PRIMARY|MODULE_SECONDARY
+ secondary_equipped_icon = 'icons/obj/armored/3x3/tank_secondary_gun.dmi'
+ ammo = /obj/item/ammo_magazine/tank/secondary_cupola
+ accepted_ammo = list(/obj/item/ammo_magazine/tank/secondary_cupola)
+ fire_mode = GUN_FIREMODE_AUTOMATIC
+ projectile_delay = 2
+ rearm_time = 1 SECONDS
+ maximum_magazines = 5
+ hud_state_empty = "rifle_empty"
+
+/obj/item/armored_weapon/apc_cannon
+ name = "MKV-7 utility payload launcher"
+ desc = "A double barrelled cannon which can rapidly deploy utility packages to the battlefield."
+ icon_state = "APC uninstalled dualcannon"
+ fire_sound = 'sound/weapons/guns/fire/tank_smokelauncher.ogg'
+ ammo = /obj/item/ammo_magazine/tank/tank_slauncher
+ accepted_ammo = list(
+ /obj/item/ammo_magazine/tank/tank_slauncher,
+ /obj/item/ammo_magazine/tank/tank_glauncher,
+ )
+ projectile_delay = 0.7 SECONDS
+ hud_state_empty = "grenade_empty"
diff --git a/code/modules/vehicles/armored/medium_apc.dm b/code/modules/vehicles/armored/medium_apc.dm
new file mode 100644
index 0000000000000..82910cc242041
--- /dev/null
+++ b/code/modules/vehicles/armored/medium_apc.dm
@@ -0,0 +1,13 @@
+/obj/vehicle/sealed/armored/multitile/medium/apc
+ name = "TAV - Nike"
+ desc = "A heavily armoured vehicle with light armaments designed to ferry troops around the battlefield, or assist with search and rescue (SAR) operations."
+ icon = 'icons/obj/armored/2x2/medium_vehicles.dmi'
+ turret_icon = 'icons/obj/armored/2x2/medium_vehicles.dmi'
+ turret_icon_state = "apc_turret"
+ damage_icon_path = null
+ flags_armored = ARMORED_HAS_PRIMARY_WEAPON|ARMORED_HAS_SECONDARY_WEAPON|ARMORED_HAS_UNDERLAY
+ icon_state = "apc"
+ move_delay = 0.25 SECONDS
+ max_occupants = 5
+ primary_weapon_type = /obj/item/armored_weapon/apc_cannon //Only has a utility launcher, no offense as standard.
+ secondary_weapon_type = null
diff --git a/code/modules/vehicles/armored/medium_tank.dm b/code/modules/vehicles/armored/medium_tank.dm
new file mode 100644
index 0000000000000..adddc2af3f6aa
--- /dev/null
+++ b/code/modules/vehicles/armored/medium_tank.dm
@@ -0,0 +1,19 @@
+/obj/vehicle/sealed/armored/multitile/medium //Its a smaller tank, we had sprites for it so whoo
+ name = "THV - Hades"
+ desc = "A metal behemoth which is designed to cleave through enemy lines. It comes pre installed with a main tank cannon capable of deploying heavy payloads, as well as a minigun which can tear through multiple targets in quick succession."
+ icon = 'icons/obj/armored/2x2/medium_vehicles.dmi'
+ turret_icon = 'icons/obj/armored/2x2/medium_vehicles.dmi'
+ turret_icon_state = "tank_turret"
+ hitbox = /obj/hitbox/medium
+ secondary_turret_icon = null
+ damage_icon_path = null
+ icon_state = "tank"
+ flags_armored = ARMORED_HAS_PRIMARY_WEAPON|ARMORED_HAS_UNDERLAY
+ pixel_x = -16
+ pixel_y = -32
+ obj_integrity = 1300
+ max_integrity = 1300
+ max_occupants = 3
+
+/obj/vehicle/sealed/armored/multitile/medium/enter_locations(mob/M)
+ return list(get_step(src, REVERSE_DIR(dir)))
diff --git a/code/modules/vehicles/armored/small_apc.dm b/code/modules/vehicles/armored/small_apc.dm
new file mode 100644
index 0000000000000..21d52a30879a8
--- /dev/null
+++ b/code/modules/vehicles/armored/small_apc.dm
@@ -0,0 +1,15 @@
+/obj/vehicle/sealed/armored/apc
+ name = "TAV - Nike"
+ desc = "A miniaturized replica of a popular personnel carrier. For ages 5 and up."
+ icon = 'icons/obj/armored/1x1/tinytank.dmi'
+ turret_icon = 'icons/obj/armored/1x1/tinytank_gun.dmi'
+ turret_icon_state = "apc_turret"
+ icon_state = "apc"
+ flags_armored = NONE
+ move_delay = 0.3 SECONDS
+ flags_armored = NONE
+ pixel_x = -16
+ pixel_y = -8
+ max_occupants = 3
+ primary_weapon_type = null
+ secondary_weapon_type = null
diff --git a/code/modules/vehicles/armored/tank_fabricator.dm b/code/modules/vehicles/armored/tank_fabricator.dm
new file mode 100644
index 0000000000000..d15bec0c721a7
--- /dev/null
+++ b/code/modules/vehicles/armored/tank_fabricator.dm
@@ -0,0 +1,9 @@
+/obj/machinery/tank_part_fabricator
+ name = "vehicle part fabricator"
+ desc = "A large automated 3D printer for producing new vehicle parts and maintaining old ones."
+ density = TRUE
+ anchored = TRUE
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 20
+ icon = 'icons/obj/machines/drone_fab.dmi'
+ icon_state = "drone_fab_idle"
diff --git a/code/modules/vehicles/armored/vehicle_collision.dm b/code/modules/vehicles/armored/vehicle_collision.dm
new file mode 100644
index 0000000000000..37692e63868fb
--- /dev/null
+++ b/code/modules/vehicles/armored/vehicle_collision.dm
@@ -0,0 +1,64 @@
+/**
+ *This proc is called when a atom is crashed into by a [armored vehicle][/obj/vehicle/sealed/armored]. Damage is then dealt to both the vehicle and atom
+ *
+ * * Arguments:
+ * * veh is the vehicle that is ramming
+ * * facing is the direction the vehicle is facing for when we ram it
+ * * T is the turf where the vehicle is used with-
+ * * temp to check whether a mob is squished
+ */
+/atom/proc/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ var/damage = veh.ram_damage // Each vehicle gets its own damage, you can modify it with snowplows and such ideally
+
+ if(!TIMER_COOLDOWN_CHECK(veh, COOLDOWN_VEHICLE_CRUSHSOUND))
+ visible_message(span_danger("[veh] rams [src]!"))
+ playsound(src, 'sound/effects/metal_crash.ogg', 45)
+ TIMER_COOLDOWN_START(veh, COOLDOWN_VEHICLE_CRUSHSOUND, 1 SECONDS)
+ return damage
+
+/obj/structure/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ . = ..()
+ take_damage(., BRUTE, MELEE, TRUE, facing, 0)
+
+/obj/structure/barricade/plasteel/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ . = ..()
+ toggle_open(FALSE)
+
+/obj/vehicle/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp) //MONSTER TRUCKS
+ . = ..()
+ take_damage(., BRUTE, MELEE, TRUE, facing, 0)
+
+/obj/machinery/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ . = ..()
+ take_damage(., BRUTE, MELEE, TRUE, facing, 0)
+
+/turf/closed/wall/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ . = ..()
+ take_damage(., BRUTE, MELEE, TRUE, facing, 0)
+
+/mob/living/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp, mob/pilot)
+ . = ..()
+ if(stat == DEAD)
+ return 0
+ if(lying_angle)
+ return 0
+ log_attack("[key_name(pilot)] drove into [key_name(src)] with [veh]")
+ temp = get_step(veh.loc, facing)
+ T = temp
+ T = get_step(T, facing)
+ T = get_step(T, facing)
+ T = get_step(T, facing)
+ face_atom(T)
+ throw_at(T, 3, 2, veh, 1)
+ return take_overall_damage(., BRUTE, MELEE, FALSE, FALSE, TRUE, 0, 4)
+
+
+/mob/living/carbon/xenomorph/larva/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ gib() //fuck you
+
+/obj/effect/alien/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ . = ..()
+ take_damage(., BRUTE, MELEE, TRUE, facing, 0)
+
+/obj/effect/alien/weeds/vehicle_collision(obj/vehicle/sealed/armored/veh, facing, turf/T, turf/temp)
+ return
diff --git a/code/modules/vehicles/atv.dm b/code/modules/vehicles/atv.dm
index a697f30646575..5328827da2f79 100644
--- a/code/modules/vehicles/atv.dm
+++ b/code/modules/vehicles/atv.dm
@@ -47,7 +47,7 @@
buckled_mob.bullet_act(P)
return TRUE
-/obj/vehicle/ridden/atv/obj_destruction()
+/obj/vehicle/ridden/atv/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
explosion(src, devastation_range = -1, light_impact_range = 2, flame_range = 3, flash_range = 4)
return ..()
diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm
index 42efcee22e8e2..52460f0fa2222 100644
--- a/code/modules/vehicles/cars/car.dm
+++ b/code/modules/vehicles/cars/car.dm
@@ -1,6 +1,7 @@
/obj/vehicle/sealed/car
layer = ABOVE_MOB_LAYER
move_resist = MOVE_FORCE_VERY_STRONG
+ move_delay = 1
///Bitflags for special behavior such as kidnapping
var/car_traits = NONE
///Sound file(s) to play when we drive around
@@ -9,8 +10,6 @@
var/engine_sound_length = 2 SECONDS
///Time it takes to break out of the car.
var/escape_time = 6 SECONDS
- /// How long it takes to move, cars don't use the riding component similar to mechs so we handle it ourselves
- var/vehicle_move_delay = 1
/// How long it takes to rev (vrrm vrrm!)
COOLDOWN_DECLARE(enginesound_cooldown)
@@ -72,20 +71,20 @@
kidnapped.forceMove(src)
add_occupant(kidnapped, VEHICLE_CONTROL_KIDNAPPED)
-/obj/vehicle/sealed/car/obj_destruction(damage_flag)
+/obj/vehicle/sealed/car/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
explosion(src, heavy_impact_range = 1, light_impact_range = 2, flash_range = 3, adminlog = FALSE)
log_message("[src] exploded due to destruction", LOG_ATTACK)
return ..()
/obj/vehicle/sealed/car/relaymove(mob/living/user, direction)
if(is_driver(user) && canmove && (!key_type || istype(inserted_key, key_type)))
- vehicle_move(direction)
+ vehicle_move(user, direction)
return TRUE
-/obj/vehicle/sealed/car/vehicle_move(direction)
- if(!COOLDOWN_CHECK(src, cooldown_vehicle_move))
- return FALSE
- COOLDOWN_START(src, cooldown_vehicle_move, vehicle_move_delay)
+/obj/vehicle/sealed/car/vehicle_move(mob/living/user, direction)
+ . = ..()
+ if(!.)
+ return
if(COOLDOWN_CHECK(src, enginesound_cooldown))
COOLDOWN_START(src, enginesound_cooldown, engine_sound_length)
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 6cc71dc101cae..22d507f5fd07e 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -190,6 +190,8 @@
var/ui_y = 600
/// ref to screen object that displays in the middle of the UI
var/atom/movable/screen/mech_view/ui_view
+ ///Current owning faction
+ var/faction
/obj/item/radio/mech //this has to go somewhere
subspace_transmission = TRUE
@@ -266,7 +268,16 @@
mech_status_hud.remove_from_hud(src)
return ..()
-/obj/vehicle/sealed/mecha/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/vehicle/sealed/mecha/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
+ if(istype(blame_mob) && blame_mob.ckey)
+ var/datum/personal_statistics/personal_statistics = GLOB.personal_statistics_list[blame_mob.ckey]
+ if(faction == blame_mob.faction)
+ personal_statistics.mechs_destroyed -- //bruh
+ personal_statistics.mission_mechs_destroyed --
+ else
+ personal_statistics.mechs_destroyed ++
+ personal_statistics.mission_mechs_destroyed ++
+
spark_system?.start()
var/mob/living/silicon/ai/unlucky_ais
diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
index 7e9f9a41c528e..510adee62cbe4 100644
--- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
+++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
@@ -151,13 +151,10 @@
holding = grey.limbs[MECH_GREY_L_ARM]
projectile_to_fire.scatter = max(variance + holding?.scatter_mod, 0)
projectile_to_fire.projectile_speed = projectile_to_fire.ammo.shell_speed
- if(projectile_to_fire.ammo.flags_ammo_behavior & AMMO_IFF)
- var/iff_signal
- if(ishuman(firer))
- var/mob/living/carbon/human/human_firer = firer
- var/obj/item/card/id/id = human_firer.get_idcard()
- iff_signal = id?.iff_signal
- projectile_to_fire.iff_signal = iff_signal
+ if((projectile_to_fire.ammo.flags_ammo_behavior & AMMO_IFF) && ishuman(firer))
+ var/mob/living/carbon/human/human_firer = firer
+ var/obj/item/card/id/id = human_firer.get_idcard()
+ projectile_to_fire.iff_signal = id?.iff_signal
///actually executes firing when autofire asks for it, returns TRUE to keep firing FALSE to stop
/obj/item/mecha_parts/mecha_equipment/weapon/proc/fire()
diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm
index 7ebc599478f13..3755c6394a05a 100644
--- a/code/modules/vehicles/mecha/mecha_defense.dm
+++ b/code/modules/vehicles/mecha/mecha_defense.dm
@@ -36,7 +36,7 @@
to_chat(occupants, "[icon2html(src, occupants)][span_danger("[gear] is critically damaged!")]")
playsound(src, gear.destroy_sound, 50)
-/obj/vehicle/sealed/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = TRUE, attack_dir, armour_penetration)
+/obj/vehicle/sealed/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = TRUE, attack_dir, armour_penetration, mob/living/blame_mob)
var/damage_taken = ..()
if(damage_taken <= 0 || obj_integrity < 0)
return damage_taken
@@ -222,7 +222,7 @@
if(!attacking_item.force)
return
- var/damage_taken = take_damage(attacking_item.force, attacking_item.damtype, MELEE, 1)
+ var/damage_taken = take_damage(attacking_item.force, attacking_item.damtype, MELEE, blame_mob = user)
try_damage_component(damage_taken, user.zone_selected)
var/hit_verb = length(attacking_item.attack_verb) ? "[pick(attacking_item.attack_verb)]" : "hit"
diff --git a/code/modules/vehicles/mecha/mecha_mob_interaction.dm b/code/modules/vehicles/mecha/mecha_mob_interaction.dm
index 513217edafad3..1daff10bd295b 100644
--- a/code/modules/vehicles/mecha/mecha_mob_interaction.dm
+++ b/code/modules/vehicles/mecha/mecha_mob_interaction.dm
@@ -56,6 +56,7 @@
hud_type.add_to_hud(src)
else
hud_type.remove_from_hud(src)
+ faction = newoccupant.faction //we do not unset when exiting, last occupant is the owner
if(!internal_damage)
SEND_SOUND(newoccupant, sound('sound/mecha/nominal.ogg',volume=50))
diff --git a/code/modules/vehicles/mecha/mecha_movement.dm b/code/modules/vehicles/mecha/mecha_movement.dm
index d2f2e03730cd4..928323d6ac0e6 100644
--- a/code/modules/vehicles/mecha/mecha_movement.dm
+++ b/code/modules/vehicles/mecha/mecha_movement.dm
@@ -22,12 +22,12 @@
. = TRUE
if(!canmove || !(user in return_drivers()))
return
- vehicle_move(direction)
+ vehicle_move(user, direction)
-/obj/vehicle/sealed/mecha/vehicle_move(direction, forcerotate = FALSE)
- if(!COOLDOWN_CHECK(src, cooldown_vehicle_move))
- return FALSE
- COOLDOWN_START(src, cooldown_vehicle_move, move_delay)
+/obj/vehicle/sealed/mecha/vehicle_move(mob/living/user, direction, forcerotate = FALSE)
+ . = ..()
+ if(!.)
+ return
if(completely_disabled)
return FALSE
if(!direction)
diff --git a/code/modules/vehicles/motorbike.dm b/code/modules/vehicles/motorbike.dm
index e95fbd25887a5..f321c48d221e0 100644
--- a/code/modules/vehicles/motorbike.dm
+++ b/code/modules/vehicles/motorbike.dm
@@ -176,7 +176,7 @@
smoke.set_up(0, src)
smoke.start()
-/obj/vehicle/ridden/motorbike/obj_destruction()
+/obj/vehicle/ridden/motorbike/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
explosion(src, light_impact_range = 2, flash_range = 0)
return ..()
@@ -197,9 +197,6 @@
if(user.lying_angle || user.incapacitated()) //Can't use your inventory when lying
return FALSE
- if(istype(user.loc, /obj/vehicle/multitile/root/cm_armored)) //Stops inventory actions in a mech/tank
- return FALSE
-
if(over_object == user && Adjacent(user)) //This must come before the screen objects only block
open(user)
return FALSE
diff --git a/code/modules/vehicles/multitile/cm_armored.dm b/code/modules/vehicles/multitile/cm_armored.dm
deleted file mode 100644
index bea65fa019307..0000000000000
--- a/code/modules/vehicles/multitile/cm_armored.dm
+++ /dev/null
@@ -1,810 +0,0 @@
-
-//NOT bitflags, just global constant values
-#define HDPT_PRIMARY "primary"
-#define HDPT_SECDGUN "secondary"
-#define HDPT_SUPPORT "support"
-#define HDPT_ARMOR "armor"
-#define HDPT_TREADS "treads"
-
-//Percentages of what hardpoints take what damage, e.g. armor takes 37.5% of the damage
-GLOBAL_LIST_INIT(armorvic_dmg_distributions, list(
- HDPT_PRIMARY = 0.15,
- HDPT_SECDGUN = 0.125,
- HDPT_SUPPORT = 0.075,
- HDPT_ARMOR = 0.5,
- HDPT_TREADS = 0.15))
-
-//The main object, should be an abstract class // todo delete me
-/obj/vehicle/multitile/root/cm_armored
- name = "Armored Vehicle"
- desc = "Get inside to operate the vehicle."
- hitbox_type = /obj/vehicle/multitile/hitbox/cm_armored //Used for emergencies and respawning hitboxes
-
- //What slots the vehicle can have
- var/list/hardpoints = list(HDPT_ARMOR, HDPT_TREADS, HDPT_SECDGUN, HDPT_SUPPORT, HDPT_PRIMARY)
-
- //The next world.time when the tank can move
- var/next_move = 0
-
- //Below are vars that can be affected by hardpoints, generally used as ratios or decisecond timers
-
- move_delay = 30 //default 3 seconds per tile
-
- var/active_hp
-
- var/list/dmg_distribs = list()
-
- //Changes cooldowns and accuracies
- var/list/misc_ratios = list(
- "move" = 1.0,
- "prim_acc" = 1.0,
- "secd_acc" = 1.0,
- "supp_acc" = 1.0,
- "prim_cool" = 1.0,
- "secd_cool" = 1.0,
- "supp_cool" = 1.0)
-
- //Changes how much damage the tank takes
- var/list/dmg_multipliers = list(
- "all" = 1.0, //for when you want to make it invincible
- "acid" = 1.0,
- "slash" = 1.0,
- "bullet" = 1.0,
- "explosive" = 1.0,
- "blunt" = 1.0,
- "abstract" = 1.0) //abstract for when you just want to hurt it
-
- //Decisecond cooldowns for the slots
- var/list/internal_cooldowns = list(
- "primary" = 300,
- "secondary" = 200,
- "support" = 150)
-
- //Percentage accuracies for slot
- var/list/accuracies = list(
- "primary" = 0.97,
- "secondary" = 0.67,
- "support" = 0.5)
-
- //Placeholders
- icon = 'icons/obj/vehicles.dmi'
- icon_state = "cargo_engine"
-
-
-/obj/vehicle/multitile/root/cm_armored/Initialize(mapload)
- . = ..()
- GLOB.tank_list += src
- set_light(0.01)
-
-
-/obj/vehicle/multitile/root/cm_armored/Destroy()
- for(var/i in linked_objs)
- var/obj/O = linked_objs[i]
- if(O == src)
- continue
- qdel(O, TRUE) //Delete all of the hitboxes etc
- GLOB.tank_list -= src
- return ..()
-
-//What to do if all ofthe installed modules have been broken
-/obj/vehicle/multitile/root/cm_armored/proc/handle_all_modules_broken()
- return
-
-/obj/vehicle/multitile/root/cm_armored/proc/deactivate_all_hardpoints()
- var/list/slots = get_activatable_hardpoints()
- for(var/slot in slots)
- var/obj/item/hardpoint/HP = hardpoints[slot]
- HP?.deactivate()
-
-/obj/vehicle/multitile/root/cm_armored/proc/remove_all_players()
- return
-
-//The basic vehicle code that moves the tank, with movement delay implemented
-/obj/vehicle/multitile/root/cm_armored/relaymove(mob/user, direction)
- if(world.time < next_move)
- return
- next_move = world.time + move_delay * misc_ratios["move"]
-
- return ..()
-
-//Same thing but for rotations
-/obj/vehicle/multitile/root/cm_armored/try_rotate(deg, mob/user, force = FALSE)
- if(world.time < next_move && !force)
- return
- next_move = world.time + move_delay * misc_ratios["move"] * (force ? 2 : 3) //3 for a 3 point turn, idk
- return ..()
-
-/obj/vehicle/multitile/root/cm_armored/proc/can_use_hp(mob/M)
- return TRUE
-
-//Used by the gunner to swap which module they are using
-//e.g. from the minigun to the smoke launcher
-//Only the active hardpoint module can be used
-/obj/vehicle/multitile/root/cm_armored/verb/switch_active_hp()
- set name = "Change Active Weapon"
- set category = "Vehicle"
- set src in view(0)
-
- if(!can_use_hp(usr))
- return
-
- var/list/slots = get_activatable_hardpoints()
-
- if(!length(slots))
- to_chat(usr, span_warning("All of the modules can't be activated or are broken."))
- return
-
- var/slot = tgui_input_list(usr, "Select a slot.", null, slots)
-
- var/obj/item/hardpoint/HP = hardpoints[slot]
- if(!(HP?.obj_integrity))
- to_chat(usr, span_warning("That module is either missing or broken."))
- return
-
- active_hp = slot
- to_chat(usr, span_notice("You select the [slot] slot."))
- if(isliving(usr))
- var/mob/living/M = usr
- M.set_interaction(src)
-
-/obj/vehicle/multitile/root/cm_armored/verb/reload_hp()
- set name = "Reload Active Weapon"
- set category = "Vehicle"
- set src in view(0)
-
- if(!can_use_hp(usr))
- return
-
- //TODO: make this a proc so I don't keep repeating this code
- var/list/slots = get_activatable_hardpoints()
-
- if(!length(slots))
- to_chat(usr, span_warning("All of the modules can't be reloaded or are broken."))
- return
-
- var/slot = tgui_input_list(usr, "Select a slot.", null, slots)
-
- var/obj/item/hardpoint/HP = hardpoints[slot]
- if(!length(HP?.backup_clips))
- to_chat(usr, span_warning("That module is either missing or has no remaining backup clips."))
- return
-
- var/obj/item/ammo_magazine/A = HP.backup_clips[1] //LISTS START AT 1 REEEEEEEEEEEE
- if(!A)
- to_chat(usr, span_danger("Something went wrong! PM a staff member! Code: T_RHPN"))
- return
-
- to_chat(usr, span_notice("You begin reloading the [slot] module."))
-
- addtimer(CALLBACK(src, PROC_REF(finish_reloading_hp), usr, HP, A, slot), 2 SECONDS)
-
-/obj/vehicle/multitile/root/cm_armored/proc/finish_reloading_hp(mob/living/user, obj/item/hardpoint/HP, obj/item/ammo_magazine/A, slot)
- if(!can_use_hp(usr))
- return
-
- HP.ammo.forceMove(get_turf(entrance))
- HP.ammo.update_icon()
- HP.ammo = A
- HP.backup_clips.Remove(A)
-
- to_chat(usr, span_notice("You reload the [slot] module."))
-
-
-/obj/vehicle/multitile/root/cm_armored/proc/get_activatable_hardpoints()
- var/list/slots = list()
- for(var/slot in hardpoints)
- var/obj/item/hardpoint/HP = hardpoints[slot]
- if(!(HP?.obj_integrity))
- continue
- if(!HP.is_activatable)
- continue
- slots += slot
-
- return slots
-
-
-
-//Special armored vic healthcheck that mainly updates the hardpoint states
-/obj/vehicle/multitile/root/cm_armored/proc/healthcheck()
- repair_damage(max_integrity) //The tank itself doesn't take damage
- var/i
- var/remove_person = TRUE //Whether or not to call handle_all_modules_broken()
- for(i in hardpoints)
- var/obj/item/hardpoint/H = hardpoints[i]
- if(!H)
- continue
- if(!H.obj_integrity)
- H.remove_buff()
- else
- remove_person = FALSE //if something exists but isnt broken
-
- if(remove_person)
- handle_all_modules_broken()
-
- update_icon()
-
-//Since the vics are 3x4 we need to swap between the two files with different dimensions
-//Also need to offset to center the tank about the root object
-/obj/vehicle/multitile/root/cm_armored/update_icon_state()
- . = ..()
- //Assuming 3x3 with half block overlaps in the tank's direction
- if(dir in list(NORTH, SOUTH))
- pixel_x = -32
- pixel_y = -48
- icon = 'icons/obj/vehicles/tank_NS.dmi'
-
- else if(dir in list(EAST, WEST))
- pixel_x = -48
- pixel_y = -32
- icon = 'icons/obj/vehicles/tank_EW.dmi'
-
-/obj/vehicle/multitile/root/cm_armored/update_overlays()
- . = ..()
-
- //Basic iteration that snags the overlay from the hardpoint module object
- for(var/i in hardpoints)
- var/obj/item/hardpoint/H = hardpoints[i]
-
- if((i == HDPT_TREADS && !H) || (H && !H.obj_integrity)) //Treads not installed or broken
- var/image/I = image(icon, icon_state = "damaged_hardpt_[i]")
- . += I
-
- if(H)
- var/image/I = H.get_icon_image(0, 0, dir)
- . += I
-
-//Hitboxes but with new names
-/obj/vehicle/multitile/hitbox/cm_armored
- name = "Armored Vehicle"
- desc = "Get inside to operate the vehicle."
- allow_pass_flags = PASSABLE
- var/lastsound = 0
-
-//If something want to delete this, it's probably either an admin or the shuttle
-//If it's an admin, they want to disable this
-//If it's the shuttle, it should do damage
-//If fully repaired and moves at least once, the broken hitboxes will respawn according to multitile.dm
-/obj/vehicle/multitile/hitbox/cm_armored/Destroy()
- var/obj/vehicle/multitile/root/cm_armored/C = root
- C?.take_damage_type(1000000, "abstract")
- return ..()
-
-//Tramplin' time, but other than that identical
-/obj/vehicle/multitile/hitbox/cm_armored/Bump(atom/A)
- . = ..()
- var/facing = get_dir(src, A)
- var/turf/temp = loc
- var/turf/T = loc
- A.tank_collision(src, facing, T, temp)
- if(isliving(A))
- log_attack("[get_driver()] drove over [A] with [root]")
-
-
-/obj/vehicle/multitile/hitbox/cm_armored/proc/get_driver()
- return "Someone"
-
-/atom/proc/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- return
-
-/mob/living/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- if(stat == DEAD) //We don't care about the dead
- return
- if(loc == C.loc) // treaded over.
- ParalyzeNoChain(2 SECONDS)
- var/target_dir = REVERSE_DIR(C.dir)
- temp = get_step(C.loc, target_dir)
- T = temp
- target_dir = REVERSE_DIR(C.dir)
- T = get_step(T, target_dir)
- face_atom(T)
- throw_at(T, 3, 2, C, 1)
- apply_damage(randfloat(5, 7.5), BRUTE, blocked = MELEE)
- return
- if(!lying_angle)
- temp = get_step(T, facing)
- T = temp
- T = get_step(T, pick(GLOB.cardinals))
- if(mob_size == MOB_SIZE_BIG)
- throw_at(T, 3, 2, C, 0)
- else
- throw_at(T, 3, 2, C, 1)
- ParalyzeNoChain(2 SECONDS)
- apply_damage(rand(10, 15), BRUTE, blocked = MELEE)
- visible_message(span_danger("[C] bumps into [src], throwing [p_them()] away!"), span_danger("[C] violently bumps into you!"))
- var/obj/vehicle/multitile/root/cm_armored/CA = C.root
- var/list/slots = CA.get_activatable_hardpoints()
- for(var/slot in slots)
- var/obj/item/hardpoint/H = CA.hardpoints[slot]
- H?.livingmob_interact(src)
-
-/mob/living/carbon/xenomorph/queen/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- if(lying_angle || loc == C.loc)
- return ..()
- temp = get_step(T, facing)
- T = temp
- T = get_step(T, pick(GLOB.cardinals))
- throw_at(T, 2, 2, C, 0)
- visible_message(span_danger("[C] bumps into [src], pushing [p_them()] away!"), span_danger("[C] bumps into you!"))
-
-/mob/living/carbon/xenomorph/crusher/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- if(lying_angle || loc == C.loc)
- return ..()
- temp = get_step(T, facing)
- T = temp
- T = get_step(T, pick(GLOB.cardinals))
- throw_at(T, 2, 2, C, 0)
- visible_message(span_danger("[C] bumps into [src], pushing [p_them()] away!"), span_danger("[C] bumps into you!"))
-
-/mob/living/carbon/xenomorph/larva/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- if(loc == C.loc) // treaded over.
- ParalyzeNoChain(2 SECONDS)
- apply_damage(randfloat(5, 7.5), BRUTE, blocked = MELEE)
- return
- var/obj/vehicle/multitile/root/cm_armored/CA = C.root
- var/list/slots = CA.get_activatable_hardpoints()
- for(var/slot in slots)
- var/obj/item/hardpoint/H = CA.hardpoints[slot]
- H?.livingmob_interact(src)
-
-/turf/closed/wall/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- var/obj/vehicle/multitile/root/cm_armored/tank/CA = C.root
- var/damage = 30
- var/tank_damage = 2
-
- if(facing == CA.old_dir && istype(CA.hardpoints[HDPT_ARMOR], /obj/item/hardpoint/armor/snowplow) ) //Snowplow eliminates collision damage, and doubles damage dealt if we're facing the thing we're crushing
- var/obj/item/hardpoint/armor/snowplow/SP = CA.hardpoints[HDPT_ARMOR]
- if(SP.obj_integrity)
- damage = 45
- tank_damage = 1
-
- take_damage(damage)
- CA.take_damage_type(tank_damage, "blunt", src)
- if(world.time > C.lastsound + 1 SECONDS)
- playsound(src, 'sound/effects/metal_crash.ogg', 35)
- C.lastsound = world.time
-
-/obj/machinery/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- var/obj/vehicle/multitile/root/cm_armored/tank/CA = C.root
- var/damage = 30
- var/tank_damage = 2
-
- if(facing == CA.old_dir && istype(CA.hardpoints[HDPT_ARMOR], /obj/item/hardpoint/armor/snowplow) ) //Snowplow eliminates collision damage, and doubles damage dealt if we're facing the thing we're crushing
- var/obj/item/hardpoint/armor/snowplow/SP = CA.hardpoints[HDPT_ARMOR]
- if(SP.obj_integrity)
- damage = 60
- tank_damage = 0
-
- take_damage(damage)
- CA.take_damage_type(tank_damage, "blunt", src)
- if(world.time > C.lastsound + 1 SECONDS)
- visible_message(span_danger("[CA] rams into \the [src]!"))
- playsound(src, 'sound/effects/metal_crash.ogg', 35)
- C.lastsound = world.time
-
-/obj/structure/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- var/obj/vehicle/multitile/root/cm_armored/tank/CA = C.root
- var/damage = 30
- var/tank_damage = 2
-
- if(facing == CA.old_dir && istype(CA.hardpoints[HDPT_ARMOR], /obj/item/hardpoint/armor/snowplow) ) //Snowplow eliminates collision damage, and doubles damage dealt if we're facing the thing we're crushing
- var/obj/item/hardpoint/armor/snowplow/SP = CA.hardpoints[HDPT_ARMOR]
- if(SP.obj_integrity)
- damage = 60
- tank_damage = 0
-
- take_damage(damage)
- CA.take_damage_type(tank_damage, "blunt", src)
- if(world.time > C.lastsound + 1 SECONDS)
- visible_message(span_danger("[CA] crushes \the [src]!"))
- playsound(src, 'sound/effects/metal_crash.ogg', 35)
- C.lastsound = world.time
-
-/obj/alien/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- take_damage(40)
-
-/obj/alien/weeds/tank_collision(obj/vehicle/multitile/hitbox/cm_armored/C, facing, turf/T, turf/temp)
- return
-
-/obj/vehicle/multitile/hitbox/cm_armored/Move(atom/A, direction)
-
- for(var/mob/living/M in get_turf(src))
- M.tank_collision(src)
-
- . = ..()
-
- if(.)
- for(var/mob/living/M in get_turf(A))
- M.tank_collision(src)
-
-//Can't hit yourself with your own bullet
-/obj/vehicle/multitile/hitbox/cm_armored/projectile_hit(obj/projectile/proj)
- if(proj.firer == root) //Don't hit our own hitboxes
- return FALSE
-
- return ..()
-
-/obj/vehicle/multitile/hitbox/cm_armored/ex_act(severity)
- return root.ex_act(severity)
-
-/obj/vehicle/multitile/hitbox/cm_armored/attackby(obj/item/I, mob/user, params)
- return root.attackby(I, user, params)
-
-/obj/vehicle/multitile/hitbox/cm_armored/attack_alien(mob/living/carbon/xenomorph/X, damage_amount = X.xeno_caste.melee_damage, damage_type = BRUTE, damage_flag = "", effects = TRUE, armor_penetration = X.xeno_caste.melee_ap, isrightclick = FALSE)
- return root.attack_alien(X, damage_amount)
-
-/obj/vehicle/multitile/hitbox/cm_armored/effect_smoke(obj/effect/particle_effect/smoke/S)
- . = ..()
- if(!.)
- return
- if(CHECK_BITFIELD(S.smoke_traits, SMOKE_XENO_ACID))
- var/obj/vehicle/multitile/root/cm_armored/T = root
- T.take_damage_type(30, "acid")
-
-//A bit icky, but basically if you're adjacent to the tank hitbox, you are then adjacent to the root object
-/obj/vehicle/multitile/root/cm_armored/Adjacent(atom/A)
- for(var/i in linked_objs)
- var/obj/vehicle/multitile/hitbox/cm_armored/H = linked_objs[i]
- if(!H)
- continue
- if(get_dist(H, A) <= 1)
- return TRUE //Using get_dist() to avoid hidden code that recurs infinitely here
- return ..()
-
-//Returns the ratio of damage to take, just a housekeeping thing
-/obj/vehicle/multitile/root/cm_armored/proc/get_dmg_multi(type)
- if(!dmg_multipliers.Find(type))
- return 0
- return dmg_multipliers[type] * dmg_multipliers["all"]
-
-//Generic proc for taking damage
-//ALWAYS USE THIS WHEN INFLICTING DAMAGE TO THE VEHICLES
-/obj/vehicle/multitile/root/cm_armored/proc/take_damage_type(damage, type, atom/attacker)
- for(var/i in hardpoints)
- var/obj/item/hardpoint/HP = hardpoints[i]
- if(HP)
- HP.take_damage(HP.obj_integrity - damage * dmg_distribs[i] * get_dmg_multi(type))
-
- healthcheck()
-
- if(istype(attacker, /mob))
- var/mob/M = attacker
- log_attack("[src] took [damage] [type] damage from [M] ([M.client ? M.client.ckey : "disconnected"]).")
- else
- log_attack("[src] took [damage] [type] damage from [attacker].")
-
-/obj/vehicle/multitile/root/cm_armored/projectile_hit(obj/projectile/proj)
- if(proj.firer == src) //Don't hit ourself.
- return FALSE
-
- return ..()
-
-
-//severity 1.0 explosions never really happen so we're gonna follow everyone else's example
-/obj/vehicle/multitile/root/cm_armored/ex_act(severity)
-
- switch(severity)
- if(EXPLODE_DEVASTATE)
- take_damage(rand(250, 350)) //Devastation level explosives are anti-tank and do real damage.
-
- if(EXPLODE_HEAVY)
- take_damage(rand(30, 40)) //Heavy explosions do some damage, but are largely deferred by the armour/bulk.
-
-//Honestly copies some code from the Xeno files, just handling some special cases
-/obj/vehicle/multitile/root/cm_armored/attack_alien(mob/living/carbon/xenomorph/M, damage_amount = M.xeno_caste.melee_damage, damage_type = BRUTE, damage_flag = "", effects = TRUE, armor_penetration = 0, isrightclick = FALSE)
-
- if(M.loc == entrance.loc && M.a_intent == INTENT_HELP)
- handle_player_entrance(M) //will call the get out of tank proc on its own
- return
-
- var/damage = damage_amount
-
- //Somehow we will deal no damage on this attack
- if(!damage)
- playsound(M.loc, 'sound/weapons/alien_claw_swipe.ogg', 25, 1)
- M.do_attack_animation(src)
- M.visible_message(span_danger("\The [M] lunges at [src]!"), \
- span_danger("We lunge at [src]!"))
- return FALSE
-
- M.do_attack_animation(src, ATTACK_EFFECT_CLAW)
- playsound(loc, "alien_claw_metal", 25, 1)
-
- M.visible_message(span_danger("\The [M] slashes [src]!"), \
- span_danger("We slash [src]!"))
-
- take_damage_type(damage * ( (isxenoravager(M)) ? 2 : 1 ), "slash", M) //Ravs do a bitchin double damage
- return ..()
-
-//Special case for entering the vehicle without using the verb
-/obj/vehicle/multitile/root/cm_armored/attack_hand(mob/living/user)
- . = ..()
- if(.)
- return
- if(user.loc == entrance.loc)
- handle_player_entrance(user)
- return
-
-
-/obj/vehicle/multitile/root/cm_armored/Entered(atom/movable/A)
- if(istype(A, /obj) && !istype(A, /obj/item/ammo_magazine/tank) && !istype(A, /obj/item/hardpoint))
- A.forceMove(loc)
- return
-
- return ..()
-
-
-//Redistributes damage ratios based off of what things are attached (no armor means the armor doesn't mitigate any damage)
-/obj/vehicle/multitile/root/cm_armored/proc/update_damage_distribs()
- dmg_distribs = GLOB.armorvic_dmg_distributions.Copy() //Assume full installs
- for(var/slot in hardpoints)
- var/obj/item/hardpoint/HP = hardpoints[slot]
- if(!HP)
- dmg_distribs[slot] = 0.0 //Remove empty slots' damage mitigation
- var/acc = 0
- for(var/slot in dmg_distribs)
- var/ratio = dmg_distribs[slot]
- acc += ratio //Get total current ratio applications
- if(acc == 0)
- return
- for(var/slot in dmg_distribs)
- var/ratio = dmg_distribs[slot]
- dmg_distribs[slot] = ratio/acc //Redistribute according to previous ratios for full damage taking, but ignoring empty slots
-
-//Special cases abound, handled below or in subclasses
-/obj/vehicle/multitile/root/cm_armored/attackby(obj/item/O, mob/user)
-
- if(istype(O, /obj/item/hardpoint)) //Are we trying to install stuff?
- var/obj/item/hardpoint/HP = O
- install_hardpoint(HP, user)
-
- else if(istype(O, /obj/item/ammo_magazine)) //Are we trying to reload?
- var/obj/item/ammo_magazine/AM = O
- handle_ammomag_attackby(AM, user)
-
- else if(iswelder(O) || iswrench(O)) //Are we trying to repair stuff?
- handle_hardpoint_repair(O, user)
- update_damage_distribs()
-
- else if(iscrowbar(O)) //Are we trying to remove stuff?
- uninstall_hardpoint(O, user)
-
- else
- . = ..()
- if(!(O.flags_item & NOBLUDGEON))
- take_damage_type(O.force * 0.05, "blunt", user) //Melee weapons from people do very little damage
-
-
-/obj/vehicle/multitile/root/cm_armored/proc/handle_hardpoint_repair(obj/item/O, mob/user)
-
- //Need to the what the hell you're doing
- if(user.skills.getRating(SKILL_ENGINEER) < SKILL_ENGINEER_MASTER)
- user.visible_message(span_notice("[user] fumbles around figuring out what to do with [O] on the [src]."),
- span_notice("You fumble around figuring out what to do with [O] on the [src]."))
- var/fumbling_time = 5 SECONDS * (SKILL_ENGINEER_MASTER - user.skills.getRating(SKILL_ENGINEER))
- if(!do_after(user, fumbling_time, NONE, src, BUSY_ICON_UNSKILLED))
- return
-
- //Pick what to repair
- var/slot = tgui_input_list(user, "Select a slot to try and repair", null, hardpoints) //maybe tgui alert ?
- var/obj/item/I = user.get_active_held_item()
- if(!Adjacent(user) || (!iswelder(I) && !iswrench(I)))
- return
-
- var/obj/item/hardpoint/old = hardpoints[slot] //Is there something there already?
-
- if(!old)
- to_chat(user, span_warning("There is nothing installed on that slot."))
- return
-
- if(old.obj_integrity >= old.max_integrity)
- to_chat(user, span_notice("\the [old] is already in perfect conditions."))
- return
-
- //Determine how many 3 second intervals to wait and if you have the right tool
- var/num_delays = 6
- switch(slot)
- if(HDPT_PRIMARY)
- num_delays = 5
- if(!iswelder(I))
- to_chat(user, span_warning("That's the wrong tool. Use a welder."))
- return
- var/obj/item/tool/weldingtool/WT = I
- if(!WT.isOn())
- to_chat(user, span_warning("You need to light your [WT] first."))
- return
-
- if(HDPT_SECDGUN)
- num_delays = 3
- if(!iswrench(I))
- to_chat(user, span_warning("That's the wrong tool. Use a wrench."))
- return
-
- if(HDPT_SUPPORT)
- num_delays = 2
- if(!iswrench(I))
- to_chat(user, span_warning("That's the wrong tool. Use a wrench."))
- return
-
- if(HDPT_ARMOR)
- num_delays = 10
- if(!iswelder(I))
- to_chat(user, span_warning("That's the wrong tool. Use a welder."))
- return
- var/obj/item/tool/weldingtool/WT = I
- if(!WT.isOn())
- to_chat(user, span_warning("You need to light your [WT] first."))
- return
-
- if(HDPT_TREADS)
- if(!iswelder(I))
- to_chat(user, span_warning("That's the wrong tool. Use a welder."))
- return
- var/obj/item/tool/weldingtool/WT = I
- if(!WT.isOn())
- to_chat(user, span_warning("You need to light your [WT] first."))
- return
- WT.remove_fuel(num_delays, user)
-
- user.visible_message(span_notice("[user] starts repairing the [slot] slot on [src]."),
- span_notice("You start repairing the [slot] slot on the [src]."))
-
- if(!do_after(user, 30 * num_delays, NONE, src, BUSY_ICON_BUILD, extra_checks = iswelder(O) ? CALLBACK(O, TYPE_PROC_REF(/obj/item/tool/weldingtool, isOn)) : null))
- user.visible_message(span_notice("[user] stops repairing the [slot] slot on [src]."),
- span_notice("You stop repairing the [slot] slot on the [src]."))
- return
-
- if(iswelder(O))
- var/obj/item/tool/weldingtool/WT = O
- WT.remove_fuel(num_delays, user)
-
- user.visible_message(span_notice("[user] repairs the [slot] slot on the [src]."),
- span_notice("You repair the [slot] slot on [src]."))
-
- old.repair_damage(old.max_integrity) //We repaired it, good job
- old.apply_buff()
-
- update_icon()
-
-//Relaoding stuff, pretty bare-bones and basic
-/obj/vehicle/multitile/root/cm_armored/proc/handle_ammomag_attackby(obj/item/ammo_magazine/AM, mob/user)
-
- //No skill checks for reloading
- //Maybe I should delineate levels of skill for reloading, installation, and repairs?
- //That would make it easier to differentiate between the two for skills
- //Instead of using MT skills for these procs and TC skills for operation
- //Oh but wait then the MTs would be able to drive fuck that
- var/slot = tgui_input_list(user, "Select a slot to try and refill", null, hardpoints)
- if(!Adjacent(user) || user.get_active_held_item() != AM)
- return
- var/obj/item/hardpoint/HP = hardpoints[slot]
-
- if(!HP)
- to_chat(user, span_warning("There is nothing installed on that slot."))
- return
-
- HP.try_add_clip(AM, user)
-
-//Putting on hardpoints
-//Similar to repairing stuff, down to the time delay
-/obj/vehicle/multitile/root/cm_armored/proc/install_hardpoint(obj/item/hardpoint/HP, mob/user)
-
- if(user.skills.getRating(SKILL_ENGINEER) < SKILL_ENGINEER_MASTER)
- user.visible_message(span_notice("[user] fumbles around figuring out what to do with [HP] on the [src]."),
- span_notice("You fumble around figuring out what to do with [HP] on the [src]."))
- var/fumbling_time = 5 SECONDS * ( SKILL_ENGINEER_MASTER - user.skills.getRating(SKILL_ENGINEER) )
- if(!do_after(user, fumbling_time, NONE, src, BUSY_ICON_UNSKILLED))
- return
-
- var/obj/item/hardpoint/occupied = hardpoints[HP.slot]
-
- if(occupied)
- to_chat(user, span_warning("Remove the previous hardpoint module first."))
- return
-
- user.visible_message(span_notice("[user] begins installing [HP] on the [HP.slot] hardpoint slot on the [src]."),
- span_notice("You begin installing [HP] on the [HP.slot] hardpoint slot on the [src]."))
-
- var/num_delays = 1
-
- switch(HP.slot)
- if(HDPT_PRIMARY)
- num_delays = 5
- if(HDPT_SECDGUN)
- num_delays = 3
- if(HDPT_SUPPORT)
- num_delays = 2
- if(HDPT_ARMOR)
- num_delays = 10
- if(HDPT_TREADS)
- num_delays = 7
-
- if(!do_after(user, 30 * num_delays, NONE, src, BUSY_ICON_BUILD))
- user.visible_message(span_warning("[user] stops installing \the [HP] on [src]."), span_warning("You stop installing \the [HP] on [src]."))
- return
-
- if(occupied)
- return
-
- user.visible_message(span_notice("[user] installs \the [HP] on [src]."), span_notice("You install \the [HP] on [src]."))
-
- user.temporarilyRemoveItemFromInventory(HP, 0)
-
- add_hardpoint(HP, user)
-
-//User-orientated proc for taking of hardpoints
-//Again, similar to the above ones
-/obj/vehicle/multitile/root/cm_armored/proc/uninstall_hardpoint(obj/item/O, mob/user)
-
- if(user.skills.getRating(SKILL_ENGINEER) < SKILL_ENGINEER_MASTER)
- user.visible_message(span_notice("[user] fumbles around figuring out what to do with [O] on the [src]."),
- span_notice("You fumble around figuring out what to do with [O] on the [src]."))
- var/fumbling_time = 5 SECONDS * ( SKILL_ENGINEER_MASTER - user.skills.getRating(SKILL_ENGINEER) )
- if(!do_after(user, fumbling_time, NONE, src, BUSY_ICON_UNSKILLED))
- return
-
- var/slot = tgui_input_list(user, "Select a slot to try and remove", null, hardpoints)
- if(!Adjacent(user) || !iscrowbar(user.get_active_held_item()))
- return
-
- var/obj/item/hardpoint/old = hardpoints[slot]
-
- if(!old)
- to_chat(user, span_warning("There is nothing installed there."))
- return
-
- user.visible_message(span_notice("[user] begins removing [old] on the [old.slot] hardpoint slot on [src]."),
- span_notice("You begin removing [old] on the [old.slot] hardpoint slot on [src]."))
-
- var/num_delays = 1
-
- switch(old.slot)
- if(HDPT_PRIMARY)
- num_delays = 5
- if(HDPT_SECDGUN)
- num_delays = 3
- if(HDPT_SUPPORT)
- num_delays = 2
- if(HDPT_ARMOR)
- num_delays = 10
- if(HDPT_TREADS)
- num_delays = 7
-
- if(!do_after(user, 30 * num_delays, NONE, src, BUSY_ICON_BUILD))
- user.visible_message(span_warning("[user] stops removing \the [old] on [src]."), span_warning("You stop removing \the [old] on [src]."))
- return
- if(QDELETED(old) || old != hardpoints[slot])
- return
-
- user.visible_message(span_notice("[user] removes \the [old] on [src]."), span_notice("You remove \the [old] on [src]."))
-
- remove_hardpoint(old, user)
-
-//General proc for putting on hardpoints
-//ALWAYS CALL THIS WHEN ATTACHING HARDPOINTS
-/obj/vehicle/multitile/root/cm_armored/proc/add_hardpoint(obj/item/hardpoint/HP, mob/user)
- if(!istype(HP))
- return
- HP.owner = src
- if(HP.obj_integrity)
- HP.apply_buff()
- HP.forceMove(src)
-
- hardpoints[HP.slot] = HP
- update_damage_distribs()
- update_icon()
-
-//General proc for taking off hardpoints
-//ALWAYS CALL THIS WHEN REMOVING HARDPOINTS
-/obj/vehicle/multitile/root/cm_armored/proc/remove_hardpoint(obj/item/hardpoint/old, mob/user)
- old.forceMove(user ? user.loc : entrance.loc)
- old.remove_buff()
- old.owner = null
-
- hardpoints[old.slot] = null
- update_damage_distribs()
- update_icon()
-
-
-
-/obj/vehicle/multitile/root/cm_armored/contents_explosion(severity)
- return
diff --git a/code/modules/vehicles/multitile/hardpoints.dm b/code/modules/vehicles/multitile/hardpoints.dm
deleted file mode 100644
index 82df22284f39c..0000000000000
--- a/code/modules/vehicles/multitile/hardpoints.dm
+++ /dev/null
@@ -1,999 +0,0 @@
-/*
-All of the hardpoints, for the tank or other
-Currently only has the tank hardpoints
-*/
-
-/obj/item/hardpoint
-
- var/slot //What slot do we attach to?
- var/obj/vehicle/multitile/root/cm_armored/owner //Who do we work for?
-
- icon = 'icons/obj/vehicles/hardpoint_modules.dmi'
- icon_state = "tires" //Placeholder
-
- max_integrity = 100
- w_class = WEIGHT_CLASS_GIGANTIC
-
- var/obj/item/ammo_magazine/tank/ammo
- //If we use ammo, put it here
- var/obj/item/ammo_magazine/tank/starter_ammo
-
- //Strings, used to get the overlay for the armored vic
- var/disp_icon //This also differentiates tank vs apc vs other
- var/disp_icon_state
-
- var/next_use = 0
- var/is_activatable = FALSE
- var/max_angle = 180
- var/point_cost = 0
-
- var/list/backup_clips = list()
- var/max_clips = 1 //1 so they can reload their backups and actually reload once
- var/buyable = TRUE
-
-/obj/item/hardpoint/Initialize(mapload)
- . = ..()
- if(starter_ammo)
- ammo = new starter_ammo
-
-/obj/item/hardpoint/examine(mob/user)
- . = ..()
- var/status = obj_integrity <= 0.1 ? "broken" : "functional"
- var/span_class = obj_integrity <= 0.1 ? "
" : ""
- if((user.skills.getRating(SKILL_ENGINEER) >= SKILL_ENGINEER_METAL) || isobserver(user))
- switch(PERCENT(obj_integrity / max_integrity))
- if(0.1 to 33)
- status = "heavily damaged"
- span_class = ""
- if(33.1 to 66)
- status = "damaged"
- span_class = ""
- if(66.1 to 90)
- status = "slighty damaged"
- if(90.1 to 100)
- status = "intact"
- to_chat(user, "[span_class]It's [status].")
-
-/obj/item/hardpoint/attackby(obj/item/W, mob/user)
- if(istype(W, /obj/item/ammo_magazine/tank))
- try_add_clip(W, user)
- return
- if(!iswelder(W) && !iswrench(W))
- return ..()
- if(obj_integrity >= max_integrity)
- to_chat(user, span_notice("[src] is already in perfect conditions."))
- return
- var/repair_delays = 6
- var/obj/item/tool/repair_tool = /obj/item/tool/weldingtool
- switch(slot)
- if(HDPT_PRIMARY)
- repair_delays = 5
- if(HDPT_SECDGUN)
- repair_tool = /obj/item/tool/wrench
- repair_delays = 3
- if(HDPT_SUPPORT)
- repair_tool = /obj/item/tool/wrench
- repair_delays = 2
- if(HDPT_ARMOR)
- repair_delays = 10
- var/obj/item/tool/weldingtool/WT = iswelder(W) ? W : null
- if(!istype(W, repair_tool))
- to_chat(user, span_warning("That's the wrong tool. Use a [WT ? "wrench" : "welder"]."))
- return
- if(WT && !WT.isOn())
- to_chat(user, span_warning("You need to light your [WT] first."))
- return
- user.visible_message(span_notice("[user] starts repairing [src]."),
- span_notice("You start repairing [src]."))
- if(!do_after(user, 3 SECONDS * repair_delays, NONE, src, BUSY_ICON_BUILD))
- user.visible_message(span_notice("[user] stops repairing [src]."),
- span_notice("You stop repairing [src]."))
- return
- if(WT)
- if(!WT.isOn())
- return
- WT.remove_fuel(repair_delays, user)
- user.visible_message(span_notice("[user] finishes repairing [src]."),
- span_notice("You finish repairing [src]."))
- repair_damage(max_integrity)
-
-//Called on attaching, for weapons sets the actual cooldowns
-/obj/item/hardpoint/proc/apply_buff()
- return
-
-//Called when removing, resets cooldown lengths, move delay, etc
-/obj/item/hardpoint/proc/remove_buff()
- return
-
-//Called when you want to activate the hardpoint, such as a gun
-//This can also be used for some type of temporary buff, up to you
-/obj/item/hardpoint/proc/active_effect(atom/A)
- return
-
-/obj/item/hardpoint/proc/deactivate()
- return
-
-/obj/item/hardpoint/proc/livingmob_interact(mob/living/M)
- return
-
-//If our cooldown has elapsed
-/obj/item/hardpoint/proc/is_ready()
- if(world.time < next_use)
- to_chat(usr, span_warning("This module is not ready to be used yet."))
- return FALSE
- if(!obj_integrity)
- to_chat(usr, span_warning("This module is too broken to be used."))
- return FALSE
- return TRUE
-
-/obj/item/hardpoint/proc/try_add_clip(obj/item/ammo_magazine/tank/A, mob/user)
-
- if(!max_clips)
- to_chat(user, span_warning("This module does not have room for additional ammo."))
- return FALSE
- else if(length(backup_clips) >= max_clips)
- to_chat(user, span_warning("The reloader is full."))
- return FALSE
- else if(!istype(A, starter_ammo))
- to_chat(user, span_warning("That is the wrong ammo type."))
- return FALSE
-
- to_chat(user, span_notice("You start loading [A] in [src]."))
-
- var/atom/target = owner ? owner : src
-
- if(!do_after(user, 10, NONE, target) || QDELETED(src))
- to_chat(user, span_warning("Something interrupted you while loading [src]."))
- return FALSE
-
- user.temporarilyRemoveItemFromInventory(A, FALSE)
- user.visible_message(span_notice("[user] loads [A] in [src]"),
- span_notice("You finish loading [A] in \the [src]."), null, 3)
- backup_clips += A
- playsound(user.loc, 'sound/weapons/guns/interact/minigun_cocked.ogg', 25)
- return TRUE
-
-//Returns the image object to overlay onto the root object
-/obj/item/hardpoint/proc/get_icon_image(x_offset, y_offset, new_dir)
-
- var/icon_suffix = "NS"
- var/icon_state_suffix = "0"
-
- if(new_dir in list(NORTH, SOUTH))
- icon_suffix = "NS"
- else if(new_dir in list(EAST, WEST))
- icon_suffix = "EW"
-
- if(!obj_integrity)
- icon_state_suffix = "1"
-
- return image(icon = "[disp_icon]_[icon_suffix]", icon_state = "[disp_icon_state]_[icon_state_suffix]", pixel_x = x_offset, pixel_y = y_offset)
-
-/obj/item/hardpoint/proc/firing_arc(atom/A)
- var/turf/T = get_turf(A)
- if(!T || !owner)
- return FALSE
- var/dx = T.x - owner.x
- var/dy = T.y - owner.y
- var/deg = 0
- switch(owner.dir)
- if(EAST) deg = 0
- if(NORTH) deg = -90
- if(WEST) deg = -180
- if(SOUTH) deg = -270
-
- var/nx = dx * cos(deg) - dy * sin(deg)
- var/ny = dx * sin(deg) + dy * cos(deg)
- if(nx == 0)
- return max_angle >= 90
- var/angle = arctan(ny/nx)
- if(nx < 0)
- angle += 180
- return abs(angle) <= max_angle
-
-//Delineating between slots
-/obj/item/hardpoint/primary
- slot = HDPT_PRIMARY
- is_activatable = TRUE
-
-/obj/item/hardpoint/secondary
- slot = HDPT_SECDGUN
- is_activatable = TRUE
-
-/obj/item/hardpoint/support
- slot = HDPT_SUPPORT
-
-/obj/item/hardpoint/armor
- slot = HDPT_ARMOR
- max_clips = 0
-
-/obj/item/hardpoint/treads
- slot = HDPT_TREADS
- max_clips = 0
- gender = PLURAL
-
-////////////////////
-// PRIMARY SLOTS // START
-////////////////////
-
-/obj/item/hardpoint/primary/cannon
- name = "LTB Cannon"
- desc = "A primary cannon for tanks that shoots explosive rounds"
-
- max_integrity = 500
- point_cost = 100
-
- icon_state = "ltb_cannon"
-
- disp_icon = "tank"
- disp_icon_state = "ltb_cannon"
-
- starter_ammo = /obj/item/ammo_magazine/tank/ltb_cannon
- max_clips = 3
- max_angle = 45
-
-/obj/item/hardpoint/primary/cannon/broken
- obj_integrity = 0
- buyable = FALSE
-
-/obj/item/hardpoint/primary/cannon/apply_buff()
- owner.internal_cooldowns["primary"] = 200
- owner.accuracies["primary"] = 0.97
-
-/obj/item/hardpoint/primary/cannon/active_effect(atom/A)
-
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
-
- next_use = world.time + owner.internal_cooldowns["primary"] * owner.misc_ratios["prim_cool"]
- var/obj/item/hardpoint/secondary/towlauncher/HP = owner.hardpoints[HDPT_SECDGUN]
- if(istype(HP))
- HP.next_use = world.time + owner.internal_cooldowns["secondary"] * owner.misc_ratios["secd_cool"]
-
- var/delay = 5
- var/turf/T = get_turf(A)
- if(!T)
- return
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- var/obj/effect/overlay/temp/tank_laser/TL
- if(C.is_zoomed)
- delay = 20
- TL = new /obj/effect/overlay/temp/tank_laser (T)
-
- to_chat(usr, span_warning("Preparing to fire... keep the tank still for [delay * 0.1] seconds."))
-
- if(!do_after(usr, delay, IGNORE_HELD_ITEM, src) || QDELETED(owner))
- to_chat(usr, span_warning("The [name]'s firing was interrupted."))
- qdel(TL)
-
- return
-
- qdel(TL)
-
- if(!prob(owner.accuracies["primary"] * 100 * owner.misc_ratios["prim_acc"]))
- T = get_step(T, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- log_bomber(usr, "fired", src)
- P.fire_at(T, owner, src, P.ammo.max_range, P.ammo.shell_speed)
- playsound(get_turf(src), pick('sound/weapons/guns/fire/tank_cannon1.ogg', 'sound/weapons/guns/fire/tank_cannon2.ogg'), 60, 1)
- ammo.current_rounds--
-
-/obj/item/hardpoint/primary/minigun
- name = "LTAA-AP Minigun"
- desc = "A primary weapon for tanks that spews bullets"
-
- max_integrity = 350
- point_cost = 100
-
- icon_state = "ltaaap_minigun"
-
- disp_icon = "tank"
- disp_icon_state = "ltaaap_minigun"
-
- starter_ammo = /obj/item/ammo_magazine/tank/ltaaap_minigun
- max_angle = 45
-
- //Miniguns don't use a conventional cooldown
- //If you fire quickly enough, the cooldown decreases according to chain_delays
- //If you fire too slowly, you slowly slow back down
- //Also, different sounds play and it sounds sick, thanks Rahlzel
- var/chained = 0 //how many quick succession shots we've fired
- var/list/chain_delays = list(4, 4, 3, 3, 2, 2, 2, 1, 1) //the different cooldowns in deciseconds, sequentially
-
- //MAIN PROBLEM WITH THIS IMPLEMENTATION OF DELAYS:
- //If you spin all the way up and then stop firing, your chained shots will only decrease by 1
- //TODO: Implement a rolling average for seconds per shot that determines chain length without being slow or buggy
- //You'd probably have to normalize it between the length of the list and the actual ROF
- //But you don't want to map it below a certain point probably since seconds per shot would go to infinity
-
- //So, I came back to this and changed it by adding a fixed reset at 1.5 seconds or later, which seems reasonable
- //Now the cutoff is a little abrupt, but at least it exists. --MadSnailDisease
-
-/obj/item/hardpoint/primary/minigun/apply_buff()
- owner.internal_cooldowns["primary"] = 2 //will be overridden, please ignore
- owner.accuracies["primary"] = 0.33
-
-/obj/item/hardpoint/primary/minigun/active_effect(atom/A)
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
- var/S = 'sound/weapons/guns/fire/tank_minigun_start.ogg'
- if(world.time - next_use <= 5)
- chained++ //minigun spins up, minigun spins down
- S = 'sound/weapons/guns/fire/tank_minigun_loop.ogg'
- else if(world.time - next_use >= 15) //Too long of a delay, they restart the chain
- chained = 1
- else //In between 5 and 15 it slows down but doesn't stop
- chained--
- S = 'sound/weapons/guns/fire/tank_minigun_stop.ogg'
- if(chained <= 0)
- chained = 1
-
- next_use = world.time + (chained > length(chain_delays) ? 0.5 : chain_delays[chained]) * owner.misc_ratios["prim_cool"]
- if(!prob(owner.accuracies["primary"] * 100 * owner.misc_ratios["prim_acc"]))
- A = get_step(A, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- P.fire_at(A, owner, src, P.ammo.max_range, P.ammo.shell_speed)
-
- playsound(get_turf(src), S, 60)
- ammo.current_rounds--
-
-////////////////////
-// PRIMARY SLOTS // END
-////////////////////
-
-/////////////////////
-// SECONDARY SLOTS // START
-/////////////////////
-
-/obj/item/hardpoint/secondary/flamer
- name = "Secondary Flamer Unit"
- desc = "A secondary weapon for tanks that shoots flames"
-
- max_integrity = 300
- point_cost = 100
-
- icon_state = "flamer"
-
- disp_icon = "tank"
- disp_icon_state = "flamer"
-
- starter_ammo = /obj/item/ammo_magazine/tank/flamer
- max_angle = 90
-
-/obj/item/hardpoint/secondary/flamer/apply_buff()
- owner.internal_cooldowns["secondary"] = 20
- owner.accuracies["secondary"] = 0.5
-
-/obj/item/hardpoint/secondary/flamer/active_effect(atom/A)
-
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
-
- next_use = world.time + owner.internal_cooldowns["secondary"] * owner.misc_ratios["secd_cool"]
- if(!prob(owner.accuracies["secondary"] * 100 * owner.misc_ratios["secd_acc"]))
- A = get_step(A, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- P.fire_at(A, owner, src, P.ammo.max_range, P.ammo.shell_speed)
- playsound(get_turf(src), 'sound/weapons/guns/fire/tank_flamethrower.ogg', 60, 1)
- ammo.current_rounds--
-
-/obj/item/hardpoint/secondary/towlauncher
- name = "TOW Launcher"
- desc = "A secondary weapon for tanks that shoots rockets"
-
- max_integrity = 500
- point_cost = 100
-
- icon_state = "tow_launcher"
-
- disp_icon = "tank"
- disp_icon_state = "towlauncher"
-
- starter_ammo = /obj/item/ammo_magazine/tank/towlauncher
- max_clips = 1
- max_angle = 90
-
-/obj/item/hardpoint/secondary/towlauncher/apply_buff()
- owner.internal_cooldowns["secondary"] = 150
- owner.accuracies["secondary"] = 0.8
-
-/obj/item/hardpoint/secondary/towlauncher/active_effect(atom/A)
-
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
-
- var/delay = 3
- var/turf/T = get_turf(A)
- if(!T)
- return
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- var/obj/effect/overlay/temp/tank_laser/TL
- if(C.is_zoomed)
- delay = 15
- TL = new /obj/effect/overlay/temp/tank_laser (T)
-
- to_chat(usr, span_warning("Preparing to fire... keep the tank still for [delay * 0.1] seconds."))
-
- if(!do_after(usr, delay, IGNORE_HELD_ITEM, src) || QDELETED(owner))
- to_chat(usr, span_warning("The [name]'s firing was interrupted."))
- qdel(TL)
- return
-
- qdel(TL)
-
- next_use = world.time + owner.internal_cooldowns["secondary"] * owner.misc_ratios["secd_cool"]
- var/obj/item/hardpoint/primary/cannon/HP = owner.hardpoints[HDPT_PRIMARY]
- if(istype(HP))
- HP.next_use = world.time + owner.internal_cooldowns["primary"] * owner.misc_ratios["prim_cool"]
-
- if(!prob(owner.accuracies["secondary"] * 100 * owner.misc_ratios["secd_acc"]))
- T = get_step(T, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- log_bomber(usr, "fired", src)
- P.fire_at(T, owner, src, P.ammo.max_range, P.ammo.shell_speed)
- ammo.current_rounds--
-
-/obj/item/hardpoint/secondary/m56cupola/broken
- obj_integrity = 0
- buyable = FALSE
-
-/obj/item/hardpoint/secondary/m56cupola/apply_buff()
- owner.internal_cooldowns["secondary"] = 3
- owner.accuracies["secondary"] = 0.7
-
-/obj/item/hardpoint/secondary/m56cupola/active_effect(atom/A)
-
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
-
- next_use = world.time + owner.internal_cooldowns["secondary"] * owner.misc_ratios["secd_cool"]
- if(!prob(owner.accuracies["secondary"] * 100 * owner.misc_ratios["secd_acc"]))
- A = get_step(A, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- P.fire_at(A, owner, src, P.ammo.max_range, P.ammo.shell_speed)
- playsound(get_turf(src), pick(list('sound/weapons/guns/fire/smartgun1.ogg', 'sound/weapons/guns/fire/smartgun2.ogg', 'sound/weapons/guns/fire/smartgun3.ogg')), 60, 1)
- ammo.current_rounds--
-
-/obj/item/hardpoint/secondary/grenade_launcher
- name = "Grenade Launcher"
- desc = "A secondary weapon for tanks that shoots grenades"
-
- max_integrity = 500
- point_cost = 25
-
- icon_state = "glauncher"
-
- disp_icon = "tank"
- disp_icon_state = "glauncher"
-
- starter_ammo = /obj/item/ammo_magazine/tank/tank_glauncher
- max_clips = 3
- max_angle = 90
-
-/obj/item/hardpoint/secondary/grenade_launcher/apply_buff()
- owner.internal_cooldowns["secondary"] = 30
- owner.accuracies["secondary"] = 0.4
-
-/obj/item/hardpoint/secondary/grenade_launcher/active_effect(atom/A)
-
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
-
- next_use = world.time + owner.internal_cooldowns["secondary"] * owner.misc_ratios["secd_cool"]
- if(!prob(owner.accuracies["secondary"] * 100 * owner.misc_ratios["secd_acc"]))
- A = get_step(A, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- log_bomber(usr, "fired", src)
- P.fire_at(A, owner, src, P.ammo.max_range, P.ammo.shell_speed)
- playsound(get_turf(src), 'sound/weapons/guns/fire/grenadelauncher.ogg', 60, 1)
- ammo.current_rounds--
-/////////////////////
-// SECONDARY SLOTS // END
-/////////////////////
-
-///////////////////
-// SUPPORT SLOTS // START
-///////////////////
-
-/obj/item/hardpoint/support/smoke_launcher
- name = "Smoke Launcher"
- desc = "Launches smoke forward to obscure vision"
-
- max_integrity = 300
- point_cost = 10
-
- icon_state = "slauncher_0"
-
- disp_icon = "tank"
- disp_icon_state = "slauncher"
-
- starter_ammo = /obj/item/ammo_magazine/tank/tank_slauncher
- max_clips = 4
- is_activatable = TRUE
-
-/obj/item/hardpoint/support/smoke_launcher/broken
- obj_integrity = 0
- buyable = FALSE
-
-/obj/item/hardpoint/support/smoke_launcher/apply_buff()
- owner.internal_cooldowns["support"] = 30
- owner.accuracies["support"] = 0.8
-
-/obj/item/hardpoint/support/smoke_launcher/active_effect(atom/A)
-
- if(!(ammo?.current_rounds > 0))
- to_chat(usr, span_warning("This module does not have any ammo."))
- return
-
- next_use = world.time + owner.internal_cooldowns["support"] * owner.misc_ratios["supp_cool"]
- if(!prob(owner.accuracies["support"] * 100 * owner.misc_ratios["supp_acc"]))
- A = get_step(A, pick(GLOB.cardinals))
- var/obj/projectile/P = new
- P.generate_bullet(new ammo.default_ammo)
- P.fire_at(A, owner, src, P.ammo.max_range, P.ammo.shell_speed)
- playsound(get_turf(src), 'sound/weapons/guns/fire/tank_smokelauncher.ogg', 60, 1)
- ammo.current_rounds--
-
-/obj/item/hardpoint/support/smoke_launcher/get_icon_image(x_offset, y_offset, new_dir)
-
- var/icon_suffix = "NS"
- var/icon_state_suffix = "0"
-
- if(new_dir in list(NORTH, SOUTH))
- icon_suffix = "NS"
- else if(new_dir in list(EAST, WEST))
- icon_suffix = "EW"
-
- if(!obj_integrity)
- icon_state_suffix = "1"
- else if(!(ammo?.current_rounds > 0))
- icon_state_suffix = "2"
-
- return image(icon = "[disp_icon]_[icon_suffix]", icon_state = "[disp_icon_state]_[icon_state_suffix]", pixel_x = x_offset, pixel_y = y_offset)
-
-/obj/item/hardpoint/support/weapons_sensor
- name = "Integrated Weapons Sensor Array"
- desc = "Improves the accuracy and fire rate of all onboard weapons"
-
- max_integrity = 250
- point_cost = 100
- max_clips = 0
-
- icon_state = "warray"
-
- disp_icon = "tank"
- disp_icon_state = "warray"
-
-/obj/item/hardpoint/support/weapons_sensor/apply_buff()
- owner.misc_ratios["prim_cool"] = 0.67
- owner.misc_ratios["secd_cool"] = 0.67
- owner.misc_ratios["supp_cool"] = 0.67
-
- owner.misc_ratios["prim_acc"] = 1.67
- owner.misc_ratios["secd_acc"] = 1.67
- owner.misc_ratios["supp_acc"] = 1.67
-
-/obj/item/hardpoint/support/weapons_sensor/remove_buff()
- owner.misc_ratios["prim_cool"] = 1
- owner.misc_ratios["secd_cool"] = 1
- owner.misc_ratios["supp_cool"] = 1
-
- owner.misc_ratios["prim_acc"] = 1
- owner.misc_ratios["secd_acc"] = 1
- owner.misc_ratios["supp_acc"] = 1
-
-/obj/item/hardpoint/support/overdrive_enhancer
- name = "Overdrive Enhancer"
- desc = "Increases the movement speed of the vehicle it's atached to"
-
- max_integrity = 250
- point_cost = 100
- max_clips = 0
-
- icon_state = "odrive_enhancer"
- is_activatable = TRUE
-
- disp_icon = "tank"
- disp_icon_state = "odrive_enhancer"
-
- var/last_boost
-
-/obj/item/hardpoint/support/overdrive_enhancer/proc/nitros_on(mob/M)
- owner.misc_ratios["move"] = 0.2
- if(M)
- to_chat(M, span_danger("You hit the nitros! RRRRRRRMMMM!!"))
- playsound(M, 'sound/mecha/hydraulic.ogg', 60, 1, vary = 0)
- addtimer(CALLBACK(src, PROC_REF(boost_off)), TANK_OVERDRIVE_BOOST_DURATION)
- addtimer(CALLBACK(src, PROC_REF(boost_ready_notice)), TANK_OVERDRIVE_BOOST_COOLDOWN)
-
-/obj/item/hardpoint/support/overdrive_enhancer/remove_buff()
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- C.verbs -= /obj/vehicle/multitile/root/cm_armored/tank/verb/overdrive_multitile
- boost_off()
-
-/obj/item/hardpoint/support/overdrive_enhancer/apply_buff()
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- C.verbs += /obj/vehicle/multitile/root/cm_armored/tank/verb/overdrive_multitile
-
-/obj/item/hardpoint/support/overdrive_enhancer/proc/boost_off()
- owner.misc_ratios["move"] = 1
-
-/obj/item/hardpoint/support/overdrive_enhancer/proc/boost_ready_notice()
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- if(C.driver)
- to_chat(C.driver, span_danger("The overdrive nitros are ready for use."))
-
-/obj/item/hardpoint/support/overdrive_enhancer/proc/activate_overdrive()
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- if(!C.driver)
- return
- if(world.time < last_boost + TANK_OVERDRIVE_BOOST_COOLDOWN)
- to_chat(C.driver, span_warning("Your nitro overdrive isn't yet ready. It will be available again in [(last_boost + TANK_OVERDRIVE_BOOST_COOLDOWN - world.time) * 0.1] seconds."))
- return
- last_boost = world.time
- nitros_on(C.driver)
-
-//How to get out, via verb
-/obj/vehicle/multitile/root/cm_armored/tank/verb/overdrive_multitile()
- set category = "Vehicle"
- set name = "Activate Overdrive"
- set src in view(0)
-
- if(usr.incapacitated(TRUE))
- return
-
- if(usr != driver)
- to_chat(usr, span_warning("You need to be in the driver seat to use this!"))
- return
-
- var/obj/item/hardpoint/support/overdrive_enhancer/OE = hardpoints[HDPT_SUPPORT]
- if(!istype(OE, /obj/item/hardpoint/support/overdrive_enhancer) || OE.obj_integrity <= 0)
- to_chat(usr, span_warning("The overdrive engine is missing or too badly damaged!"))
- return
- OE.activate_overdrive(usr)
-
-/obj/item/hardpoint/support/artillery_module
- name = "Artillery Module"
- desc = "Allows the gunner to look far into the distance."
-
- max_integrity = 250
- point_cost = 100
- max_clips = 0
-
- is_activatable = TRUE
- var/is_active = FALSE
-
- var/view_buff = "25x25" //This way you can VV for more or less fun
- var/view_tile_offset = 5
-
- icon_state = "artillery"
-
- disp_icon = "tank"
- disp_icon_state = "artillerymod"
-
-/obj/item/hardpoint/support/artillery_module/active_effect(atom/A)
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- if(!C.gunner)
- return
- var/mob/M = C.gunner
- if(!M.client)
- return
- if(is_active)
- M.client.view_size.reset_to_default()
- M.client.pixel_x = 0
- M.client.pixel_y = 0
- is_active = FALSE
- C.is_zoomed = FALSE
- return
- M.client.view_size.reset_to_default()
- is_active = TRUE
- C.is_zoomed = TRUE
- switch(C.dir)
- if(NORTH)
- M.client.pixel_x = 0
- M.client.pixel_y = view_tile_offset * 32
- if(SOUTH)
- M.client.pixel_x = 0
- M.client.pixel_y = -1 * view_tile_offset * 32
- if(EAST)
- M.client.pixel_x = view_tile_offset * 32
- M.client.pixel_y = 0
- if(WEST)
- M.client.pixel_x = -1 * view_tile_offset * 32
- M.client.pixel_y = 0
-
-/obj/item/hardpoint/support/artillery_module/deactivate()
- var/obj/vehicle/multitile/root/cm_armored/tank/C = owner
- if(!ismob(C.gunner))
- return
- var/mob/M = C.gunner
- if(!M.client)
- return
- is_active = FALSE
- M.client.view_size.reset_to_default()
- M.client.pixel_x = 0
- M.client.pixel_y = 0
-
-/obj/item/hardpoint/support/artillery_module/remove_buff()
- deactivate()
-
-/obj/item/hardpoint/support/artillery_module/is_ready()
- if(!obj_integrity)
- to_chat(usr, span_warning("This module is too broken to be used."))
- return FALSE
- return TRUE
-
-///////////////////
-// SUPPORT SLOTS // END
-///////////////////
-
-/////////////////
-// ARMOR SLOTS // START
-/////////////////
-
-/obj/item/hardpoint/armor/ballistic
- name = "Ballistic Armor"
- desc = "Protects the vehicle from high-penetration weapons. Provides some protection against slashing and high impact attacks."
-
- max_integrity = 1000
- point_cost = 100
-
- icon_state = "ballistic_armor"
-
- disp_icon = "tank"
- disp_icon_state = "ballistic_armor"
-
-/obj/item/hardpoint/armor/ballistic/broken
- obj_integrity = 0
- buyable = FALSE
-
-/obj/item/hardpoint/armor/ballistic/apply_buff()
- owner.dmg_multipliers["bullet"] = 0.5
- owner.dmg_multipliers["slash"] = 0.75
- owner.dmg_multipliers["blunt"] = 0.75
- owner.dmg_multipliers["all"] = 0.9
-
-/obj/item/hardpoint/armor/ballistic/remove_buff()
- owner.dmg_multipliers["bullet"] = 1
- owner.dmg_multipliers["slash"] = 1
- owner.dmg_multipliers["blunt"] = 1
- owner.dmg_multipliers["all"] = 1
-
-/obj/item/hardpoint/armor/caustic
- name = "Caustic Armor"
- desc = "Protects vehicles from most types of acid. Provides some protection against slashing and high impact attacks."
-
- max_integrity = 1000
- point_cost = 100
-
- icon_state = "caustic_armor"
-
- disp_icon = "tank"
- disp_icon_state = "caustic_armor"
-
-/obj/item/hardpoint/armor/caustic/apply_buff()
- owner.dmg_multipliers["acid"] = 0.5
- owner.dmg_multipliers["slash"] = 0.75
- owner.dmg_multipliers["blunt"] = 0.75
- owner.dmg_multipliers["all"] = 0.9
-
-/obj/item/hardpoint/armor/caustic/remove_buff()
- owner.dmg_multipliers["acid"] = 1
- owner.dmg_multipliers["slash"] = 1
- owner.dmg_multipliers["blunt"] = 1
- owner.dmg_multipliers["all"] = 1
-
-/obj/item/hardpoint/armor/concussive
- name = "Concussive Armor"
- desc = "Protects the vehicle from high-impact weapons. Provides some protection against ballistic and explosive attacks."
-
- max_integrity = 1000
- point_cost = 100
-
- icon_state = "concussive_armor"
-
- disp_icon = "tank"
- disp_icon_state = "concussive_armor"
-
-/obj/item/hardpoint/armor/concussive/apply_buff()
- owner.dmg_multipliers["blunt"] = 0.5
- owner.dmg_multipliers["explosive"] = 0.75
- owner.dmg_multipliers["ballistic"] = 0.75
- owner.dmg_multipliers["all"] = 0.9
-
-/obj/item/hardpoint/armor/concussive/remove_buff()
- owner.dmg_multipliers["blunt"] = 1
- owner.dmg_multipliers["explosive"] = 1
- owner.dmg_multipliers["ballistic"] = 1
- owner.dmg_multipliers["all"] = 1
-
-/obj/item/hardpoint/armor/paladin
- name = "Paladin Armor"
- desc = "Protects the vehicle from large incoming explosive projectiles. Provides some protection against slashing and high impact attacks."
-
- max_integrity = 1000
- point_cost = 100
-
- icon_state = "paladin_armor"
-
- disp_icon = "tank"
- disp_icon_state = "paladin_armor"
-
-/obj/item/hardpoint/armor/paladin/apply_buff()
- owner.dmg_multipliers["explosive"] = 0.5
- owner.dmg_multipliers["blunt"] = 0.75
- owner.dmg_multipliers["slash"] = 0.75
- owner.dmg_multipliers["all"] = 0.9
-
-/obj/item/hardpoint/armor/paladin/remove_buff()
- owner.dmg_multipliers["explosive"] = 1
- owner.dmg_multipliers["blunt"] = 1
- owner.dmg_multipliers["slash"] = 1
- owner.dmg_multipliers["all"] = 1
-
-/obj/item/hardpoint/armor/snowplow
- name = "Snowplow"
- desc = "Clears a path in the snow for friendlies"
-
- max_integrity = 700
- is_activatable = TRUE
- point_cost = 50
-
- icon_state = "snowplow"
-
- disp_icon = "tank"
- disp_icon_state = "snowplow"
-
-/obj/item/hardpoint/armor/snowplow/livingmob_interact(mob/living/M)
- var/turf/targ = get_step(M, owner.dir)
- targ = get_step(M, owner.dir)
- targ = get_step(M, owner.dir)
- M.throw_at(targ, 4, 2, src, 1)
- M.apply_damage(7 + rand(0, 3), BRUTE, blocked = MELEE)
-
-/////////////////
-// ARMOR SLOTS // END
-/////////////////
-
-/////////////////
-// TREAD SLOTS // START
-/////////////////
-
-/obj/item/hardpoint/treads/standard
- name = "Treads"
- desc = "Integral to the movement of the vehicle"
-
- max_integrity = 500
- point_cost = 25
-
- icon_state = "treads"
-
- disp_icon = "tank"
- disp_icon_state = "treads"
-
-/obj/item/hardpoint/treads/standard/broken
- obj_integrity = 0
- buyable = FALSE
-
-/obj/item/hardpoint/treads/standard/get_icon_image(x_offset, y_offset, new_dir)
- return null //Handled in update_icon()
-
-/obj/item/hardpoint/treads/standard/apply_buff()
- owner.move_delay = 7
-
-/obj/item/hardpoint/treads/standard/remove_buff()
- owner.move_delay = 30
-
-/////////////////
-// TREAD SLOTS // END
-/////////////////
-
-
-///////////////
-// AMMO MAGS // START
-///////////////
-
-//Special ammo magazines for hardpoint modules. Some aren't here since you can use normal magazines on them
-/obj/item/ammo_magazine/tank
- flags_magazine = NONE //No refilling
- var/point_cost = 0
-
-/obj/item/ammo_magazine/tank/ltb_cannon
- name = "LTB Cannon Magazine"
- desc = "A primary armament cannon magazine"
- caliber = CALIBER_86 //Making this unique on purpose
- icon_state = "ltbcannon_4"
- w_class = WEIGHT_CLASS_GIGANTIC
- default_ammo = /datum/ammo/rocket/ltb
- max_rounds = 4
- point_cost = 50
-
-/obj/item/ammo_magazine/tank/ltb_cannon/update_icon_state()
- . = ..()
- icon_state = "ltbcannon_[current_rounds]"
-
-
-/obj/item/ammo_magazine/tank/ltaaap_minigun
- name = "LTAA-AP Minigun Magazine"
- desc = "A primary armament minigun magazine"
- caliber = CALIBER_762X51 //Correlates to miniguns
- icon_state = "painless"
- w_class = WEIGHT_CLASS_GIGANTIC
- default_ammo = /datum/ammo/bullet/minigun
- max_rounds = 500
- point_cost = 25
-
-
-
-/obj/item/ammo_magazine/tank/flamer
- name = "Flamer Magazine"
- desc = "A secondary armament flamethrower magazine"
- caliber = CALIBER_FUEL_THICK //correlates to flamer mags
- icon_state = "flametank_large"
- w_class = WEIGHT_CLASS_GIGANTIC
- default_ammo = /datum/ammo/flamethrower/tank_flamer
- max_rounds = 120
- point_cost = 50
-
-
-
-/obj/item/ammo_magazine/tank/towlauncher
- name = "TOW Launcher Magazine"
- desc = "A secondary armament rocket magazine"
- caliber = CALIBER_84MM //correlates to any rocket mags
- icon_state = "quad_rocket"
- w_class = WEIGHT_CLASS_GIGANTIC
- default_ammo = /datum/ammo/rocket/ap //Fun fact, AP rockets seem to be a straight downgrade from normal rockets. Maybe I'm missing something...
- max_rounds = 5
- point_cost = 100
-
-/obj/item/ammo_magazine/tank/tank_glauncher
- name = "Grenade Launcher Magazine"
- desc = "A secondary armament grenade magazine"
- caliber = CALIBER_40MM
- icon_state = "glauncher_2"
- w_class = WEIGHT_CLASS_GIGANTIC
- default_ammo = /datum/ammo/grenade_container
- max_rounds = 10
- point_cost = 25
-
-
-/obj/item/ammo_magazine/tank/tank_glauncher/update_icon_state()
- . = ..()
- if(current_rounds >= max_rounds)
- icon_state = "glauncher_2"
- else if(current_rounds <= 0)
- icon_state = "glauncher_0"
- else
- icon_state = "glauncher_1"
-
-
-/obj/item/ammo_magazine/tank/tank_slauncher
- name = "Smoke Launcher Magazine"
- desc = "A support armament grenade magazine"
- caliber = CALIBER_40MM
- icon_state = "slauncher_1"
- w_class = WEIGHT_CLASS_GIGANTIC
- default_ammo = /datum/ammo/grenade_container/smoke
- max_rounds = 6
- point_cost = 5
-
-/obj/item/ammo_magazine/tank/tank_slauncher/update_icon_state()
- . = ..()
- icon_state = "slauncher_[current_rounds <= 0 ? "0" : "1"]"
-
-///////////////
-// AMMO MAGS // END
-///////////////
diff --git a/code/modules/vehicles/multitile/multitile.dm b/code/modules/vehicles/multitile/multitile.dm
deleted file mode 100644
index 784a98157affb..0000000000000
--- a/code/modules/vehicles/multitile/multitile.dm
+++ /dev/null
@@ -1,246 +0,0 @@
-
-/*
-A multitile vehicle is made up of 2 types of objects, root and hitbox
-relaymove() only does something for root
-You can inherit and do special stuff for either, but only one will let you move
-Rotations treat root as x = 0, y = 0
-All of the backend for movement will be under root
-
-Vehicles are placed on the map by a spawner or admin verb
-*/
-
-//This was part of an old plan to have a dynamic number of vehicle interiors
-//Turns out that's incredibly fucking dificult, so a fixed number is gonna be the ideal choice
-/*
-/obj/effect/landmark/multitile_starter
- name = "Landmark"
- desc = "Where the interiors for multitiles start spawning"
-*/
-
-/obj/effect/multitile_spawner
-
- var/width = 2
- var/height = 3
- var/spawn_dir = SOUTH
-
-//A hidden marker for where you mount and dismount the vehicle
-//You could have multiple if you wanted
-/obj/effect/multitile_entrance
- name = "Entrance marker"
- desc = "Marker for the entrance of a multitile vehicle."
-
- var/obj/vehicle/multitile/root/master
- invisibility = INVISIBILITY_MAXIMUM
-
-/obj/effect/multitile_entrance/Destroy(force = FALSE)
- if(!force)
- return QDEL_HINT_LETMELIVE
- return ..()
-
-//Always moves where you want it to, no matter what
-/obj/effect/multitile_entrance/Move(atom/A)
- loc = get_turf(A)
- return TRUE
-
-//A basic handoff to the root object to actually deal with attempted player entrance
-/obj/effect/multitile_entrance/verb/enter_multitile()
- set category = "Vehicle"
- set name = "Enter Vehicle"
- set src in view(0)
-
- master.handle_player_entrance(usr)
-
-//Remnant of vehicle interiors
-/*
-/obj/effect/landmark/multitile_exit
- name = "Landmark"
- desc = "Marker for the exit of the interior"
-
- invisibility = INVISIBILITY_MAXIMUM
-
- var/obj/vehicle/multitile/root/master
-*/
-
-/*
-/obj/effect/landmark/multitile_exit/verb/exit_multitile(mob/M)
- set category = "Vehicle"
- set name = "Exit Vehicle"
- set src in master
-
- master.handle_player_exit(M)
-*/
-
-//Super super generic, doesn't really need to exist
-/obj/vehicle/multitile
- name = "multitile vehicle"
- desc = "You shouldn't see this"
-
-/obj/vehicle/multitile/relaymove(mob/user, direction)
- return
-
-//Hitboxes, do notthing but move with the root object and take up space
-//All interactions like bullets or whatever should be passed up to the root object
-/obj/vehicle/multitile/hitbox
- name = "hitbox"
- desc = "Generic multitile vehicle hitbox"
-
- var/obj/vehicle/multitile/root/root
- invisibility = INVISIBILITY_MAXIMUM
-
-/obj/vehicle/multitile/root
- name = "root"
- desc = "Root tile for multitile vehicles"
-
- var/old_dir
-
- var/obj/effect/multitile_entrance/entrance
- //var/obj/effect/landmark/multitile_exit/exit
-
- //Objects that move in accordance with this one
- //Objects indexed by /datum/coords
- //Does not include the root obj
- var/list/linked_objs = list()
-
- //list of turfs that the vehicle was in before
- var/list/old_locs = list()
-
- //list of idle passengers in the vehicle
- //used for any type of APC
- var/list/idle_passengers = list()
- var/max_idle_passengers = 0
-
- //Another remnant of vehicle interiors
- //var/list/interior_data = list()
-
- var/base_icon_type = "" //e.g. "tank" or "apc", used to assign icons to the hitboxes
-
- var/hitbox_type = /obj/vehicle/multitile/hitbox
-
-//How to get out, via verb
-/obj/vehicle/multitile/root/verb/exit_multitile()
- set category = "Vehicle"
- set name = "Exit Vehicle"
- set src in view(0)
-
- if(!usr.incapacitated(TRUE))
- handle_player_exit(usr)
-
-/obj/vehicle/multitile/root/proc/handle_player_exit(mob/M)
- return
-
-/obj/vehicle/multitile/root/proc/handle_player_entrance(mob/living/M)
- if(M.resting || M.buckled || M.incapacitated())
- return FALSE
- return TRUE
-
-/obj/vehicle/multitile/root/proc/handle_harm_attack(mob/living/M)
- if(M.resting || M.buckled || M.incapacitated())
- return FALSE
- return TRUE
-
-//Vebrs for rotations, set up a macro and get turnin
-/obj/vehicle/multitile/root/verb/clockwise_rotate_multitile()
- set category = "Vehicle"
- set name = "Rotate Vehicle Clockwise"
- set src in view(0)
-
- var/mob/M = usr
- try_rotate(-90, M)
-
-/obj/vehicle/multitile/root/verb/counterclockwise_rotate_multitile()
- set category = "Vehicle"
- set name = "Rotate Vehicle Counterclockwise"
- set src in view(0)
-
- var/mob/M = usr
- try_rotate(90, M)
-
-//A wrapper for try_move() that rotates
-/obj/vehicle/multitile/root/proc/try_rotate(deg, mob/user, force = FALSE)
- save_locs()
- rotate_coords(deg)
- if(!try_move(linked_objs, null, TRUE))
- rotate_coords(-1*deg)
- revert_locs()
- return FALSE
-
- update_icon()
- return TRUE
-
-//Called when players try to move from inside the vehicle
-//Another wrapper for try_move()
-/obj/vehicle/multitile/root/relaymove(mob/user, direction)
- if(dir in list(EAST, WEST))
- if(direction == SOUTH)
- return try_rotate( (dir == WEST ? 90 : -90), user, 1)
- else if(direction == NORTH)
- return try_rotate( (dir == EAST ? 90 : -90), user, 1)
-
- else if(dir in list(SOUTH, NORTH))
- if(direction == EAST)
- return try_rotate( (dir == SOUTH ? 90 : -90), user, 1)
- else if(direction == WEST)
- return try_rotate( (dir == NORTH ? 90 : -90), user, 1)
-
- old_dir = dir
- save_locs()
- if(!try_move(linked_objs, direction))
- revert_locs()
- setDir(old_dir)
- return FALSE //Failed movement
-
- setDir(old_dir) //Preserve the direction you're facing when moving backwards
- return TRUE
-
-
-/obj/vehicle/multitile/root/proc/load_hitboxes()
- return
-
-/obj/vehicle/multitile/root/proc/load_entrance_marker()
- return
-
-//Saves where everything is so we can revert
-/obj/vehicle/multitile/root/proc/save_locs()
-
-
-
-//We were unable to move, so revert everything we may have done so far
-/obj/vehicle/multitile/root/proc/revert_locs()
-
-
-//Forces the root object to move so everything can update relative to it
-/obj/vehicle/multitile/root/proc/move_root(direction)
-
- var/turf/T = get_step(loc, direction)
- loc = T
-
-//The REAL guts of multitile movement
-//Here's how this shit works:
-
-//Step 1: Iterate over every associated object and move what can be moved in the right dir
-//Step 2: Save everything that couldn't move in a list
-//Step 3: Recursively try to move those after everything else that can move has done so
-// This is so if one hitbox is blocking another, eventually they will both move
-//Step 4: If on this level of recursion, we couldn't move any more things, we've failed
-//Step 5: Continue steps 1 through 4 until we fail or succeed
-/obj/vehicle/multitile/root/proc/try_move(list/objs, direction, is_rotation = FALSE)
-
- var/list/blocked = list() //What couldn't move this time
-
-
- if(length(blocked) == length(objs))
- return FALSE //No more things can move, return false
-
- else if(length(blocked))
- return try_move(blocked, direction, is_rotation) //Some things moved, retry the others
-
- else if(!length(blocked))
- return TRUE //Everything finished moving, return true
-
- else
- return FALSE //Shouldn't even be possible, so say we failed anyways
-
-//Applies the 2D transformation matrix to the saved coords
-/obj/vehicle/multitile/root/proc/rotate_coords(deg)
-
-
diff --git a/code/modules/vehicles/multitile/tank.dm b/code/modules/vehicles/multitile/tank.dm
deleted file mode 100644
index d7ca9e8d39af2..0000000000000
--- a/code/modules/vehicles/multitile/tank.dm
+++ /dev/null
@@ -1,308 +0,0 @@
-
-//TANKS, HURRAY
-//Read the documentation in cm_armored.dm and multitile.dm before trying to decipher this stuff
-
-
-/obj/vehicle/multitile/root/cm_armored/tank
- name = "\improper M34A2 Longstreet Light Tank"
- desc = "A giant piece of armor with a big gun, you know what to do. Entrance in the back."
-
- icon = 'icons/obj/vehicles/tank_NS.dmi'
- icon_state = "tank_base"
- pixel_x = -32
- pixel_y = -32
-
- var/mob/gunner
- var/mob/driver
-
- var/mob/occupant_exiting
- var/next_sound_play = 0
-
- var/is_zoomed = FALSE
-
- req_access = list()
-
-/obj/effect/multitile_spawner/cm_armored/tank
-
- width = 3
- height = 3
- spawn_dir = EAST
- var/list/spawn_hardpoints = list()
-
-/obj/effect/multitile_spawner/cm_armored/tank/Initialize(mapload)
- . = ..()
- return INITIALIZE_HINT_QDEL
-
-//Spawns a tank that has a bunch of broken hardpoints
-/obj/effect/multitile_spawner/cm_armored/tank/decrepit
- spawn_hardpoints = list(HDPT_PRIMARY = /obj/item/hardpoint/primary/cannon/broken,
- HDPT_SECDGUN = /obj/item/hardpoint/secondary/m56cupola/broken,
- HDPT_SUPPORT = /obj/item/hardpoint/support/smoke_launcher/broken,
- HDPT_ARMOR = /obj/item/hardpoint/armor/ballistic/broken,
- HDPT_TREADS = /obj/item/hardpoint/treads/standard/broken)
-
-/obj/effect/multitile_spawner/cm_armored/tank/fixed
- spawn_hardpoints = list(HDPT_PRIMARY = /obj/item/hardpoint/primary/cannon,
- HDPT_SECDGUN = /obj/item/hardpoint/secondary/m56cupola,
- HDPT_SUPPORT = /obj/item/hardpoint/support/smoke_launcher,
- HDPT_ARMOR = /obj/item/hardpoint/armor/ballistic,
- HDPT_TREADS = /obj/item/hardpoint/treads/standard)
-
-//For the tank, start forcing people out if everything is broken
-/obj/vehicle/multitile/root/cm_armored/tank/handle_all_modules_broken()
- deactivate_all_hardpoints()
-
- if(driver)
- to_chat(driver, span_danger("You dismount to as the smoke and flames start to choke you!"))
- driver.Move(entrance.loc)
- driver.unset_interaction()
- driver = null
- else if(gunner)
- to_chat(gunner, span_danger("You dismount to as the smoke and flames start to choke you!"))
- gunner.Move(entrance.loc)
- gunner.unset_interaction()
- gunner = null
-
-/obj/vehicle/multitile/root/cm_armored/tank/remove_all_players()
- deactivate_all_hardpoints()
- for(var/mob/living/L in (contents + loc.contents))
- if(!entrance) //Something broke, uh oh
- forceMove(get_turf(src))
- else
- forceMove(get_turf(entrance))
- gunner = null
- driver = null
-
-//Let's you switch into the other seat, doesn't work if it's occupied
-/obj/vehicle/multitile/root/cm_armored/tank/verb/switch_seats()
- set name = "Swap Seats"
- set category = "Vehicle"
- set src in view(0)
-
- if(usr.incapacitated())
- return
-
- var/wannabe_trucker = (usr == gunner) ? TRUE : FALSE
- var/neighbour = wannabe_trucker ? driver : gunner
- if(neighbour)
- to_chat(usr, span_notice("There's already someone in the other seat."))
- return
-
- to_chat(usr, span_notice("You start getting into the other seat."))
- addtimer(CALLBACK(src, PROC_REF(seat_switched), wannabe_trucker, usr), 3 SECONDS)
-
-/obj/vehicle/multitile/root/cm_armored/tank/proc/seat_switched(wannabe_trucker, mob/living/user)
-
- var/player = wannabe_trucker ? gunner : driver
- var/challenger = wannabe_trucker ? driver : gunner
- if(QDELETED(user) || user.incapacitated() || player != user)
- return
-
- if(challenger)
- to_chat(usr, span_notice("Someone beat you to the other seat!"))
- return
-
- to_chat(usr, span_notice("You man up the [wannabe_trucker ? "driver" : "gunner"]'s seat."))
-
- if(wannabe_trucker)
- deactivate_all_hardpoints()
- driver = wannabe_trucker ? user : null
- gunner = wannabe_trucker ? null : user
-
-/obj/vehicle/multitile/root/cm_armored/tank/can_use_hp(mob/M)
- if(!M || M != gunner)
- return FALSE
- if(!M.dextrous)
- to_chat(M, span_warning("You don't have the dexterity to do this!"))
- return FALSE
- return !M.incapacitated()
-
-/obj/vehicle/multitile/root/cm_armored/tank/handle_harm_attack(mob/M, mob/occupant)
- . = ..()
- if(!.)
- return
- if(!occupant)
- to_chat(M, span_warning("There is no one on that seat."))
- return
- M.visible_message(span_warning("[M] starts pulling [occupant] out of \the [src]."),
- span_warning("You start pulling [occupant] out of \the [src]. (this will take a while...)"), null, 6)
- var/fumbling_time = 20 SECONDS - 2 SECONDS * M.skills.getRating(SKILL_POLICE) - 2 SECONDS * M.skills.getRating(SKILL_LARGE_VEHICLE)
- if(!do_after(M, fumbling_time, NONE, src, BUSY_ICON_HOSTILE))
- return
- exit_tank(occupant, TRUE, TRUE)
- M.visible_message(span_warning("[M] forcibly pulls [occupant] out of [src]."),
- span_notice("you forcibly pull [occupant] out of [src]."), null, 6)
- if(!isliving(occupant))
- return
- var/mob/living/L = occupant
- L.Paralyze(8 SECONDS)
-
-//Two seats, gunner and driver
-//Must have the skills to do so
-/obj/vehicle/multitile/root/cm_armored/tank/handle_player_entrance(mob/living/carbon/M)
- . = ..()
- if(!. || !istype(M) || M.do_actions)
- return
-
- var/slot = tgui_alert(M, "Select a seat", null, list("Driver", "Gunner"))
- if(!Adjacent(M))
- return
-
- var/occupant = (slot == "Driver") ? driver : gunner
- if((M.a_intent == INTENT_HARM || isxeno(M)) && occupant)
- handle_harm_attack(M, occupant)
- return
-
- if(!M.dextrous)
- to_chat(M, span_warning("You don't have the dexterity to drive [src]!"))
- return
- if(!allowed(M))
- to_chat(M, span_warning("Access denied."))
- return
- if(occupant)
- to_chat(M, span_warning("That seat is already taken."))
- return
- var/obj/item/offhand = M.get_inactive_held_item()
- if(offhand && !(HAS_TRAIT(offhand, TRAIT_NODROP) || (offhand.flags_item & (DELONDROP|ITEM_ABSTRACT))))
- to_chat(M, span_warning("You need your hands free to climb on [src]."))
- return
-
- if(M.skills.getRating(SKILL_LARGE_VEHICLE) < SKILL_LARGE_VEHICLE_TRAINED)
- M.visible_message(span_notice("[M] fumbles around figuring out how to get into the [src]."),
- span_notice("You fumble around figuring out how to get into [src]."))
- var/fumbling_time = 10 SECONDS - 2 SECONDS * M.skills.getRating(SKILL_LARGE_VEHICLE)
- if(!do_after(M, fumbling_time, NONE, src, BUSY_ICON_UNSKILLED) || \
- (offhand && !(HAS_TRAIT(offhand, TRAIT_NODROP) || (offhand.flags_item & DELONDROP))))
- return
-
- to_chat(M, span_notice("You start climbing into [src]."))
- if(!do_after(M, 10 SECONDS, NONE, src, BUSY_ICON_GENERIC) || \
- (offhand && !(HAS_TRAIT(offhand, TRAIT_NODROP) || (offhand.flags_item & DELONDROP))))
- return
- if(occupant)
- to_chat(M, span_warning("Someone got into the [lowertext(slot)]'s seat before you could."))
- return
-
- if(slot == "Driver")
- driver = M
- else
- gunner = M
- M.forceMove(src)
- to_chat(M, span_notice("You enter into the [lowertext(slot)]'s seat."))
- M.set_interaction(src)
-
-//Deposits you onto the exit marker
-/obj/vehicle/multitile/root/cm_armored/tank/handle_player_exit(mob/M)
-
- if(!(M in list(gunner, driver))) //someone whom isn't supposed to be here to begin with.
- exit_tank(M, TRUE)
- return
-
- if(!M.do_actions)
- if(occupant_exiting)
- to_chat(M, span_notice("Someone is already getting out of [src]."))
- return
- occupant_exiting = M
-
- to_chat(M, span_notice("You start climbing out of [src]."))
-
- addtimer(CALLBACK(src, PROC_REF(exit_tank), M), 5 SECONDS)
-
-/obj/vehicle/multitile/root/cm_armored/tank/proc/exit_tank(mob/M, forced = FALSE, silent = FALSE)
- if(!forced)
- occupant_exiting = null
-
- if(!M || get_turf(M) != get_turf(src) || (M.incapacitated() && !forced))
- return
-
- var/turf/T = get_turf(entrance)
- if(!forced)
- if(!T.CanPass(M, T))
- if(!silent)
- to_chat(M, span_notice("Something is blocking you from exiting."))
- return
- for(var/atom/A in T)
- if(A.CanPass(M, T))
- continue
- if(!silent)
- to_chat(M, span_notice("Something is blocking you from exiting."))
- return
- M.forceMove(T)
-
- if(M == gunner)
- deactivate_all_hardpoints()
- gunner = null
- else if(M == driver)
- driver = null
- M.unset_interaction()
- if(!silent)
- to_chat(M, span_notice("You climb out of [src]."))
-
-//No one but the driver can drive
-/obj/vehicle/multitile/root/cm_armored/tank/relaymove(mob/user, direction)
- if(user != driver || user.incapacitated())
- return
-
- . = ..(user, direction)
-
-
-
- if(next_sound_play < world.time)
- playsound(src, 'sound/ambience/tank_driving.ogg', vol = 20, sound_range = 30)
- next_sound_play = world.time + 21
-
-//No one but the driver can turn
-/obj/vehicle/multitile/root/cm_armored/tank/try_rotate(deg, mob/user, force = FALSE)
-
- if(user != driver || user.incapacitated())
- return
-
- . = ..(deg, user, force)
-
- if(. && istype(hardpoints[HDPT_SUPPORT], /obj/item/hardpoint/support/artillery_module) && gunner?.client)
- var/client/C = gunner.client
- var/old_x = C.pixel_x
- var/old_y = C.pixel_y
- C.pixel_x = old_x*cos(deg) - old_y*sin(deg)
- C.pixel_y = old_x*sin(deg) + old_y*cos(deg)
-
-/obj/vehicle/multitile/hitbox/cm_armored/tank/get_driver()
- var/obj/vehicle/multitile/root/cm_armored/tank/T = root
- return T?.driver
-
-
-/obj/vehicle/multitile/root/cm_armored/tank/take_damage_type(damage, type, atom/attacker)
- . = ..()
-
- if(istype(attacker, /mob))
- var/mob/M = attacker
- log_combat(M, src, "damaged [src] with [damage] [type] damage.")
-
- if(gunner)
- log_combat(gunner, null, "[src] took [damage] [type] damage [ismob(attacker) ? "from [key_name(attacker)]" : ""].")
- if(driver)
- log_combat(driver, null, "[src] took [damage] [type] damage [ismob(attacker) ? "from [key_name(attacker)]" : ""].")
-
-
-/obj/vehicle/multitile/root/cm_armored/proc/click_action(A, mob/user, params)
- if(istype(A, /atom/movable/screen) || A == src)
- return FALSE
-
- if(!can_use_hp(user))
- return TRUE
-
- if(!hardpoints.Find(active_hp))
- to_chat(user, span_warning("Please select an active hardpoint first."))
- return TRUE
-
- var/obj/item/hardpoint/HP = hardpoints[active_hp]
-
- if(!HP?.is_ready())
- return TRUE
-
- if(!HP.firing_arc(A))
- to_chat(user, span_warning("The target is not within your firing arc."))
- return TRUE
-
- HP.active_effect(A)
- return TRUE
diff --git a/code/modules/vehicles/multitile/tankvendor.dm b/code/modules/vehicles/multitile/tankvendor.dm
deleted file mode 100644
index 4b86e08154c1a..0000000000000
--- a/code/modules/vehicles/multitile/tankvendor.dm
+++ /dev/null
@@ -1,291 +0,0 @@
-#define TANKFAB_MAIN_MENU 0
-#define TANKFAB_MOD_MAINT 1
-#define TANKFAB_PRINTER 2
-#define TANKFAB_BUSY 3
-
-
-/obj/machinery/tank_part_fabricator
- name = "tank part fabricator"
- desc = "A large automated 3D printer for producing new tank parts and maintaining old ones."
- density = TRUE
- anchored = TRUE
- use_power = IDLE_POWER_USE
- idle_power_usage = 20
- icon = 'icons/obj/machines/drone_fab.dmi'
- icon_state = "drone_fab_idle"
- var/obj/item/loaded_mod
- var/tank_points = 625
- var/busy = FALSE
- var/screen = TANKFAB_MAIN_MENU
-
-/obj/machinery/tank_part_fabricator/proc/set_busy(business = TRUE, timer)
- busy = business
- if(timer)
- addtimer(CALLBACK(src, PROC_REF(set_busy), !business), timer)
- update_icon()
- updateUsrDialog()
-
-/obj/machinery/tank_part_fabricator/update_icon_state()
- . = ..()
- if(machine_stat & NOPOWER)
- icon_state = "drone_fab_nopower"
- return
- if(busy)
- icon_state = "drone_fab_active"
- return
- else
- icon_state = "drone_fab_idle"
-
-/obj/machinery/tank_part_fabricator/interact(mob/user)
- . = ..()
- if(.)
- return
- var/dat
- if(screen == TANKFAB_BUSY)
- dat += "[src] is busy. Please wait for completion of the current operation..."
- else
- dat += "Points Available: [tank_points]
"
- var/slot = loaded_mod ? "[icon2html(loaded_mod, usr)] [loaded_mod]" : "None"
- dat += "Loaded Hardpoint/Ammo Clip: [slot]
"
-
- var/screen_menu = screen != TANKFAB_MAIN_MENU ? "Main Menu" : "Main Menu"
- var/screen_maint = screen != TANKFAB_MOD_MAINT ? "Hardpoint Maintenance" : "Hardpoint Maintenance"
- var/screen_fab = screen != TANKFAB_PRINTER ? "Hardpoint Printer" : "Hardpoint Printer"
- dat += "[screen_fab] | | [screen_menu] | | [screen_maint] | |
"
-
-
- switch(screen)
-
- if(TANKFAB_MOD_MAINT)
- if(!loaded_mod)
- dat += "No hardpoint or clip loaded. Please stand-by..."
- else
- var/price = calculate_mod_value(loaded_mod)
- var/restore = istype(loaded_mod, /obj/item/hardpoint) ? "Repair" : "Refill"
- var/cost = calculate_repair_price(loaded_mod)
- dat += "[restore] ([cost]) | | Refund ([price]) | |
"
- dat += "Hardpoint/Ammo Clip infos:
"
- dat += "Brief description: [loaded_mod.desc]
"
- if(istype(loaded_mod, /obj/item/hardpoint))
- var/obj/item/hardpoint/H = loaded_mod
- dat += "Hardpoint integrity: [PERCENT(H.obj_integrity/H.max_integrity)]%
"
- if(H.starter_ammo)
- var/ammo_count = H.ammo ? "[H.ammo] ([H.ammo.current_rounds]/[H.ammo.max_rounds])" : "No clip loaded"
- dat += "Current clip: [ammo_count]
"
- if(H.max_clips)
- dat += "Backup clips:
"
- for(var/I in H.backup_clips)
- var/obj/item/ammo_magazine/tank/A = I
- dat += "[A] ([A.current_rounds]/[A.max_rounds])
"
- else if(istype(loaded_mod, /obj/item/ammo_magazine))
- var/obj/item/ammo_magazine/A = loaded_mod
- dat += "Current rounds: [A] [A.current_rounds]/[A.max_rounds]
"
- dat += "Caliber: [A.caliber]
"
- //var/obj/item/hardpoint/H = A.gun_type //Ima break this.
- //dat += "Supported Hardpoint: [initial(H.name)]
"
-
- if(TANKFAB_PRINTER)
- dat += "Armor:
"
- for(var/build_type in subtypesof(/obj/item/hardpoint/armor))
- var/obj/item/hardpoint/armor/AR = build_type
- if(!initial(AR.buyable))
- continue
- var/build_name = initial(AR.name)
- var/build_cost = initial(AR.point_cost)
- dat += "[build_name] ([build_cost])
"
-
- dat += "Primary Weapon:
"
- for(var/build_type in subtypesof(/obj/item/hardpoint/primary))
- var/obj/item/hardpoint/primary/PR = build_type
- if(!initial(PR.buyable))
- continue
- var/build_name = initial(PR.name)
- var/build_cost = initial(PR.point_cost)
- dat += "[build_name] ([build_cost])
"
-
- dat += "Secondary Weapon:
"
- for(var/build_type in subtypesof(/obj/item/hardpoint/secondary))
- var/obj/item/hardpoint/secondary/SE = build_type
- if(!initial(SE.buyable))
- continue
- var/build_name = initial(SE.name)
- var/build_cost = initial(SE.point_cost)
- dat += "[build_name] ([build_cost])
"
-
- dat += "Support Module:
"
- for(var/build_type in subtypesof(/obj/item/hardpoint/support))
- var/obj/item/hardpoint/support/SP = build_type
- if(!initial(SP.buyable))
- continue
- var/build_name = initial(SP.name)
- var/build_cost = initial(SP.point_cost)
- dat += "[build_name] ([build_cost])
"
-
- dat += "Treads:
"
- for(var/build_type in subtypesof(/obj/item/hardpoint/treads))
- var/obj/item/hardpoint/treads/TR = build_type
- if(!initial(TR.buyable))
- continue
- var/build_name = initial(TR.name)
- var/build_cost = initial(TR.point_cost)
- dat += "[build_name] ([build_cost])
"
-
- dat += "Weapon Ammo:
"
- for(var/build_type in subtypesof(/obj/item/ammo_magazine/tank))
- var/obj/item/ammo_magazine/tank/AM = build_type
- var/build_name = initial(AM.name)
- var/build_cost = initial(AM.point_cost)
- dat += "[build_name] ([build_cost])
"
-
- var/datum/browser/popup = new(user, "dropship_part_fab", "Tank Part Fabricator
")
- popup.set_content(dat)
- popup.open(TRUE)
-
-
-/obj/machinery/tank_part_fabricator/attackby(obj/item/W, mob/user)
- if((istype(W, /obj/item/hardpoint) || istype(W, /obj/item/ammo_magazine/tank)) && user.a_intent != INTENT_HARM)
- if(machine_stat & (NOPOWER|BROKEN))
- return
- if(busy)
- to_chat(usr, span_warning("[src] is busy. Please wait for completion of previous operation."))
- else if(user.transferItemToLoc(W, src))
- user.visible_message(span_notice("[user] loads [W] into [src]'s maintenance slot."), span_notice("You load [W] into [src]'s maintenance slot."), null, 4)
- loaded_mod = W
- else
- to_chat(user, span_warning("[W] appears to be stuck to your hands."))
- else if(iscrowbar(W) && machine_stat & (NOPOWER|BROKEN) && !QDELETED(loaded_mod))
- user.visible_message(span_warning("[user] starts to pry [src]'s maintenance slot open."), span_notice("You start to pry [loaded_mod] out of [src]'s maintenance slot..."))
- if(!do_after(user, 4 SECONDS, NONE, src, BUSY_ICON_GENERIC) || QDELETED(loaded_mod))
- return
- user.visible_message("[user] pries [loaded_mod] out of [src].", span_notice("You retrieve [loaded_mod] from [src]."))
- eject_tank_part()
- else
- return ..()
-
-/obj/machinery/tank_part_fabricator/proc/build_tank_part(part_type, cost, mob/user)
- if(machine_stat & (NOPOWER|BROKEN) || busy)
- return
- if(tank_points < cost)
- to_chat(user, span_warning("You don't have enough points to build that."))
- return
- visible_message(span_notice("[src] starts printing something."))
- tank_points -= cost
- set_busy()
- addtimer(CALLBACK(src, PROC_REF(dispense_tank_part), part_type), 10 SECONDS)
-
-/obj/machinery/tank_part_fabricator/proc/dispense_tank_part(part_type)
- set_busy(FALSE)
- var/turf/T = get_step(src, SOUTHEAST)
- playsound(src, 'sound/machines/hydraulics_1.ogg', 40, 1)
- new part_type(T)
-
-/obj/machinery/tank_part_fabricator/proc/calculate_mod_value()
- if(istype(loaded_mod, /obj/item/hardpoint))
- var/obj/item/hardpoint/mod = loaded_mod
- . = (mod.point_cost - mod.point_cost * (1 - (mod.obj_integrity/mod.max_integrity)) * 0.5) * 0.5
- if(mod.starter_ammo)
- if(mod.ammo)
- . += (mod.ammo.point_cost - mod.ammo.point_cost * (1 - (mod.ammo.current_rounds/mod.ammo.max_rounds)) * 0.5) * 0.5
- else
- . -= initial(mod.starter_ammo.point_cost) * 0.5
- for(var/O in mod.backup_clips)
- var/obj/item/ammo_magazine/tank/A = O
- . = (A.point_cost - A.point_cost * (1 - (A.current_rounds/A.max_rounds)) * 0.5) * 0.5
- else if(istype(loaded_mod, /obj/item/ammo_magazine/tank))
- var/obj/item/ammo_magazine/tank/A = loaded_mod
- . = (A.point_cost - A.point_cost * (1 - (A.current_rounds/A.max_rounds)) * 0.5) * 0.5
- . = max(round(.), 0)
-
-/obj/machinery/tank_part_fabricator/proc/calculate_repair_price()
- if(istype(loaded_mod, /obj/item/hardpoint))
- var/obj/item/hardpoint/mod = loaded_mod
- . = ((mod.point_cost - mod.point_cost * (mod.obj_integrity/mod.max_integrity)) * 0.1)
- if(mod.starter_ammo)
- if(mod.ammo)
- . += ((mod.ammo.point_cost - mod.ammo.point_cost * (mod.ammo.current_rounds/mod.ammo.max_rounds)) * 0.9)
- else
- . += initial(mod.starter_ammo.point_cost) * 0.9
- else if(istype(loaded_mod, /obj/item/ammo_magazine/tank))
- var/obj/item/ammo_magazine/tank/A = loaded_mod
- . = ((A.point_cost - A.point_cost * (A.current_rounds/A.max_rounds)) * 0.9)
-
- . = max(round(.), 0)
-
-/obj/machinery/tank_part_fabricator/proc/eject_tank_part(mob/user)
- if(busy || QDELETED(loaded_mod))
- return
- var/turf/T = get_step(src, SOUTHEAST)
- if(user)
- to_chat(user, span_notice("You retrieve [loaded_mod] from [src]."))
- loaded_mod.forceMove(T)
- loaded_mod = null
-
-/obj/machinery/tank_part_fabricator/proc/refund_tank_part()
- if(machine_stat & (NOPOWER|BROKEN) || busy || QDELETED(loaded_mod))
- return
- tank_points += calculate_mod_value()
- visible_message(span_notice("[src] starts disassembling [loaded_mod]."))
- QDEL_NULL(loaded_mod)
- set_busy(TRUE, 10 SECONDS)
-
-/obj/machinery/tank_part_fabricator/proc/restore_tank_part(mob/user)
- if(machine_stat & (NOPOWER|BROKEN) || busy || QDELETED(loaded_mod))
- return
- var/cost = calculate_repair_price()
- if(tank_points < cost)
- to_chat(user, span_warning("You don't have enough points to repair that."))
- tank_points -= cost
- if(istype(loaded_mod, /obj/item/hardpoint))
- var/obj/item/hardpoint/H = loaded_mod
- H.repair_damage(H.max_integrity)
- if(H.ammo)
- H.ammo.current_rounds = H.ammo.max_rounds
- H.ammo.update_icon()
- else if(H.starter_ammo)
- H.ammo = new H.starter_ammo
- else if(istype(loaded_mod, /obj/item/ammo_magazine/tank))
- var/obj/item/ammo_magazine/tank/A = loaded_mod
- A.current_rounds = A.max_rounds
- set_busy(TRUE, 6 SECONDS)
-
-/obj/machinery/tank_part_fabricator/Topic(href, href_list)
- . =..()
- if(.)
- return
-
- if(busy)
- to_chat(usr, span_warning("[src] is busy. Please wait for completion of previous operation."))
- return
-
- if(href_list["produce"])
- var/produce = text2path(href_list["produce"])
- var/cost
- if(ispath(produce, /obj/item/hardpoint))
- var/obj/item/hardpoint/H = produce
- cost = initial(H.point_cost)
- else if(ispath(produce, /obj/item/ammo_magazine/tank))
- var/obj/item/ammo_magazine/tank/A = produce
- cost = initial(A.point_cost)
- if(isnull(cost))
- updateUsrDialog()
- return
- build_tank_part(produce, cost, usr)
-
- if(href_list["eject"])
- eject_tank_part(usr)
-
- if(href_list["refund"])
- refund_tank_part()
-
- if(href_list["restore"])
- restore_tank_part(usr)
-
- if(href_list["screen"])
- screen = text2num(href_list["screen"])
-
- updateUsrDialog()
-
-#undef TANKFAB_MAIN_MENU
-#undef TANKFAB_MOD_MAINT
-#undef TANKFAB_PRINTER
-#undef TANKFAB_BUSY
diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm
index 3413ae5ea1d60..1efc2384d5320 100644
--- a/code/modules/vehicles/sealed.dm
+++ b/code/modules/vehicles/sealed.dm
@@ -88,7 +88,7 @@
M.visible_message(span_notice("[M] drops out of \the [src]!"))
return TRUE
-/obj/vehicle/sealed/proc/exit_location(M)
+/obj/vehicle/sealed/proc/exit_location(mob/M)
return drop_location()
/obj/vehicle/sealed/attackby(obj/item/I, mob/user, params)
@@ -141,10 +141,14 @@
return FALSE
/obj/vehicle/sealed/relaymove(mob/living/user, direction)
- if(canmove)
- vehicle_move(direction)
+ if(is_driver(user) && canmove)
+ vehicle_move(user, direction)
return TRUE
/// Sinced sealed vehicles (cars and mechs) don't have riding components, the actual movement is handled here from [/obj/vehicle/sealed/proc/relaymove]
-/obj/vehicle/sealed/proc/vehicle_move(direction)
- return FALSE
+/obj/vehicle/sealed/proc/vehicle_move(mob/living/user, direction)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!COOLDOWN_CHECK(src, cooldown_vehicle_move))
+ return FALSE
+ COOLDOWN_START(src, cooldown_vehicle_move, move_delay)
+ return !(SEND_SIGNAL(src, COMSIG_VEHICLE_MOVE, user, direction) & COMPONENT_DRIVER_BLOCK_MOVE)
diff --git a/code/modules/vehicles/unmanned/unmanned_vehicle.dm b/code/modules/vehicles/unmanned/unmanned_vehicle.dm
index 1777677a5db28..a109237c9313f 100644
--- a/code/modules/vehicles/unmanned/unmanned_vehicle.dm
+++ b/code/modules/vehicles/unmanned/unmanned_vehicle.dm
@@ -79,11 +79,11 @@
QDEL_NULL(in_chamber)
return ..()
-/obj/vehicle/unmanned/obj_destruction()
+/obj/vehicle/unmanned/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
robogibs(src)
return ..()
-/obj/vehicle/unmanned/take_damage(damage_amount, damage_type, damage_flag, effects, attack_dir, armour_penetration)
+/obj/vehicle/unmanned/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
. = ..()
hud_set_machine_health()
diff --git a/code/modules/vehicles/wheelchair.dm b/code/modules/vehicles/wheelchair.dm
index 0104c18930440..b9e3653eed098 100644
--- a/code/modules/vehicles/wheelchair.dm
+++ b/code/modules/vehicles/wheelchair.dm
@@ -20,7 +20,7 @@
wheels_overlay = image(icon, overlay_icon, FLY_LAYER)
AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE, CALLBACK(src, PROC_REF(can_user_rotate)),CALLBACK(src, PROC_REF(can_be_rotated)),null)
-/obj/vehicle/ridden/wheelchair/obj_destruction(damage_flag)
+/obj/vehicle/ridden/wheelchair/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
new /obj/item/stack/rods(drop_location(), 1)
return ..()
diff --git a/code/modules/xenomorph/acidwell.dm b/code/modules/xenomorph/acidwell.dm
index 656b62d7ff51d..0e04cc9427a80 100644
--- a/code/modules/xenomorph/acidwell.dm
+++ b/code/modules/xenomorph/acidwell.dm
@@ -44,7 +44,7 @@
qdel(src)
-/obj/structure/xeno/acidwell/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/structure/xeno/acidwell/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
if(!QDELETED(creator) && creator.stat == CONSCIOUS && creator.z == z)
var/area/A = get_area(src)
if(A)
diff --git a/code/modules/xenomorph/silo.dm b/code/modules/xenomorph/silo.dm
index f24f516952f51..15e4270421866 100644
--- a/code/modules/xenomorph/silo.dm
+++ b/code/modules/xenomorph/silo.dm
@@ -56,7 +56,7 @@
newt.tunnel_desc = "[AREACOORD_NO_Z(newt)]"
newt.name += " [name]"
-/obj/structure/xeno/silo/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/structure/xeno/silo/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
if(GLOB.hive_datums[hivenumber])
UnregisterSignal(GLOB.hive_datums[hivenumber], list(COMSIG_HIVE_XENO_MOTHER_PRE_CHECK, COMSIG_HIVE_XENO_MOTHER_CHECK))
GLOB.hive_datums[hivenumber].xeno_message("A resin silo has been destroyed at [AREACOORD_NO_Z(src)]!", "xenoannounce", 5, FALSE,src.loc, 'sound/voice/alien_help2.ogg',FALSE , null, /atom/movable/screen/arrow/silo_damaged_arrow)
@@ -91,7 +91,7 @@
. += span_info("It appears in good shape, pulsating healthily.")
-/obj/structure/xeno/silo/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+/obj/structure/xeno/silo/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
. = ..()
//We took damage, so it's time to start regenerating if we're not already processing
diff --git a/code/modules/xenomorph/spawner.dm b/code/modules/xenomorph/spawner.dm
index c12dc12f2d7bb..01a0ce1f6894b 100644
--- a/code/modules/xenomorph/spawner.dm
+++ b/code/modules/xenomorph/spawner.dm
@@ -41,7 +41,7 @@
. += span_info("It appears in good shape, pulsating healthily.")
-/obj/structure/xeno/spawner/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+/obj/structure/xeno/spawner/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", effects = TRUE, attack_dir, armour_penetration = 0, mob/living/blame_mob)
. = ..()
spawner_damage_alert()
diff --git a/code/modules/xenomorph/trap.dm b/code/modules/xenomorph/trap.dm
index 51f60d3ea160e..fda71d477cf8a 100644
--- a/code/modules/xenomorph/trap.dm
+++ b/code/modules/xenomorph/trap.dm
@@ -55,7 +55,7 @@
else
icon_state = "trap"
-/obj/structure/xeno/trap/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/structure/xeno/trap/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
if((damage_amount || damage_flag) && hugger && loc)
trigger_trap()
return ..()
diff --git a/code/modules/xenomorph/xeno_turret.dm b/code/modules/xenomorph/xeno_turret.dm
index 4eb332b10e842..715a837f0ef87 100644
--- a/code/modules/xenomorph/xeno_turret.dm
+++ b/code/modules/xenomorph/xeno_turret.dm
@@ -56,7 +56,7 @@
SIGNAL_HANDLER
qdel(src)
-/obj/structure/xeno/xeno_turret/obj_destruction(damage_amount, damage_type, damage_flag)
+/obj/structure/xeno/xeno_turret/obj_destruction(damage_amount, damage_type, damage_flag, mob/living/blame_mob)
if(damage_amount) //Spawn the gas only if we actually get destroyed by damage
var/datum/effect_system/smoke_spread/xeno/smoke = new /datum/effect_system/smoke_spread/xeno/acid(src)
smoke.set_up(1, get_turf(src))
diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml
index bc690f7632e40..b3b7ef4fd41fb 100644
--- a/html/changelogs/archive/2024-02.yml
+++ b/html/changelogs/archive/2024-02.yml
@@ -335,3 +335,134 @@
blackdav123:
- balance: Reinforced the external walls of two buildings on Magmoor.
- balance: Preweeded Gelida IV and added a tunnel.
+2024-02-20:
+ blackdav123:
+ - rscadd: A set of vendors near the Alamo on Sulaco.
+2024-02-21:
+ TiviPlus:
+ - bugfix: Fixes custom squad tablets not working
+ - bugfix: Fix Main console not being able to overwatch custom squads
+ - bugfix: fixed headsets being invisible if you spawned them while in a custom squad
+ - bugfix: 'Custom squads will now automatically pick the first most unused letter
+ in the name: (e.g cool squad will be .o because .c is taken by charlie by default)
+ the key can be found by examining your headset. people in the squad still have
+ to use ; because im lazy and didnt fix it'
+ XElectricX:
+ - bugfix: Fix interactive emotes
+ ivanmixo:
+ - bugfix: Nulls in the global client list shouldnt be as common anymore
+2024-02-22:
+ Lumipharon:
+ - bugfix: fixed heightened stamina skill not properly lowering the stam regen delay
+ - bugfix: 'Campaign: Fixed an issue with some perks unlocking items incorrectly'
+ - balance: 'Campaign: Advanced firearm training perk reduced to 400 credits'
+ - balance: 'Campaign: Advanced construction and medical training reduced to 300
+ credits and increase skill by 2'
+ Naaanii:
+ - balance: Increases hunter speed to -1.5
+ - balance: Essence link (Drone), Create Jelly (Drone and Hivelord), Place Jelly
+ Pod (Hivelord), Place Resin trap (Carrier), Create Spiderling (Widow), Acid
+ well placement (several castes) can be used while resting.
+ blackdav123:
+ - balance: ' Removed 7 phoron miners from Ice Colony, Added 1.'
+2024-02-23:
+ Barnet2:
+ - balance: Resin walls take 1.75x from melee, but have highly increased bullet and
+ laser armor
+ Lewdcifer:
+ - rscadd: Various Warrior changes, as part of a minor rework and rebalancing!
+ - rscadd: 'Added a new Primordial Warrior ability: Flurry!'
+ - rscadd: 'FLURRY: 10 plasma, 7 CD, three charges.'
+ - rscadd: 'FLURRY: Each attack deals 25% of the Warrior''s melee damage, and counts
+ towards the Empower combo count.'
+ - rscadd: 'FLURRY: Empowered version is the same as Jab''s. Inflicts 3 stacks of
+ blind, 6 of blur, and confuses for 3s.'
+ - rscdel: Primordial Warrior's Jab ability removed.
+ - balance: Melee damage reduced from 23 to 20.
+ - balance: Maximum plasma reduced from 120 to 100.
+ - balance: Plasma gain reduced from 12 to 10.
+ - balance: 'AGILITY: Cooldown reduced from 0.5s to 0.4s.'
+ - balance: 'LUNGE: Plasma cost increased from 18 to 20.'
+ - balance: 'GRAPPLE TOSS: Plasma cost increased from 18 to 20.'
+ - balance: 'FLING + GRAPPLE TOSS: Distance penalty on big mobs changed. Instead
+ of halving it, it now reduces it by one.'
+ - balance: 'FLING + GRAPPLE TOSS: Throwing impact effect added.'
+ - balance: 'PUNCH: Plasma cost increased from 12 to 15.'
+ - balance: 'PUNCH: Will no longer push targets if they''re against a wall.'
+ - qol: 'ALL ABILITIES: Abilities will now inform the player via a balloon message
+ when their cooldown is finished.'
+ - qol: 'AGILITY: No longer disallows using abilities while active. However, using
+ any ability will automatically disable Agility.'
+ - imageadd: Visual effects for throwing impacts, and punching.
+ - refactor: Refactored most of Warrior's coding, with the intention of better quality
+ and containment.
+ Pariah919:
+ - balance: Reinforced Walls have 75 laser/bullet armor and 5000 HP.
+2024-02-24:
+ 74delta:
+ - rscadd: hefa grenades to req for 550 a box and 10 in roundstart vendors
+ Lumipharon:
+ - qol: Xeno plasma levels will update on the hud as soon as plasma levels change,
+ instead of waiting for life ticks
+ - bugfix: fixed pheros not using plasma if you were on full plasma, even off weeds
+ - code_imp: Improved some xeno plasma hud code
+ - balance: 'Campaign: Added individual stats to track shrapnel removed, mines placed
+ and mechs destroyed'
+ - balance: 'Campaign: Boosted the cash rewards for performing non combat actions'
+ - bugfix: fixed some wrong/misleading wield time stats
+ - bugfix: 'Campaign: Fixed an incorrect objective desc for ASAT Capture'
+ - code_imp: Cleaned up some conveyor code
+ - bugfix: 'Campaign: Fixed not being properly despawned at the end of a mission
+ in some cases'
+ MiniMeatwad:
+ - admin: Added message for recipient when a ticket is (un)marked, closed, or tiered.
+ XElectricX:
+ - code_imp: Interactive emotes can be bound to a key instead of using right clicking.
+ Xander3359:
+ - bugfix: Fix robotic cradle sprite
+ ivanmixo:
+ - rscadd: Added directional attacks, you can enable them in the preference menu
+ for all living mobs.
+2024-02-25:
+ RipGrayson:
+ - rscadd: Added some barricade mapping helpers for mapper use.
+ TiviPlus:
+ - rscadd: added some admin-only vehicles
+ - code_imp: added multitile, multicrew vehicle support
+ Wisemonster:
+ - balance: ERT ships will now have plasteel cades protecting their doors.
+ - code_imp: Added a plasteel child that spawns in closed
+2024-02-26:
+ Barnet2:
+ - balance: Daedalus Prison has weaker tables and pre-broken fences for a better
+ prep experience
+ - bugfix: Snows shows up as a proper texture in SDMM
+2024-02-27:
+ Barnet2:
+ - balance: Boiler no longer has to root to fire
+ - spellcheck: New groundside marine ranks
+ - balance: Antigas nade req price reduced to 600, 20 nades in vendor instead of
+ 10
+ - balance: Widow and Puppeteer minions have a 5% chance to hit their selected limb
+ (chest) instead of a 70% chance.
+ - rscadd: Xeno manifest on lobby screen
+ - balance: Queen has a sunder multiplier of 0.8 (takes 80% sunder), Crusher has
+ a sunder multiplier of 0.5 (takes 50% sunder)
+ DeltaFire15:
+ - bugfix: The new larva queue check should no longer be broken.
+ Helg2:
+ - code_imp: Weather danger messages now can be filtered by chat settings.
+ Lumipharon:
+ - balance: 'Campaign: Improved HP, shield and stamina perks'
+ - bugfix: fixed a larva queue issue
+ - bugfix: fixed xeno plasma hud getting stuck on death
+ - balance: Meleeing humans can no longer embed the item
+ - balance: 'Campaign: Slightly increased some credit reward values, and loss bonus
+ now applies to mission credit rewards'
+ Pariah919:
+ - balance: AC has significantly more wall damage to keep up with wall resistance
+ buffs.
+ Wisemonster:
+ - qol: The tadpole surgery dispenser now layers over objects
+ exdal:
+ - rscadd: AI VOX announcements are now visible on chat
diff --git a/icons/Xeno/actions.dmi b/icons/Xeno/actions.dmi
index b6d8564f6d0e2..483953daefc2f 100644
Binary files a/icons/Xeno/actions.dmi and b/icons/Xeno/actions.dmi differ
diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi
index 97972aa2dca2b..70d83f6e308dd 100644
Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index 4ca785e3cfc23..6deca04d367fb 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 675ac6b129ad1..cce2a0a4c3e6c 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/effects/mapping_helpers.dmi b/icons/effects/mapping_helpers.dmi
index e553d521f130e..9d1d541a3d0f1 100644
Binary files a/icons/effects/mapping_helpers.dmi and b/icons/effects/mapping_helpers.dmi differ
diff --git a/icons/mob/ammoHUD.dmi b/icons/mob/ammoHUD.dmi
index 8f4811fe64dd4..26fb5e0ef8e87 100644
Binary files a/icons/mob/ammoHUD.dmi and b/icons/mob/ammoHUD.dmi differ
diff --git a/icons/mob/inhands/weapons/grenades_left.dmi b/icons/mob/inhands/weapons/grenades_left.dmi
index b260c09ec2f59..9829b2acfdf3f 100644
Binary files a/icons/mob/inhands/weapons/grenades_left.dmi and b/icons/mob/inhands/weapons/grenades_left.dmi differ
diff --git a/icons/mob/inhands/weapons/grenades_right.dmi b/icons/mob/inhands/weapons/grenades_right.dmi
index d5f61eff768d4..73c5aac4ca89d 100644
Binary files a/icons/mob/inhands/weapons/grenades_right.dmi and b/icons/mob/inhands/weapons/grenades_right.dmi differ
diff --git a/icons/obj/armored/1x1/tinytank.dmi b/icons/obj/armored/1x1/tinytank.dmi
new file mode 100644
index 0000000000000..8f45e78adefee
Binary files /dev/null and b/icons/obj/armored/1x1/tinytank.dmi differ
diff --git a/icons/obj/armored/1x1/tinytank_gun.dmi b/icons/obj/armored/1x1/tinytank_gun.dmi
new file mode 100644
index 0000000000000..83176645615a9
Binary files /dev/null and b/icons/obj/armored/1x1/tinytank_gun.dmi differ
diff --git a/icons/obj/armored/2x2/medium_vehicles.dmi b/icons/obj/armored/2x2/medium_vehicles.dmi
new file mode 100644
index 0000000000000..f8234bb6c9c23
Binary files /dev/null and b/icons/obj/armored/2x2/medium_vehicles.dmi differ
diff --git a/icons/obj/armored/3x3/apc.dmi b/icons/obj/armored/3x3/apc.dmi
new file mode 100644
index 0000000000000..536eb9c856a6c
Binary files /dev/null and b/icons/obj/armored/3x3/apc.dmi differ
diff --git a/icons/obj/armored/3x3/apc_damage_overlay.dmi b/icons/obj/armored/3x3/apc_damage_overlay.dmi
new file mode 100644
index 0000000000000..e3da1e1f15f7d
Binary files /dev/null and b/icons/obj/armored/3x3/apc_damage_overlay.dmi differ
diff --git a/icons/obj/armored/3x3/tank.dmi b/icons/obj/armored/3x3/tank.dmi
new file mode 100644
index 0000000000000..07453c2693a3c
Binary files /dev/null and b/icons/obj/armored/3x3/tank.dmi differ
diff --git a/icons/obj/armored/3x3/tank_damage.dmi b/icons/obj/armored/3x3/tank_damage.dmi
new file mode 100644
index 0000000000000..b3e72ab3651dd
Binary files /dev/null and b/icons/obj/armored/3x3/tank_damage.dmi differ
diff --git a/icons/obj/armored/3x3/tank_gun.dmi b/icons/obj/armored/3x3/tank_gun.dmi
new file mode 100644
index 0000000000000..853daccd3cb08
Binary files /dev/null and b/icons/obj/armored/3x3/tank_gun.dmi differ
diff --git a/icons/obj/armored/3x3/tank_secondary_gun.dmi b/icons/obj/armored/3x3/tank_secondary_gun.dmi
new file mode 100644
index 0000000000000..b4f739db1d735
Binary files /dev/null and b/icons/obj/armored/3x3/tank_secondary_gun.dmi differ
diff --git a/icons/obj/armored/hardpoint_modules.dmi b/icons/obj/armored/hardpoint_modules.dmi
new file mode 100644
index 0000000000000..5c7bbb4b0641a
Binary files /dev/null and b/icons/obj/armored/hardpoint_modules.dmi differ
diff --git a/icons/obj/items/grenade.dmi b/icons/obj/items/grenade.dmi
index ed63d51f0252d..af815f9f2c302 100644
Binary files a/icons/obj/items/grenade.dmi and b/icons/obj/items/grenade.dmi differ
diff --git a/icons/obj/items/items_mini.dmi b/icons/obj/items/items_mini.dmi
index 97c528c26eece..25f6d064f22f5 100644
Binary files a/icons/obj/items/items_mini.dmi and b/icons/obj/items/items_mini.dmi differ
diff --git a/icons/obj/items/radio.dmi b/icons/obj/items/radio.dmi
index 2d2e20cf62c6e..2755744fc2e92 100644
Binary files a/icons/obj/items/radio.dmi and b/icons/obj/items/radio.dmi differ
diff --git a/icons/obj/items/storage/storage_boxes.dmi b/icons/obj/items/storage/storage_boxes.dmi
index 159b210d92ceb..e04eb5239031a 100644
Binary files a/icons/obj/items/storage/storage_boxes.dmi and b/icons/obj/items/storage/storage_boxes.dmi differ
diff --git a/icons/obj/recycling.dmi b/icons/obj/recycling.dmi
index 57260af0f7db5..1635c50612fef 100644
Binary files a/icons/obj/recycling.dmi and b/icons/obj/recycling.dmi differ
diff --git a/icons/obj/vehicles/hardpoint_modules.dmi b/icons/obj/vehicles/hardpoint_modules.dmi
deleted file mode 100644
index 0c720a9237c04..0000000000000
Binary files a/icons/obj/vehicles/hardpoint_modules.dmi and /dev/null differ
diff --git a/icons/obj/vehicles/tank_EW.dmi b/icons/obj/vehicles/tank_EW.dmi
deleted file mode 100644
index ff984a6abb1e7..0000000000000
Binary files a/icons/obj/vehicles/tank_EW.dmi and /dev/null differ
diff --git a/icons/obj/vehicles/tank_NS.dmi b/icons/obj/vehicles/tank_NS.dmi
deleted file mode 100644
index a709c718c331d..0000000000000
Binary files a/icons/obj/vehicles/tank_NS.dmi and /dev/null differ
diff --git a/sound/effects/tankswivel.ogg b/sound/effects/tankswivel.ogg
new file mode 100644
index 0000000000000..775a13c07ccf8
Binary files /dev/null and b/sound/effects/tankswivel.ogg differ
diff --git a/sound/vehicles/weapons/ltb_reload.ogg b/sound/vehicles/weapons/ltb_reload.ogg
new file mode 100644
index 0000000000000..19bf8ae3015ee
Binary files /dev/null and b/sound/vehicles/weapons/ltb_reload.ogg differ
diff --git a/tgmc.dme b/tgmc.dme
index defbb042e9d2d..fc6b14e80dfec 100644
--- a/tgmc.dme
+++ b/tgmc.dme
@@ -442,6 +442,7 @@
#include "code\datums\elements\attachment.dm"
#include "code\datums\elements\connect_loc.dm"
#include "code\datums\elements\debris.dm"
+#include "code\datums\elements\directional_attack.dm"
#include "code\datums\elements\egrill_element.dm"
#include "code\datums\elements\footstep.dm"
#include "code\datums\elements\gestures.dm"
@@ -2011,7 +2012,8 @@
#include "code\modules\tgui_panel\tgui_panel.dm"
#include "code\modules\tooltip\tooltip.dm"
#include "code\modules\unit_tests\_unit_tests.dm"
-#include "code\modules\vehicles\_vehicle.dm"
+#include "code\modules\vehicles\__vehicle.dm"
+#include "code\modules\vehicles\_hitbox.dm"
#include "code\modules\vehicles\atv.dm"
#include "code\modules\vehicles\bicycle.dm"
#include "code\modules\vehicles\cargo_train.dm"
@@ -2023,6 +2025,18 @@
#include "code\modules\vehicles\vehicle_actions.dm"
#include "code\modules\vehicles\vehicle_key.dm"
#include "code\modules\vehicles\wheelchair.dm"
+#include "code\modules\vehicles\armored\__armored.dm"
+#include "code\modules\vehicles\armored\_multitile.dm"
+#include "code\modules\vehicles\armored\ammo_magazine.dm"
+#include "code\modules\vehicles\armored\apc.dm"
+#include "code\modules\vehicles\armored\armored_actions.dm"
+#include "code\modules\vehicles\armored\armored_modules.dm"
+#include "code\modules\vehicles\armored\armored_weapons.dm"
+#include "code\modules\vehicles\armored\medium_apc.dm"
+#include "code\modules\vehicles\armored\medium_tank.dm"
+#include "code\modules\vehicles\armored\small_apc.dm"
+#include "code\modules\vehicles\armored\tank_fabricator.dm"
+#include "code\modules\vehicles\armored\vehicle_collision.dm"
#include "code\modules\vehicles\cars\car.dm"
#include "code\modules\vehicles\mecha\_mecha.dm"
#include "code\modules\vehicles\mecha\mech_bay.dm"
@@ -2055,11 +2069,6 @@
#include "code\modules\vehicles\mecha\equipment\weapons\weapons.dm"
#include "code\modules\vehicles\mecha\working\ripley.dm"
#include "code\modules\vehicles\mecha\working\working.dm"
-#include "code\modules\vehicles\multitile\cm_armored.dm"
-#include "code\modules\vehicles\multitile\hardpoints.dm"
-#include "code\modules\vehicles\multitile\multitile.dm"
-#include "code\modules\vehicles\multitile\tank.dm"
-#include "code\modules\vehicles\multitile\tankvendor.dm"
#include "code\modules\vehicles\unmanned\deployable_vehicles.dm"
#include "code\modules\vehicles\unmanned\unmanned_droid.dm"
#include "code\modules\vehicles\unmanned\unmanned_turrets.dm"
diff --git a/tgui/packages/tgui/interfaces/PlayerPreferences/GameSettings.tsx b/tgui/packages/tgui/interfaces/PlayerPreferences/GameSettings.tsx
index ba09c0f0b1615..e164f50f48144 100644
--- a/tgui/packages/tgui/interfaces/PlayerPreferences/GameSettings.tsx
+++ b/tgui/packages/tgui/interfaces/PlayerPreferences/GameSettings.tsx
@@ -143,6 +143,13 @@ export const GameSettings = (props) => {
leftLabel={'Enabled'}
rightLabel={'Disabled'}
/>
+
|