diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 4ba064a17b..f677a877a6 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -53823,12 +53823,10 @@
/obj/structure/sign/poster/official/cohiba_robusto_ad,
/turf/closed/wall,
/area/lawoffice)
-"cIw" = (
-/obj/machinery/atmospherics/components/unary/vent_pump/on{
- dir = 8
- },
-/turf/open/floor/plasteel/dark,
-/area/hallway/primary/central)
+"cJW" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/circuit,
+/area/ai_monitored/nuke_storage)
"cMC" = (
/obj/effect/turf_decal/stripes/line{
dir = 5
@@ -54657,6 +54655,12 @@
},
/turf/open/floor/carpet,
/area/crew_quarters/cryopod)
+"dzQ" = (
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/hallway/primary/central)
"dBm" = (
/obj/machinery/portable_atmospherics/scrubber,
/turf/open/floor/plating,
@@ -56668,10 +56672,6 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/dorms)
-"luR" = (
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
-/turf/open/floor/plating,
-/area/maintenance/starboard/fore)
"lva" = (
/obj/effect/turf_decal/stripes/corner{
dir = 1
@@ -56850,12 +56850,6 @@
},
/turf/open/floor/plasteel,
/area/hallway/primary/starboard)
-"mvZ" = (
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
- dir = 8
- },
-/turf/open/floor/plasteel/dark,
-/area/hallway/primary/central)
"myh" = (
/obj/structure/piano,
/obj/structure/window/reinforced,
@@ -56921,13 +56915,6 @@
dir = 1
},
/area/hallway/primary/starboard)
-"mOi" = (
-/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
- dir = 4;
- pixel_y = 0
- },
-/turf/open/floor/plasteel/dark,
-/area/hallway/primary/central)
"mOB" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -57011,10 +56998,6 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/locker)
-"mVY" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/turf/open/floor/circuit,
-/area/ai_monitored/nuke_storage)
"naI" = (
/turf/open/space,
/area/space/station_ruins)
@@ -57110,17 +57093,6 @@
},
/turf/open/floor/plating,
/area/construction)
-"nyZ" = (
-/obj/structure/sign/warning/securearea,
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/turf/closed/wall/r_wall,
-/area/ai_monitored/nuke_storage)
-"nFC" = (
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
- dir = 4
- },
-/turf/open/floor/plasteel/dark,
-/area/hallway/primary/central)
"nGf" = (
/obj/machinery/hydroponics/constructable,
/obj/machinery/light{
@@ -57376,8 +57348,7 @@
/area/crew_quarters/locker)
"oyz" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on{
- dir = 4;
- pixel_y = 5
+ dir = 4
},
/turf/open/floor/wood,
/area/crew_quarters/theatre)
@@ -57459,6 +57430,12 @@
},
/turf/open/floor/plating,
/area/crew_quarters/abandoned_gambling_den)
+"oNz" = (
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/hallway/primary/central)
"oTW" = (
/obj/structure/chair/stool{
pixel_y = 8
@@ -57610,6 +57587,12 @@
dir = 1
},
/area/crew_quarters/fitness)
+"pzk" = (
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/hallway/primary/central)
"pBp" = (
/obj/effect/landmark/event_spawn,
/turf/closed/wall,
@@ -57707,6 +57690,12 @@
},
/turf/open/floor/plating,
/area/crew_quarters/fitness)
+"pQN" = (
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/hallway/primary/central)
"pRs" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 5
@@ -57835,6 +57824,11 @@
/obj/structure/grille/broken,
/turf/open/space/basic,
/area/space/nearstation)
+"qyj" = (
+/obj/effect/spawner/structure/window/reinforced,
+/obj/machinery/atmospherics/pipe/simple/supplymain/hidden,
+/turf/open/floor/plasteel/dark,
+/area/maintenance/fore)
"qBi" = (
/obj/structure/lattice,
/turf/closed/wall/r_wall,
@@ -58180,11 +58174,6 @@
/obj/item/gun/energy/laser/practice,
/turf/open/floor/plasteel/white,
/area/science/circuit)
-"saN" = (
-/obj/effect/spawner/structure/window/reinforced,
-/obj/machinery/atmospherics/pipe/simple/supplymain/hidden,
-/turf/open/floor/plasteel/dark,
-/area/maintenance/fore)
"saU" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/closed/wall/r_wall,
@@ -58753,8 +58742,7 @@
/area/maintenance/bar)
"tWj" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
- dir = 4;
- pixel_y = 5
+ dir = 4
},
/turf/open/floor/wood,
/area/crew_quarters/theatre)
@@ -59417,6 +59405,11 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/locker)
+"vOU" = (
+/obj/structure/sign/warning/securearea,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/closed/wall/r_wall,
+/area/ai_monitored/nuke_storage)
"vPs" = (
/obj/structure/lattice/catwalk,
/obj/structure/cable{
@@ -59578,6 +59571,23 @@
},
/turf/closed/wall/r_wall,
/area/maintenance/disposal/incinerator)
+<<<<<<< HEAD
+=======
+"wHT" = (
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
+"wIG" = (
+/obj/effect/turf_decal/tile/red{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/red{
+ dir = 8
+ },
+/obj/effect/turf_decal/tile/red,
+/turf/open/floor/plasteel,
+/area/security/brig)
+>>>>>>> cda2ace252871b0904c5d36f3d7da23ceb4bf152
"wKe" = (
/obj/effect/spawner/structure/window/reinforced,
/obj/machinery/door/poddoor/shutters/preopen{
@@ -81266,7 +81276,7 @@ aqQ
aqQ
awa
axq
-saN
+qyj
azI
rjQ
aBU
@@ -88498,7 +88508,7 @@ aBa
bqD
bsa
vCn
-mOi
+dzQ
bsa
nbT
byS
@@ -88749,13 +88759,13 @@ xUL
aBb
gpD
aDr
-mVY
+cJW
aGa
aHF
bqF
buQ
buQ
-nFC
+pzk
buQ
bxI
bwa
@@ -89265,11 +89275,11 @@ xEB
aDt
aEO
aGc
-nyZ
+vOU
aKG
dml
dml
-mvZ
+oNz
dml
bxK
bwh
@@ -89526,7 +89536,7 @@ aBa
bqG
bsa
eih
-cIw
+pQN
bsa
nbT
bwb
@@ -105680,7 +105690,7 @@ hHQ
feE
hHQ
kGJ
-luR
+wHT
asA
apE
dPk
diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm
index a04260249a..73386e6e1e 100644
--- a/_maps/map_files/KiloStation/KiloStation.dmm
+++ b/_maps/map_files/KiloStation/KiloStation.dmm
@@ -2284,7 +2284,6 @@
/area/maintenance/port/aft)
"aea" = (
/obj/structure/bed,
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/cobweb,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/blood/old,
@@ -2646,7 +2645,6 @@
/turf/open/floor/plasteel/dark,
/area/security/courtroom)
"aeN" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/cobweb,
/obj/structure/closet/firecloset,
@@ -2859,7 +2857,6 @@
/obj/structure/closet/secure_closet/injection{
name = "Justice Injections"
},
-/obj/effect/turf_decal/bot,
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/obj/machinery/button/door{
id = "SecJusticeChamber";
@@ -3423,7 +3420,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/dark,
/area/security/warden)
"afS" = (
@@ -3431,7 +3427,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/cobweb/cobweb2,
/turf/open/floor/plasteel/dark,
/area/security/warden)
@@ -3839,7 +3834,6 @@
/turf/open/floor/plasteel/dark,
/area/science/xenobiology)
"agw" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/emcloset,
/obj/effect/decal/cleanable/cobweb/cobweb2,
/turf/open/floor/plating,
@@ -3895,7 +3889,6 @@
},
/area/maintenance/starboard/fore)
"agC" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet,
/obj/item/cardboard_cutout{
@@ -4204,7 +4197,6 @@
/area/security/execution/education)
"ahe" = (
/obj/effect/landmark/secequipment,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -4213,7 +4205,6 @@
/area/security/main)
"ahf" = (
/obj/effect/landmark/secequipment,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -4977,7 +4968,6 @@
/turf/open/floor/plasteel/showroomfloor,
/area/science/server)
"ail" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/emcloset,
/turf/open/floor/plating,
@@ -5336,7 +5326,6 @@
/area/security/main)
"aiM" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet{
name = "chapel locker"
},
@@ -5448,7 +5437,6 @@
/obj/effect/turf_decal/tile/neutral,
/obj/effect/decal/cleanable/blood/old,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/box/corners,
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"aiW" = (
@@ -5597,9 +5585,6 @@
dir = 8
},
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"aji" = (
@@ -5774,7 +5759,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
-/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/dark,
/area/security/warden)
"ajv" = (
@@ -6032,7 +6016,6 @@
/area/maintenance/port/aft)
"ajU" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/emcloset,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -6556,7 +6539,6 @@
"akU" = (
/obj/structure/closet/crate/coffin,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
@@ -6617,7 +6599,6 @@
/obj/machinery/light{
dir = 1
},
-/obj/effect/turf_decal/box,
/obj/structure/extinguisher_cabinet{
pixel_x = -26
},
@@ -6635,7 +6616,6 @@
"akZ" = (
/obj/structure/closet/crate/coffin,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -6968,12 +6948,9 @@
/turf/open/floor/plasteel/dark,
/area/ai_monitored/security/armory)
"alw" = (
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
/obj/effect/decal/cleanable/blood/old,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/box/corners{
+/obj/effect/turf_decal/tile/neutral{
dir = 4
},
/turf/open/floor/plasteel/dark,
@@ -7040,7 +7017,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/status_display/evac{
pixel_y = 32
},
@@ -7127,11 +7103,8 @@
/turf/open/floor/plasteel/showroomfloor,
/area/security/brig)
"alH" = (
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/box/corners{
+/obj/effect/turf_decal/tile/neutral{
dir = 1
},
/turf/open/floor/plasteel/dark,
@@ -7228,7 +7201,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
-/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel/dark,
/area/ai_monitored/security/armory)
"alQ" = (
@@ -8034,7 +8006,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel/dark,
/area/security/warden)
@@ -8299,7 +8270,6 @@
/area/space/nearstation)
"anI" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet{
name = "bible locker"
},
@@ -8616,7 +8586,6 @@
/area/space/nearstation)
"aog" = (
/obj/effect/landmark/secequipment,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -9298,7 +9267,6 @@
},
/area/ai_monitored/turret_protected/ai)
"apo" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet{
name = "science locker"
@@ -9353,7 +9321,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/light/small,
/turf/open/floor/plasteel/dark,
@@ -9399,7 +9366,6 @@
},
/obj/effect/turf_decal/delivery,
/obj/structure/closet/crate/engineering,
-/obj/item/hand_tele,
/turf/open/floor/plasteel/dark,
/area/teleporter)
"apw" = (
@@ -9560,7 +9526,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/item/gun/energy/e_gun/advtaser,
/turf/open/floor/plasteel/dark,
/area/security/brig)
@@ -9586,11 +9551,8 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
-/obj/structure/rack,
-/obj/item/storage/toolbox/emergency,
-/obj/item/wrench,
-/obj/item/stack/packageWrap,
-/obj/item/hand_labeler,
+/obj/structure/table,
+/obj/item/hand_tele,
/turf/open/floor/plasteel/dark,
/area/teleporter)
"apL" = (
@@ -9697,7 +9659,6 @@
/area/bridge)
"apU" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet/firecloset,
/turf/open/floor/plating,
/area/maintenance/port/fore)
@@ -9715,7 +9676,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer1{
dir = 8
@@ -11430,7 +11390,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/structure/cable{
icon_state = "1-2"
},
@@ -12746,7 +12705,6 @@
/turf/open/floor/plating,
/area/crew_quarters/heads/hop)
"auH" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/firecloset,
/turf/open/floor/plating,
@@ -13040,7 +12998,6 @@
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"avm" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/emcloset,
/turf/open/floor/plating,
@@ -13665,7 +13622,6 @@
/turf/open/floor/plating,
/area/maintenance/port/fore)
"awf" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/item/storage/backpack/cultpack{
pixel_x = 4;
@@ -13709,7 +13665,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/item/gun/energy/e_gun/advtaser,
/turf/open/floor/plasteel/dark,
@@ -14536,7 +14491,6 @@
/turf/open/floor/grass,
/area/medical/genetics)
"axD" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet{
name = "skirt closet"
@@ -14899,7 +14853,6 @@
/area/engine/break_room)
"ayl" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet/l3closet/virology,
/turf/open/floor/plating,
/area/maintenance/starboard)
@@ -16498,7 +16451,6 @@
/area/science/robotics/lab)
"aAG" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet,
/obj/item/storage/backpack/duffelbag{
pixel_y = 4
@@ -16835,12 +16787,10 @@
/area/engine/engineering)
"aBh" = (
/obj/structure/closet/secure_closet/RD,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
/obj/effect/turf_decal/tile/neutral,
-/obj/item/gun/energy/e_gun/mini,
/obj/structure/extinguisher_cabinet{
pixel_y = 28
},
@@ -17956,7 +17906,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/delivery,
/obj/machinery/light{
dir = 1
},
@@ -19296,7 +19245,6 @@
/turf/open/floor/engine/vacuum,
/area/engine/atmos)
"aFg" = (
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/radiation,
/obj/item/clothing/glasses/meson,
/obj/machinery/power/apc/highcap/ten_k{
@@ -19527,7 +19475,6 @@
name = "Labor Shuttle Dock APC";
pixel_x = -26
},
-/obj/effect/turf_decal/bot,
/obj/structure/closet/secure_closet/evidence,
/turf/open/floor/plasteel/dark,
/area/security/processing)
@@ -19590,7 +19537,6 @@
/area/maintenance/starboard)
"aFF" = (
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet/emcloset,
/turf/open/floor/plating,
/area/maintenance/starboard)
@@ -20044,7 +19990,6 @@
/obj/effect/turf_decal/tile/yellow,
/obj/machinery/portable_atmospherics/scrubber,
/obj/machinery/atmospherics/components/unary/portables_connector/visible,
-/obj/effect/turf_decal/box,
/turf/open/floor/plasteel/showroomfloor,
/area/engine/atmos)
"aGx" = (
@@ -20543,7 +20488,6 @@
"aHv" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/secure_closet/exile,
-/obj/effect/turf_decal/bot,
/obj/machinery/light_switch{
pixel_y = -24
},
@@ -20985,7 +20929,6 @@
/turf/open/floor/plating,
/area/maintenance/port/fore)
"aId" = (
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/secure_closet/CMO,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
@@ -20998,7 +20941,6 @@
/obj/structure/extinguisher_cabinet{
pixel_x = -26
},
-/obj/item/gun/energy/e_gun/mini,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/heads/cmo)
"aIe" = (
@@ -23366,7 +23308,6 @@
/turf/open/floor/plasteel/showroomfloor,
/area/science/mixing)
"aLT" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet,
/obj/item/clothing/under/rank/bartender/purple{
pixel_x = 4;
@@ -23545,7 +23486,6 @@
/area/science/mixing/chamber)
"aMh" = (
/obj/structure/closet/secure_closet/security/engine,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -23983,7 +23923,6 @@
/area/storage/tech)
"aMK" = (
/obj/effect/landmark/secequipment,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -24086,21 +24025,11 @@
},
/area/hallway/primary/fore)
"aMV" = (
-/obj/effect/turf_decal/tile/blue{
- dir = 4
- },
-/obj/effect/turf_decal/tile/blue{
- dir = 8
- },
-/obj/effect/turf_decal/tile/yellow{
+/obj/effect/turf_decal/loading_area{
dir = 1
},
-/obj/effect/turf_decal/tile/yellow,
-/obj/machinery/portable_atmospherics/pump,
-/obj/machinery/atmospherics/components/unary/portables_connector/visible,
-/obj/effect/turf_decal/box,
-/turf/open/floor/plasteel/showroomfloor,
-/area/engine/atmos)
+/turf/closed/wall/r_wall,
+/area/maintenance/port/aft)
"aMW" = (
/obj/structure/reflector/single/anchored{
dir = 9
@@ -24374,7 +24303,6 @@
"aNs" = (
/obj/structure/closet/cardboard,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -25158,7 +25086,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
-/obj/effect/turf_decal/bot,
/obj/structure/sign/warning/securearea{
name = "EMERGENCY STORAGE";
pixel_y = -32
@@ -25963,7 +25890,6 @@
/obj/structure/closet/secure_closet/medical3,
/obj/item/storage/belt/medical,
/obj/item/clothing/neck/stethoscope,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
@@ -26298,7 +26224,6 @@
/obj/structure/closet/secure_closet/medical3,
/obj/item/storage/belt/medical,
/obj/item/clothing/neck/stethoscope,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
@@ -27142,7 +27067,6 @@
pixel_x = -24;
pixel_y = 24
},
-/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel/dark,
/area/security/checkpoint/science/research)
"aRA" = (
@@ -27370,7 +27294,6 @@
/turf/open/floor/plasteel/dark,
/area/medical/chemistry)
"aRP" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet{
name = "medical locker"
},
@@ -27652,7 +27575,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/delivery,
/obj/item/radio/intercom{
pixel_y = 22
},
@@ -31346,7 +31268,6 @@
/turf/open/floor/plasteel/dark,
/area/science/mixing)
"aXB" = (
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -33468,7 +33389,6 @@
/area/science/mixing)
"baT" = (
/obj/structure/closet/secure_closet/security/med,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
@@ -34857,7 +34777,6 @@
/turf/open/floor/plating,
/area/maintenance/central)
"bcZ" = (
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/red{
dir = 1
},
@@ -35287,7 +35206,6 @@
/turf/open/floor/engine,
/area/science/xenobiology)
"bdE" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet{
name = "kitchen closet"
@@ -36156,7 +36074,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
-/obj/effect/turf_decal/bot,
/obj/structure/closet/firecloset,
/turf/open/floor/plasteel/dark,
/area/hallway/primary/starboard)
@@ -36305,7 +36222,6 @@
/area/maintenance/port)
"bfc" = (
/obj/effect/decal/cleanable/cobweb/cobweb2,
-/obj/effect/turf_decal/bot,
/obj/structure/closet/l3closet,
/turf/open/floor/plasteel/dark,
/area/maintenance/port)
@@ -36467,7 +36383,6 @@
/obj/structure/cable{
icon_state = "0-2"
},
-/obj/effect/turf_decal/box,
/obj/machinery/light{
dir = 1
},
@@ -38196,7 +38111,6 @@
/turf/open/floor/plating,
/area/maintenance/port)
"bib" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/l3closet,
/turf/open/floor/plasteel/dark,
/area/maintenance/port)
@@ -38400,7 +38314,6 @@
/area/medical/virology)
"biq" = (
/obj/structure/closet/wardrobe/miner,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -38458,7 +38371,6 @@
/turf/open/floor/plasteel/dark,
/area/quartermaster/miningoffice)
"biu" = (
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -38902,7 +38814,6 @@
/area/quartermaster/qm)
"bja" = (
/obj/structure/closet/secure_closet/quartermaster,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
@@ -39184,7 +39095,6 @@
/area/quartermaster/office)
"bju" = (
/obj/structure/closet/secure_closet/miner,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -39196,7 +39106,6 @@
/area/quartermaster/miningoffice)
"bjv" = (
/obj/structure/closet/secure_closet/miner,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -40003,7 +39912,6 @@
/area/security/checkpoint/supply)
"bkK" = (
/obj/structure/closet/secure_closet/miner,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -41261,8 +41169,12 @@
/turf/open/floor/plasteel/dark,
/area/hallway/primary/aft)
"bmA" = (
-/obj/machinery/smartfridge,
-/turf/closed/wall,
+/obj/structure/table/reinforced,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id = "kitchen";
+ name = "Serving Hatch"
+ },
+/turf/open/floor/plating,
/area/crew_quarters/kitchen)
"bmB" = (
/obj/machinery/status_display/evac,
@@ -41325,7 +41237,6 @@
/turf/open/floor/plasteel/dark,
/area/hydroponics)
"bmH" = (
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/red{
dir = 1
},
@@ -41465,7 +41376,6 @@
id = "kitchen_2";
name = "Hallway Hatch"
},
-/obj/structure/window/reinforced,
/turf/open/floor/plating,
/area/crew_quarters/kitchen)
"bmT" = (
@@ -41663,7 +41573,6 @@
"bnk" = (
/obj/structure/disposalpipe/segment,
/obj/structure/closet/wardrobe/white,
-/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/locker)
"bnl" = (
@@ -41762,7 +41671,6 @@
/turf/open/floor/grass,
/area/hydroponics)
"bnr" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/secure_closet/personal,
/obj/item/storage/backpack,
/obj/item/storage/backpack/satchel,
@@ -42556,7 +42464,6 @@
/turf/open/floor/plating,
/area/maintenance/central)
"boz" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/secure_closet/hydroponics,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -42721,7 +42628,6 @@
/turf/open/floor/plasteel/dark,
/area/crew_quarters/kitchen)
"boJ" = (
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/red{
dir = 1
},
@@ -42801,7 +42707,6 @@
"boO" = (
/obj/structure/disposalpipe/segment,
/obj/structure/closet/wardrobe/grey,
-/obj/effect/turf_decal/bot,
/obj/machinery/light{
dir = 8
},
@@ -42841,7 +42746,6 @@
/turf/open/floor/plasteel/dark,
/area/crew_quarters/kitchen)
"boR" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/secure_closet/personal,
/obj/item/storage/backpack,
/obj/item/storage/backpack/satchel,
@@ -42970,7 +42874,6 @@
/turf/open/floor/plasteel/dark,
/area/crew_quarters/kitchen)
"bpc" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/firecloset,
/turf/open/floor/plating,
/area/maintenance/port)
@@ -43138,7 +43041,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/firecloset,
/turf/open/floor/plasteel/dark,
/area/hallway/primary/port)
@@ -43448,7 +43350,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/emcloset,
/turf/open/floor/plasteel/dark,
/area/hallway/primary/port)
@@ -44133,7 +44034,6 @@
/turf/open/floor/plasteel/dark,
/area/crew_quarters/kitchen)
"bqQ" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/secure_closet/hydroponics,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -45527,7 +45427,6 @@
/area/crew_quarters/bar)
"bsV" = (
/obj/structure/closet/secure_closet/security/cargo,
-/obj/effect/turf_decal/delivery,
/obj/item/crowbar,
/obj/item/book/manual/wiki/security_space_law,
/obj/effect/turf_decal/tile/neutral,
@@ -47528,7 +47427,6 @@
},
/area/hallway/primary/port)
"bwg" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet,
/obj/item/stack/rods/ten,
/obj/item/stock_parts/matter_bin,
@@ -49072,7 +48970,6 @@
dir = 8
},
/obj/structure/closet/secure_closet/captains,
-/obj/effect/turf_decal/delivery,
/obj/machinery/airalarm{
dir = 8;
pixel_x = 24
@@ -49082,7 +48979,6 @@
"byF" = (
/obj/structure/closet/secure_closet/engineering_personal,
/obj/item/clothing/suit/hooded/wintercoat/engineering,
-/obj/effect/turf_decal/delivery,
/obj/machinery/newscaster{
pixel_y = 30
},
@@ -49364,7 +49260,6 @@
"byZ" = (
/obj/structure/disposalpipe/segment,
/obj/structure/closet/wardrobe/black,
-/obj/effect/turf_decal/bot,
/obj/item/clothing/under/trendy_fit,
/obj/item/clothing/under/trendy_fit,
/turf/open/floor/plasteel/dark,
@@ -49536,7 +49431,6 @@
/turf/open/floor/plating,
/area/ai_monitored/turret_protected/ai_upload)
"bzn" = (
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -52750,7 +52644,6 @@
dir = 1
},
/obj/structure/closet/secure_closet/hop,
-/obj/effect/turf_decal/bot,
/obj/machinery/airalarm{
dir = 4;
pixel_x = -22
@@ -53093,7 +52986,6 @@
dir = 1
},
/obj/structure/closet/firecloset,
-/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel/dark,
/area/hallway/primary/aft)
"bEM" = (
@@ -54624,7 +54516,6 @@
dir = 1
},
/obj/structure/closet/emcloset,
-/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel/dark,
/area/hallway/primary/aft)
"bHn" = (
@@ -56582,10 +56473,6 @@
pixel_x = -4;
pixel_y = 4
},
-/obj/item/megaphone{
- pixel_x = 4;
- pixel_y = 4
- },
/obj/item/pen,
/obj/effect/decal/cleanable/cobweb/cobweb2,
/obj/effect/turf_decal/tile/neutral{
@@ -56669,7 +56556,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/structure/sink{
dir = 8;
pixel_x = -12;
@@ -56682,7 +56568,6 @@
dir = 1
},
/obj/effect/decal/cleanable/cobweb,
-/obj/effect/decal/cleanable/blood/gibs/limb,
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/turf/open/floor/plasteel/showroomfloor,
/area/security/prison)
@@ -56745,7 +56630,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/door_timer{
id = "Cell 3";
name = "Cell 3";
@@ -56765,7 +56649,6 @@
/turf/open/floor/plasteel/dark,
/area/security/warden)
"bKA" = (
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet{
name = "detective closet"
@@ -56828,7 +56711,6 @@
/area/crew_quarters/locker)
"bKD" = (
/obj/structure/closet/boxinggloves,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -56867,7 +56749,6 @@
/area/crew_quarters/toilet/restrooms)
"bKH" = (
/obj/structure/closet/masks,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -56939,7 +56820,6 @@
/area/maintenance/port/aft)
"bKN" = (
/obj/structure/closet/athletic_mixed,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -56957,7 +56837,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
-/obj/effect/turf_decal/bot,
/obj/structure/closet/emcloset,
/obj/structure/sign/warning/securearea{
name = "EMERGENCY STORAGE";
@@ -57772,7 +57651,6 @@
},
/obj/effect/decal/cleanable/blood/old,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/decal/remains/human,
/obj/machinery/atmospherics/pipe/manifold/supply/hidden,
/obj/machinery/airalarm{
dir = 1;
@@ -58252,12 +58130,12 @@
/turf/open/floor/plasteel,
/area/hallway/primary/starboard)
"bMz" = (
-/obj/machinery/computer/prisoner,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
/obj/effect/turf_decal/bot,
+/obj/machinery/computer/prisoner,
/turf/open/floor/plasteel/dark,
/area/security/prison)
"bMA" = (
@@ -59029,7 +58907,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/light/small{
dir = 8
},
@@ -59911,7 +59788,6 @@
"bPa" = (
/obj/structure/flora/ausbushes/ywflowers,
/obj/effect/decal/cleanable/blood/old,
-/mob/living/simple_animal/butterfly,
/turf/open/floor/grass,
/area/security/prison)
"bPb" = (
@@ -60030,7 +59906,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/structure/sink{
dir = 8;
pixel_x = -12;
@@ -61759,7 +61634,6 @@
/obj/structure/closet/secure_closet/brig{
name = "Prisoner Locker"
},
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -61781,7 +61655,6 @@
/area/hallway/secondary/exit/departure_lounge)
"bRG" = (
/obj/structure/closet/secure_closet/warden,
-/obj/effect/turf_decal/delivery,
/obj/machinery/power/apc/highcap/five_k{
areastring = "/area/security/warden";
dir = 8;
@@ -61818,7 +61691,6 @@
/obj/structure/closet/secure_closet/brig{
name = "Prisoner Locker"
},
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -61842,7 +61714,6 @@
/obj/structure/closet/secure_closet/brig{
name = "Prisoner Locker"
},
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -62701,7 +62572,6 @@
/obj/structure/closet/l3closet/security,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/cobweb/cobweb2,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
@@ -62750,7 +62620,6 @@
"bTa" = (
/obj/structure/closet/secure_closet/evidence,
/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/bot,
/obj/machinery/light/small{
dir = 1
},
@@ -63117,7 +62986,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/structure/sink{
dir = 8;
pixel_x = -12;
@@ -63195,7 +63063,6 @@
/turf/open/floor/plasteel/showroomfloor,
/area/security/prison)
"bTL" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet{
name = "security locker"
},
@@ -63673,7 +63540,6 @@
},
/area/hallway/primary/aft)
"bUB" = (
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -64158,8 +64024,6 @@
dir = 4
},
/obj/effect/turf_decal/tile/neutral,
-/obj/effect/decal/cleanable/blood/old,
-/obj/effect/decal/cleanable/blood/gibs/limb,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
@@ -64492,7 +64356,6 @@
"bVP" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/closet/bombcloset/security,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -64845,7 +64708,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/door_timer{
id = "Cell 6";
name = "Cell 6";
@@ -64896,7 +64758,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/door_timer{
id = "Cell 5";
name = "Cell 5";
@@ -64915,7 +64776,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/door_timer{
id = "Cell 4";
name = "Cell 4";
@@ -65068,7 +64928,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/door_timer{
id = "Cell 2";
name = "Cell 2";
@@ -65093,7 +64952,6 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/door_timer{
id = "Cell 1";
name = "Cell 1";
@@ -67208,7 +67066,6 @@
/turf/open/floor/plasteel/dark/corner,
/area/hallway/primary/starboard)
"bZL" = (
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -67288,7 +67145,6 @@
"bZQ" = (
/obj/structure/closet/l3closet/janitor,
/obj/structure/window/reinforced,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
@@ -68259,8 +68115,6 @@
/area/maintenance/starboard)
"cbo" = (
/obj/structure/closet/secure_closet/engineering_chief,
-/obj/effect/turf_decal/delivery,
-/obj/item/gun/energy/e_gun/mini,
/obj/structure/extinguisher_cabinet{
pixel_x = 24
},
@@ -68965,7 +68819,7 @@
/area/maintenance/starboard)
"ccv" = (
/obj/structure/sign/warning/securearea,
-/turf/closed/wall,
+/turf/closed/wall/r_wall,
/area/security/main)
"ccw" = (
/obj/effect/turf_decal/tile/yellow{
@@ -69011,7 +68865,6 @@
/turf/closed/mineral/random/labormineral,
/area/space/nearstation)
"ccB" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/firecloset,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel/dark,
@@ -69644,7 +69497,6 @@
},
/obj/effect/turf_decal/tile/red,
/obj/structure/closet/secure_closet/engineering_electrical,
-/obj/effect/turf_decal/box,
/turf/open/floor/plasteel,
/area/engine/break_room)
"cdO" = (
@@ -70940,7 +70792,6 @@
/obj/effect/turf_decal/tile/yellow,
/obj/effect/turf_decal/tile/red,
/obj/structure/closet/secure_closet/engineering_welding,
-/obj/effect/turf_decal/box,
/turf/open/floor/plasteel,
/area/engine/break_room)
"cfI" = (
@@ -71072,7 +70923,6 @@
/turf/closed/wall/r_wall/rust,
/area/ai_monitored/storage/satellite)
"cgb" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet/emcloset,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel/dark,
@@ -71177,7 +71027,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
-/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
/area/crew_quarters/heads/hos)
"cgq" = (
@@ -71591,7 +71440,6 @@
"cgW" = (
/obj/structure/closet/secure_closet/engineering_personal,
/obj/item/clothing/suit/hooded/wintercoat/engineering,
-/obj/effect/turf_decal/delivery,
/obj/machinery/light{
dir = 1
},
@@ -71618,7 +71466,6 @@
"cgY" = (
/obj/structure/closet/crate/coffin,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
@@ -74485,7 +74332,6 @@
},
/area/maintenance/starboard)
"clB" = (
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/radiation,
/obj/item/clothing/glasses/meson,
/turf/open/floor/plasteel/dark,
@@ -74690,13 +74536,11 @@
/turf/closed/wall/r_wall,
/area/engine/gravity_generator)
"clV" = (
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/radiation,
/obj/item/clothing/glasses/meson,
/turf/open/floor/plasteel/dark,
/area/engine/gravity_generator)
"clW" = (
-/obj/effect/turf_decal/delivery,
/obj/structure/closet/radiation,
/obj/item/clothing/glasses/meson,
/obj/machinery/power/apc{
@@ -75556,7 +75400,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/firealarm{
dir = 4;
@@ -78861,6 +78704,9 @@
},
/obj/structure/barricade/wooden/crude,
/obj/effect/decal/cleanable/dirt,
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"ctj" = (
@@ -80488,6 +80334,9 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"cwh" = (
@@ -80500,6 +80349,9 @@
icon_state = "1-2"
},
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"cwi" = (
@@ -80885,6 +80737,9 @@
},
/obj/structure/barricade/wooden/crude,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"cwR" = (
@@ -80893,7 +80748,6 @@
},
/obj/effect/decal/cleanable/cobweb/cobweb2,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/structure/closet,
/obj/item/stack/packageWrap,
/obj/item/storage/bag/trash,
@@ -81247,7 +81101,6 @@
},
/area/maintenance/port/fore)
"cxL" = (
-/obj/effect/turf_decal/bot,
/obj/structure/closet{
name = "engineering locker"
},
@@ -81526,6 +81379,9 @@
"cyu" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/barricade/wooden/crude,
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plating{
icon_state = "platingdmg3"
},
@@ -81558,7 +81414,6 @@
/turf/closed/wall,
/area/maintenance/port/aft)
"cyz" = (
-/obj/effect/turf_decal/bot,
/obj/structure/rack,
/obj/item/crowbar/red,
/obj/item/restraints/handcuffs,
@@ -82027,7 +81882,6 @@
/obj/structure/closet/crate/coffin,
/obj/effect/decal/cleanable/cobweb/cobweb2,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/delivery,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -82044,6 +81898,9 @@
/obj/structure/cable{
icon_state = "1-2"
},
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"czL" = (
@@ -82053,6 +81910,9 @@
/obj/structure/barricade/wooden/crude,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1,
+/obj/effect/turf_decal/loading_area{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/maintenance/port/fore)
"czP" = (
@@ -82186,7 +82046,6 @@
"cAi" = (
/obj/structure/closet/crate/coffin,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
@@ -82204,7 +82063,6 @@
"cAk" = (
/obj/structure/closet/crate/coffin,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
@@ -82219,7 +82077,6 @@
/turf/open/floor/plasteel/dark,
/area/chapel/office)
"cAo" = (
-/obj/effect/turf_decal/bot,
/obj/structure/frame/computer{
anchored = 1
},
@@ -82234,7 +82091,6 @@
/turf/closed/wall/rust,
/area/chapel/office)
"cAq" = (
-/obj/effect/turf_decal/bot,
/obj/structure/frame/computer{
anchored = 1
},
@@ -82553,7 +82409,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/machinery/light{
dir = 8
},
@@ -82902,7 +82757,6 @@
"cCr" = (
/obj/effect/decal/cleanable/cobweb,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -82931,7 +82785,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/effect/turf_decal/bot,
/obj/effect/decal/cleanable/dirt,
/obj/item/clothing/gloves/color/white,
/obj/item/clothing/under/maid{
@@ -83807,7 +83660,6 @@
"cEB" = (
/obj/effect/decal/cleanable/cobweb/cobweb2,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
@@ -85018,8 +84870,10 @@
},
/area/maintenance/aft)
"cIe" = (
-/obj/machinery/door/airlock/maintenance{
- name = "xenobiology maintenance";
+/obj/machinery/door/airlock/research{
+ glass = 1;
+ name = "Slime Euthanization Chamber";
+ opacity = 0;
req_access_txt = "55"
},
/turf/open/floor/plasteel/dark,
@@ -86190,7 +86044,6 @@
/obj/machinery/light/small{
dir = 1
},
-/obj/effect/turf_decal/bot,
/turf/open/floor/plating,
/area/maintenance/starboard/aft)
"cKY" = (
@@ -87349,10 +87202,6 @@
"dis" = (
/turf/closed/wall/r_wall/rust,
/area/bridge)
-"dww" = (
-/obj/effect/decal/cleanable/dirt,
-/turf/open/floor/plating/asteroid/airless,
-/area/space/nearstation)
"dxq" = (
/obj/effect/turf_decal/bot,
/obj/effect/turf_decal/tile/neutral{
@@ -88060,8 +87909,13 @@
/turf/closed/wall/rust,
/area/crew_quarters/heads/hor)
"nJw" = (
-/obj/machinery/smartfridge,
-/turf/closed/wall/rust,
+/obj/structure/table/reinforced,
+/obj/machinery/door/firedoor,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id = "kitchen";
+ name = "Serving Hatch"
+ },
+/turf/open/floor/plating,
/area/crew_quarters/kitchen)
"nNA" = (
/turf/closed/wall/rust,
@@ -106127,10 +105981,10 @@ cDB
cfb
aey
ajx
+aMV
+ajx
+ajx
ajx
-ajd
-ajd
-ajd
anZ
cmt
cko
@@ -118204,7 +118058,7 @@ bGJ
cdr
bGG
awu
-aMV
+cgF
chz
ciA
aGl
diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index 0a64f01a43..6604811d34 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -2,32 +2,57 @@
#define CHECK_DNA_AND_SPECIES(C) if((!(C.dna)) || (!(C.dna.species))) return
-//Defines copying names of mutations in all cases, make sure to change this if you change mutation's name
-#define HULK "Hulk"
-#define XRAY "X Ray Vision"
-#define COLDRES "Cold Resistance"
-#define TK "Telekinesis"
-#define NERVOUS "Nervousness"
-#define EPILEPSY "Epilepsy"
-#define MUTATE "Unstable DNA"
-#define COUGH "Cough"
-#define DWARFISM "Dwarfism"
-#define CLOWNMUT "Clumsiness"
-#define TOURETTES "Tourettes Syndrome"
-#define DEAFMUT "Deafness"
-#define BLINDMUT "Blindness"
-#define RACEMUT "Monkified"
-#define BADSIGHT "Near Sightness"
-#define LASEREYES "Laser Eyes"
-#define CHAMELEON "Chameleon"
-#define WACKY "Wacky"
-#define MUT_MUTE "Mute"
-#define SMILE "Smile"
-#define STONER "Stoner"
-#define UNINTELLIGIBLE "Unintelligible"
-#define SWEDISH "Swedish"
-#define CHAV "Chav"
-#define ELVIS "Elvis"
+//Defines copying names of mutations in all cases, make sure to change this if you change mutation's type
+#define HULK /datum/mutation/human/hulk
+#define XRAY /datum/mutation/human/thermal/x_ray
+#define SPACEMUT /datum/mutation/human/space_adaptation
+#define TK /datum/mutation/human/telekinesis
+#define NERVOUS /datum/mutation/human/nervousness
+#define EPILEPSY /datum/mutation/human/epilepsy
+#define MUTATE /datum/mutation/human/bad_dna
+#define COUGH /datum/mutation/human/cough
+#define DWARFISM /datum/mutation/human/dwarfism
+#define GIGANTISM /datum/mutation/human/gigantism
+#define CLOWNMUT /datum/mutation/human/clumsy
+#define TOURETTES /datum/mutation/human/tourettes
+#define DEAFMUT /datum/mutation/human/deaf
+#define BLINDMUT /datum/mutation/human/blind
+#define RACEMUT /datum/mutation/human/race
+#define BADSIGHT /datum/mutation/human/nearsight
+#define LASEREYES /datum/mutation/human/laser_eyes
+#define CHAMELEON /datum/mutation/human/chameleon
+#define WACKY /datum/mutation/human/wacky
+#define MUT_MUTE /datum/mutation/human/mute
+#define SMILE /datum/mutation/human/smile
+#define STONER /datum/mutation/human/stoner
+#define UNINTELLIGIBLE /datum/mutation/human/unintelligible
+#define SWEDISH /datum/mutation/human/swedish
+#define CHAV /datum/mutation/human/chav
+#define ELVIS /datum/mutation/human/elvis
+#define RADIOACTIVE /datum/mutation/human/radioactive
+#define GLOWY /datum/mutation/human/glow
+#define ANTIGLOWY /datum/mutation/human/glow/anti
+#define TELEPATHY /datum/mutation/human/telepathy
+#define FIREBREATH /datum/mutation/human/firebreath
+#define VOID /datum/mutation/human/void
+#define TONGUESPIKE /datum/mutation/human/tongue_spike
+#define TONGUESPIKECHEM /datum/mutation/human/tongue_spike/chem
+#define STRONG /datum/mutation/human/strong
+#define STIMMED /datum/mutation/human/stimmed
+#define FIRESWEAT /datum/mutation/human/fire
+#define THERMAL /datum/mutation/human/thermal
+#define ANTENNA /datum/mutation/human/antenna
+#define PARANOIA /datum/mutation/human/paranoia
+#define MINDREAD /datum/mutation/human/mindreader
+#define INSULATED /datum/mutation/human/insulated
+#define SHOCKTOUCH /datum/mutation/human/shock
+#define OLFACTION /datum/mutation/human/olfaction
+#define ACIDFLESH /datum/mutation/human/acidflesh
+#define BADBLINK /datum/mutation/human/badblink
+#define SPASTIC /datum/mutation/human/spastic
+#define GELADIKINESIS /datum/mutation/human/geladikinesis
+#define CRYOKINESIS /datum/mutation/human/cryokinesis
+#define SPIDER_WEB /datum/mutation/human/webbing
#define UI_CHANGED "ui changed"
#define UE_CHANGED "ue changed"
@@ -39,10 +64,12 @@
//Types of usual mutations
#define POSITIVE 1
#define NEGATIVE 2
-#define MINOR_NEGATIVE 3
+#define MINOR_NEGATIVE 4
-//Mutations that cant be taken from genetics and are not in SE
-#define NON_SCANNABLE -1
+//Mutation classes. Normal being on them, extra being additional mutations with instability and other being stuff you dont want people to fuck with like wizard mutate
+#define MUT_NORMAL 1
+#define MUT_EXTRA 2
+#define MUT_OTHER 3
//DNA - Because fuck you and your magic numbers being all over the codebase.
#define DNA_BLOCK_SIZE 3
@@ -63,7 +90,8 @@
#define DNA_MUTANTMARKING_BLOCK 13
#define DNA_TAUR_BLOCK 14
-#define DNA_STRUC_ENZYMES_BLOCKS 18
+#define DNA_SEQUENCE_LENGTH 4
+#define DNA_MUTATION_BLOCKS 8
#define DNA_UNIQUE_ENZYMES_LEN 32
//Transformation proc stuff
@@ -141,7 +169,13 @@
#define STANDARD_ORGAN_HEALING (1/(15 MINUTES / (2 SECONDS)))
#define STANDARD_ORGAN_DECAY (1/(15 MINUTES / (2 SECONDS))) //designed to fail organs when left to decay for ~15 minutes. 2 SECOND is SSmobs tickrate.
+
+//used for the can_chromosome var on mutations
+#define CHROMOSOME_NEVER 0
+#define CHROMOSOME_NONE 1
+#define CHROMOSOME_USED 2
+
#define G_MALE 1
#define G_FEMALE 2
#define G_PLURAL 3
-#define G_NEUTER 4
+#define G_NEUTER 4
\ No newline at end of file
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index b849e272e7..145055206c 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -72,3 +72,9 @@
#define CAT_STRUCTURE "Structures"
#define MARTIALART_HUNTER "hunter-fu"
+
+//Blob
+#define BLOB_REROLL_TIME 2400 // blob gets a free reroll every X time
+#define BLOB_SPREAD_COST 4
+#define BLOB_ATTACK_REFUND 2 //blob refunds this much if it attacks and doesn't spread
+#define BLOB_REFLECTOR_COST 15
\ No newline at end of file
diff --git a/code/__DEFINES/dcs/flags.dm b/code/__DEFINES/dcs/flags.dm
new file mode 100644
index 0000000000..128c9f1938
--- /dev/null
+++ b/code/__DEFINES/dcs/flags.dm
@@ -0,0 +1,41 @@
+/// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type.
+/// `parent` must not be modified if this is to be returned.
+/// This will be noted in the runtime logs
+#define COMPONENT_INCOMPATIBLE 1
+/// Returned in PostTransfer to prevent transfer, similar to `COMPONENT_INCOMPATIBLE`
+#define COMPONENT_NOTRANSFER 2
+
+/// Return value to cancel attaching
+#define ELEMENT_INCOMPATIBLE 1
+
+// /datum/element flags
+/// Causes the detach proc to be called when the host object is being deleted
+#define ELEMENT_DETACH (1 << 0)
+/**
+ * Only elements created with the same arguments given after `id_arg_index` share an element instance
+ * The arguments are the same when the text and number values are the same and all other values have the same ref
+ */
+#define ELEMENT_BESPOKE (1 << 1)
+
+// How multiple components of the exact same type are handled in the same datum
+/// old component is deleted (default)
+#define COMPONENT_DUPE_HIGHLANDER 0
+/// duplicates allowed
+#define COMPONENT_DUPE_ALLOWED 1
+/// new component is deleted
+#define COMPONENT_DUPE_UNIQUE 2
+/// old component is given the initialization args of the new
+#define COMPONENT_DUPE_UNIQUE_PASSARGS 4
+/// each component of the same type is consulted as to whether the duplicate should be allowed
+#define COMPONENT_DUPE_SELECTIVE 5
+
+//Redirection component init flags
+#define REDIRECT_TRANSFER_WITH_TURF 1
+
+//Arch
+#define ARCH_PROB "probability" //Probability for each item
+#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
+
+//Ouch my toes!
+#define CALTROP_BYPASS_SHOES 1
+#define CALTROP_IGNORE_WALKERS 2
diff --git a/code/__DEFINES/dcs/helpers.dm b/code/__DEFINES/dcs/helpers.dm
new file mode 100644
index 0000000000..b13530cdf7
--- /dev/null
+++ b/code/__DEFINES/dcs/helpers.dm
@@ -0,0 +1,16 @@
+/// Used to trigger signals and call procs registered for that signal
+/// The datum hosting the signal is automaticaly added as the first argument
+/// Returns a bitfield gathered from all registered procs
+/// Arguments given here are packaged in a list and given to _SendSignal
+#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
+
+#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
+
+/// A wrapper for _AddElement that allows us to pretend we're using normal named arguments
+#define AddElement(arguments...) _AddElement(list(##arguments))
+
+/// A wrapper for _RemoveElement that allows us to pretend we're using normal named arguments
+#define RemoveElement(arguments...) _RemoveElement(list(##arguments))
+
+/// A wrapper for _AddComponent that allows us to pretend we're using normal named arguments
+#define AddComponent(arguments...) _AddComponent(list(##arguments))
\ No newline at end of file
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/dcs/signals.dm
similarity index 94%
rename from code/__DEFINES/components.dm
rename to code/__DEFINES/dcs/signals.dm
index 56cf681a27..c4e57154c2 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -1,28 +1,3 @@
-#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
-
-#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
-
-#define COMPONENT_INCOMPATIBLE 1
-#define COMPONENT_NOTRANSFER 2
-
-#define ELEMENT_INCOMPATIBLE 1 // Return value to cancel attaching
-
-// /datum/element flags
-/// Causes the detach proc to be called when the host object is being deleted
-#define ELEMENT_DETACH (1 << 0)
-/**
- * Only elements created with the same arguments given after `id_arg_index` share an element instance
- * The arguments are the same when the text and number values are the same and all other values have the same ref
- */
-#define ELEMENT_BESPOKE (1 << 1)
-
-// How multiple components of the exact same type are handled in the same datum
-
-#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default)
-#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
-#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted
-#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new
-
// All signals. Format:
// When the signal is called: (signal arguments)
// All signals send the source datum of the signal as the first argument
@@ -206,7 +181,7 @@
#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living)
#define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living)
#define COMSIG_LIVING_EXTINGUISHED "living_extinguished" //from base of mob/living/ExtinguishMob() (/mob/living)
-#define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage)
+#define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage, source, siemens_coeff, flags)
#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: ()
#define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive)
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client)
@@ -224,6 +199,7 @@
// /mob/living/carbon signals
#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity))
+#define COMSIG_CARBON_IDENTITY_TRANSFERRED_TO "carbon_id_transferred_to" //from datum/dna/transfer_identity(): (datum/dna, transfer_SE)
// /mob/living/simple_animal/hostile signals
#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget"
@@ -289,6 +265,9 @@
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
+#define COMSIG_HUMAN_PREFS_COPIED_TO "human_prefs_copied_to" //from datum/preferences/copy_to(): (datum/preferences, icon_updates, roundstart_checks)
+#define COMSIG_HUMAN_HARDSET_DNA "human_hardset_dna" //from mob/living/carbon/human/hardset_dna(): (ui, list/mutation_index, newreal_name, newblood_type, datum/species, newfeatures)
+#define COMSIG_HUMAN_ON_RANDOMIZE "humman_on_randomize" //from base of proc/randomize_human()
// /datum/species signals
#define COMSIG_SPECIES_GAIN "species_gain" //from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species)
@@ -355,19 +334,6 @@
#define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action)
#define COMPONENT_ACTION_BLOCK_TRIGGER 1
-/*******Non-Signal Component Related Defines*******/
-
-//Redirection component init flags
-#define REDIRECT_TRANSFER_WITH_TURF 1
-
-//Arch
-#define ARCH_PROB "probability" //Probability for each item
-#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
-
-//Ouch my toes!
-#define CALTROP_BYPASS_SHOES 1
-#define CALTROP_IGNORE_WALKERS 2
-
//Xenobio hotkeys
#define COMSIG_XENO_SLIME_CLICK_CTRL "xeno_slime_click_ctrl" //from slime CtrlClickOn(): (/mob)
#define COMSIG_XENO_SLIME_CLICK_ALT "xeno_slime_click_alt" //from slime AltClickOn(): (/mob)
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index c1686560d6..69e7ac99fc 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -210,6 +210,17 @@
#define MAX_CHICKENS 50
+///Flags used by the flags parameter of electrocute act.
+
+///Makes it so that the shock doesn't take gloves into account.
+#define SHOCK_NOGLOVES (1 << 0)
+///Used when the shock is from a tesla bolt.
+#define SHOCK_TESLA (1 << 1)
+///Used when an illusion shocks something. Makes the shock deal stamina damage and not trigger certain secondary effects.
+#define SHOCK_ILLUSION (1 << 2)
+///The shock doesn't stun.
+#define SHOCK_NOSTUN (1 << 3)
+
#define INCORPOREAL_MOVE_BASIC 1
#define INCORPOREAL_MOVE_SHADOW 2 // leaves a trail of shadows
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 9d09714cc0..ef86db07cb 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -118,3 +118,4 @@
#define STATUS_EFFECT_RAINBOWPROTECTION /datum/status_effect/rainbow_protection //Invulnerable and pacifistic
#define STATUS_EFFECT_SLIMESKIN /datum/status_effect/slimeskin //Increased armor
+#define STATUS_EFFECT_DNA_MELT /datum/status_effect/dna_melt //usually does something horrible to you when you hit 100 genetic instability
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 373945d951..ba96b7cc7f 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -83,6 +83,7 @@
#define TRAIT_DISFIGURED "disfigured"
#define TRAIT_XENO_HOST "xeno_host" //Tracks whether we're gonna be a baby alien's mummy.
#define TRAIT_STUNIMMUNE "stun_immunity"
+#define TRAIT_TASED_RESISTANCE "tased_resistance" //prevents you from suffering most of the effects of being tased
#define TRAIT_SLEEPIMMUNE "sleep_immunity"
#define TRAIT_PUSHIMMUNE "push_immunity"
#define TRAIT_SHOCKIMMUNE "shock_immunity"
@@ -194,6 +195,8 @@
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
#define TRAIT_FREESPRINT "free_sprinting"
+#define TRAIT_XRAY_VISION "xray_vision"
+#define TRAIT_THERMAL_VISION "thermal_vision"
#define TRAIT_NO_TELEPORT "no-teleport" //you just can't
#define TRAIT_NO_INTERNALS "no-internals"
#define TRAIT_NO_ALCOHOL "alcohol_intolerance"
diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm
index 4ae30838e4..ffc1bcc6b7 100644
--- a/code/__HELPERS/_cit_helpers.dm
+++ b/code/__HELPERS/_cit_helpers.dm
@@ -107,38 +107,6 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
if(!src.holder) return
message_admins("[key_name_admin(usr)] manually reloaded mentors")
-//Flavor Text
-/mob/proc/set_flavor()
- set name = "Set Flavor Text"
- set desc = "Sets an extended description of your character's features."
- set category = "IC"
-
- var/new_flavor = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", flavor_text, MAX_FAVOR_LEN, TRUE)
- if(!isnull(new_flavor))
- flavor_text = html_decode(new_flavor)
- to_chat(src, "Your flavor text has been updated.")
-
-//Flavor Text
-/mob/proc/set_flavor_2()
- set name = "Set Temporary Flavor Text"
- set desc = "Sets a description of your character's current appearance. Use this for emotions, poses etc."
- set category = "IC"
-
- var/new_flavor = stripped_multiline_input(usr, "Set the temporary flavor text in your 'examine' verb. This should be used only for things pertaining to the current round!", "Short-Term Flavor Text", flavor_text_2, MAX_FAVOR_LEN, TRUE)
- if(!isnull(new_flavor))
- flavor_text_2 = html_decode(new_flavor)
- to_chat(src, "Your temporary flavor text has been updated.")
-
-/mob/proc/print_flavor_text(flavor,temp = FALSE)
- if(!flavor)
- return
- // We are decoding and then encoding to not only get correct amount of characters, but also to prevent partial escaping characters being shown.
- var/msg = html_decode(replacetext(flavor, "\n", " "))
- if(length_char(msg) <= 40)
- return "[html_encode(msg)]"
- else
- return "[html_encode(copytext_char(msg, 1, 37))]... More..."
-
//LOOC toggles
/client/verb/listen_looc()
set name = "Show/Hide LOOC"
diff --git a/code/__HELPERS/dna.dm b/code/__HELPERS/dna.dm
new file mode 100644
index 0000000000..bb0c89d1f3
--- /dev/null
+++ b/code/__HELPERS/dna.dm
@@ -0,0 +1,12 @@
+//////////////////////////////////////////////////////////
+//A bunch of helpers to make genetics less of a headache//
+//////////////////////////////////////////////////////////
+
+#define GET_INITIALIZED_MUTATION(A) GLOB.all_mutations[A]
+#define GET_GENE_STRING(A, B) (B.mutation_index[A])
+#define GET_SEQUENCE(A) (GLOB.full_sequences[A])
+
+#define GET_MUTATION_STABILIZER(A) ((A.stabilizer_coeff < 0) ? 1 : A.stabilizer_coeff)
+#define GET_MUTATION_SYNCHRONIZER(A) ((A.synchronizer_coeff < 0) ? 1 : A.synchronizer_coeff)
+#define GET_MUTATION_POWER(A) ((A.power_coeff < 0) ? 1 : A.power_coeff)
+#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff)
\ No newline at end of file
diff --git a/code/_globalvars/genetics.dm b/code/_globalvars/genetics.dm
index 7016415d48..39702fd62f 100644
--- a/code/_globalvars/genetics.dm
+++ b/code/_globalvars/genetics.dm
@@ -21,8 +21,11 @@ GLOBAL_LIST(op_se_blocks)
GLOBAL_VAR(NULLED_SE)
GLOBAL_VAR(NULLED_UI)
-GLOBAL_LIST_EMPTY(global_mutations) // list of hidden mutation things
+GLOBAL_LIST_EMPTY(all_mutations)
+GLOBAL_LIST_EMPTY(full_sequences)
GLOBAL_LIST_EMPTY(bad_mutations)
GLOBAL_LIST_EMPTY(good_mutations)
-GLOBAL_LIST_EMPTY(not_good_mutations)
\ No newline at end of file
+GLOBAL_LIST_EMPTY(not_good_mutations)
+
+GLOBAL_LIST_EMPTY(mutation_recipes)
\ No newline at end of file
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index ffad931cd8..04d4c2888a 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -29,7 +29,6 @@ GLOBAL_LIST_EMPTY(available_ai_shells)
GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list(),list())) // One for each AI_* status define
GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs
GLOBAL_LIST_EMPTY(bots_list)
-GLOBAL_LIST_EMPTY(living_cameras)
GLOBAL_LIST_EMPTY(aiEyes)
GLOBAL_LIST_EMPTY(language_datum_instances)
diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm
index 8257b744d6..b9118da5f1 100644
--- a/code/_onclick/hud/blob_overmind.dm
+++ b/code/_onclick/hud/blob_overmind.dm
@@ -48,7 +48,7 @@
if(isovermind(usr))
var/mob/camera/blob/B = usr
if(!B.placed)
- B.place_blob_core(B.base_point_rate, 0)
+ B.place_blob_core(0)
B.transport_core()
/obj/screen/blob/Blobbernaut
@@ -91,26 +91,26 @@
var/mob/camera/blob/B = usr
B.create_factory()
-/obj/screen/blob/ReadaptChemical
+/obj/screen/blob/ReadaptStrain
icon_state = "ui_chemswap"
- name = "Readapt Chemical (40)"
- desc = "Randomly rerolls your chemical for 40 resources."
+ name = "Readapt Strain (40)"
+ desc = "Allows you to choose a new strain from 4 random choices for 40 resources."
-/obj/screen/blob/ReadaptChemical/MouseEntered(location,control,params)
+/obj/screen/blob/ReadaptStrain/MouseEntered(location,control,params)
if(hud && hud.mymob && isovermind(hud.mymob))
var/mob/camera/blob/B = hud.mymob
- if(B.free_chem_rerolls)
- name = "Readapt Chemical (FREE)"
- desc = "Randomly rerolls your chemical for free."
+ if(B.free_strain_rerolls)
+ name = "Readapt Strain (FREE)"
+ desc = "Randomly rerolls your strain for free."
else
name = initial(name)
desc = initial(desc)
..()
-/obj/screen/blob/ReadaptChemical/Click()
+/obj/screen/blob/ReadaptStrain/Click()
if(isovermind(usr))
var/mob/camera/blob/B = usr
- B.chemical_reroll()
+ B.strain_reroll()
/obj/screen/blob/RelocateCore
icon_state = "ui_swap"
@@ -175,7 +175,7 @@
using.hud = src
static_inventory += using
- using = new /obj/screen/blob/ReadaptChemical()
+ using = new /obj/screen/blob/ReadaptStrain()
using.screen_loc = ui_storage1
using.hud = src
static_inventory += using
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index cba53eb4a5..2fa2a8e85f 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -125,11 +125,7 @@
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
- if(iscarbon(src))
- var/mob/living/carbon/C = src
- C.bleed(totitemdamage)
- else
- add_splatter_floor(location)
+ add_splatter_floor(location)
if(totitemdamage >= 10 && get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
return TRUE //successful attack
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 974a0c0e88..f1db37d9fa 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -371,19 +371,18 @@
for(var/T in storyteller_cache)
var/datum/dynamic_storyteller/S = T
var/config_tag = initial(S.config_tag)
- var/final_weight = initial(S.weight)
- if(probabilities[config_tag]<=0)
+ var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight)
+ if(probability <= 0)
continue
- final_weight = probabilities[config_tag]
- if(SSpersistence.saved_storytellers.len == 3 && repeated_mode_adjust.len == 3)
+ if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len)
var/name = initial(S.name)
var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3)
var/adjustment = 0
while(recent_round)
adjustment += repeated_mode_adjust[recent_round]
recent_round = SSpersistence.saved_modes.Find(name,recent_round+1,0)
- final_weight *= ((100-adjustment)/100)
- runnable_storytellers[S] = final_weight
+ probability *= ((100-adjustment)/100)
+ runnable_storytellers[S] = probability
return runnable_storytellers
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 2d9264adf5..a1f8f098d3 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -82,17 +82,23 @@
config_entry_value = 600
min_val = 0
-/datum/config_entry/number/vote_autotransfer_initial //length of time before the first autotransfer vote is called (deciseconds, default 2 hours)
+/// Length of time before the first autotransfer vote is called (deciseconds, default 2 hours)
+/// Set to 0 to disable the subsystem altogether.
+/datum/config_entry/number/vote_autotransfer_initial
config_entry_value = 72000
min_val = 0
-/datum/config_entry/number/vote_autotransfer_interval //length of time to wait before subsequent autotransfer votes (deciseconds, default 30 minutes)
+///length of time to wait before subsequent autotransfer votes (deciseconds, default 30 minutes)
+/datum/config_entry/number/vote_autotransfer_interval
config_entry_value = 18000
min_val = 0
-/datum/config_entry/number/vote_autotransfer_maximum // maximum extensions until the round autoends
+/// maximum extensions until the round autoends.
+/// Set to 0 to force automatic crew transfer after the 'vote_autotransfer_initial' elapsed.
+/// Set to -1 to disable the maximum extensions cap.
+/datum/config_entry/number/vote_autotransfer_maximum
config_entry_value = 4
- min_val = 0
+ min_val = -1
/datum/config_entry/flag/default_no_vote // vote does not default to nochange/norestart
@@ -459,3 +465,6 @@
/datum/config_entry/number/max_bunker_days
config_entry_value = 7
min_val = 1
+
+/datum/config_entry/flag/minimaps_enabled
+ config_entry_value = TRUE
diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm
index d94526e658..35feab6648 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -16,7 +16,7 @@ SUBSYSTEM_DEF(atoms)
/datum/controller/subsystem/atoms/Initialize(timeofday)
GLOB.fire_overlay.appearance_flags = RESET_COLOR
- setupGenetics() //to set the mutations' place in structural enzymes, so monkey.initialize() knows where to put the monkey mutation.
+ setupGenetics() //to set the mutations' sequence.
initialized = INITIALIZATION_INNEW_MAPLOAD
InitializeAtoms()
return ..()
@@ -108,16 +108,19 @@ SUBSYSTEM_DEF(atoms)
BadInitializeCalls = SSatoms.BadInitializeCalls
/datum/controller/subsystem/atoms/proc/setupGenetics()
- var/list/avnums = new /list(DNA_STRUC_ENZYMES_BLOCKS)
- for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
- avnums[i] = i
- CHECK_TICK
-
- for(var/A in subtypesof(/datum/mutation/human))
- var/datum/mutation/human/B = new A()
- if(B.dna_block == NON_SCANNABLE)
+ var/list/mutations = subtypesof(/datum/mutation/human)
+ shuffle_inplace(mutations)
+ for(var/A in subtypesof(/datum/generecipe))
+ var/datum/generecipe/GR = A
+ GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
+ for(var/i in 1 to LAZYLEN(mutations))
+ var/path = mutations[i] //byond gets pissy when we do it in one line
+ var/datum/mutation/human/B = new path ()
+ B.alias = "Mutation #[i]"
+ GLOB.all_mutations[B.type] = B
+ GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
+ if(B.locked)
continue
- B.dna_block = pick_n_take(avnums)
if(B.quality == POSITIVE)
GLOB.good_mutations |= B
else if(B.quality == NEGATIVE)
diff --git a/code/controllers/subsystem/autotransfer.dm b/code/controllers/subsystem/autotransfer.dm
index 4f8fc3631d..ece203abba 100644
--- a/code/controllers/subsystem/autotransfer.dm
+++ b/code/controllers/subsystem/autotransfer.dm
@@ -1,3 +1,5 @@
+#define NO_MAXVOTES_CAP -1
+
SUBSYSTEM_DEF(autotransfer)
name = "Autotransfer Vote"
flags = SS_KEEP_TIMING | SS_BACKGROUND
@@ -7,21 +9,32 @@ SUBSYSTEM_DEF(autotransfer)
var/targettime
var/voteinterval
var/maxvotes
- var/curvotes
+ var/curvotes = 0
/datum/controller/subsystem/autotransfer/Initialize(timeofday)
+ var/init_vote = CONFIG_GET(number/vote_autotransfer_initial)
+ if(!init_vote) //Autotransfer voting disabled.
+ can_fire = FALSE
+ return ..()
starttime = world.time
- targettime = starttime + CONFIG_GET(number/vote_autotransfer_initial)
+ targettime = starttime + init_vote
voteinterval = CONFIG_GET(number/vote_autotransfer_interval)
maxvotes = CONFIG_GET(number/vote_autotransfer_maximum)
- curvotes = 0
return ..()
+/datum/controller/subsystem/autotransfer/Recover()
+ starttime = SSautotransfer.starttime
+ voteinterval = SSautotransfer.voteinterval
+ curvotes = SSautotransfer.curvotes
+
/datum/controller/subsystem/autotransfer/fire()
- if(maxvotes > curvotes)
- if(world.time > targettime)
- SSvote.initiate_vote("transfer",null) //TODO figure out how to not use null as the user
- targettime = targettime + voteinterval
- curvotes += 1
+ if(world.time < targettime)
+ return
+ if(maxvotes == NO_MAXVOTES_CAP || maxvotes > curvotes)
+ SSvote.initiate_vote("transfer","server")
+ targettime = targettime + voteinterval
+ curvotes++
else
SSshuttle.autoEnd()
+
+#undef NO_MAXVOTES_CAP
\ No newline at end of file
diff --git a/code/controllers/subsystem/dcs.dm b/code/controllers/subsystem/dcs.dm
index faf95fd319..8b068e5d67 100644
--- a/code/controllers/subsystem/dcs.dm
+++ b/code/controllers/subsystem/dcs.dm
@@ -1,27 +1,53 @@
PROCESSING_SUBSYSTEM_DEF(dcs)
name = "Datum Component System"
flags = SS_NO_INIT
+
var/list/elements_by_type = list()
/datum/controller/subsystem/processing/dcs/Recover()
comp_lookup = SSdcs.comp_lookup
-/datum/controller/subsystem/processing/dcs/proc/GetElement(datum/element/eletype, ...)
+/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments)
+ var/datum/element/eletype = arguments[1]
var/element_id = eletype
+ if(!ispath(eletype, /datum/element))
+ CRASH("Attempted to instantiate [eletype] as a /datum/element")
+
if(initial(eletype.element_flags) & ELEMENT_BESPOKE)
- var/list/fullid = list("[eletype]")
- for(var/i in initial(eletype.id_arg_index) to length(args))
- var/argument = args[i]
- if(istext(argument) || isnum(argument))
- fullid += "[argument]"
- else
- fullid += "[REF(argument)]"
- element_id = fullid.Join("&")
+ element_id = GetIdFromArguments(arguments)
. = elements_by_type[element_id]
if(.)
return
- if(!ispath(eletype, /datum/element))
- CRASH("Attempted to instantiate [eletype] as a /datum/element")
- . = elements_by_type[element_id] = new eletype
\ No newline at end of file
+ . = elements_by_type[element_id] = new eletype
+
+/****
+ * Generates an id for bespoke elements when given the argument list
+ * Generating the id here is a bit complex because we need to support named arguments
+ * Named arguments can appear in any order and we need them to appear after ordered arguments
+ * We assume that no one will pass in a named argument with a value of null
+ **/
+/datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments)
+ var/datum/element/eletype = arguments[1]
+ var/list/fullid = list("[eletype]")
+ var/list/named_arguments = list()
+ for(var/i in initial(eletype.id_arg_index) to length(arguments))
+ var/key = arguments[i]
+ var/value
+ if(istext(key))
+ value = arguments[key]
+ if(!(istext(key) || isnum(key)))
+ key = REF(key)
+ key = "[key]" // Key is stringified so numbers dont break things
+ if(!isnull(value))
+ if(!(istext(value) || isnum(value)))
+ value = REF(value)
+ named_arguments["[key]"] = value
+ else
+ fullid += "[key]"
+
+ if(length(named_arguments))
+ named_arguments = sortList(named_arguments)
+ fullid += named_arguments
+ return list2params(fullid)
diff --git a/code/controllers/subsystem/minimaps.dm b/code/controllers/subsystem/minimaps.dm
new file mode 100644
index 0000000000..75de71ca96
--- /dev/null
+++ b/code/controllers/subsystem/minimaps.dm
@@ -0,0 +1,20 @@
+SUBSYSTEM_DEF(minimaps)
+ name = "Minimaps"
+ flags = SS_NO_FIRE
+ var/list/station_minimaps
+ var/datum/minimap_group/station_minimap
+
+/datum/controller/subsystem/minimaps/Initialize()
+ if(!CONFIG_GET(flag/minimaps_enabled))
+ to_chat(world, "Minimaps disabled! Skipping init.")
+ return ..()
+ build_minimaps()
+ return ..()
+
+/datum/controller/subsystem/minimaps/proc/build_minimaps()
+ station_minimaps = list()
+ for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ var/datum/space_level/SL = SSmapping.get_level(z)
+ var/name = (SL.name == initial(SL.name))? "[z] - Station" : "[z] - [SL.name]"
+ station_minimaps += new /datum/minimap(z, name = name)
+ station_minimap = new(station_minimaps, "Station")
diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm
index 56cdf2fa03..8caf2a4623 100644
--- a/code/controllers/subsystem/mobs.dm
+++ b/code/controllers/subsystem/mobs.dm
@@ -26,18 +26,16 @@ SUBSYSTEM_DEF(mobs)
var/seconds = wait * 0.1
if (!resumed)
src.currentrun = GLOB.mob_living_list.Copy()
- if (GLOB.living_cameras.len)
- src.currentrun += GLOB.living_cameras
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
var/times_fired = src.times_fired
while(currentrun.len)
- var/mob/M = currentrun[currentrun.len]
+ var/mob/living/L = currentrun[currentrun.len]
currentrun.len--
- if(M)
- M.Life(seconds, times_fired)
+ if(L)
+ L.Life(seconds, times_fired)
else
- GLOB.mob_living_list.Remove(M)
+ GLOB.mob_living_list.Remove(L)
if (MC_TICK_CHECK)
return
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index e9e725faeb..ac0acfb61d 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -89,20 +89,6 @@ SUBSYSTEM_DEF(vote)
choices[GLOB.master_mode] += non_voters.len
if(choices[GLOB.master_mode] >= greatest_votes)
greatest_votes = choices[GLOB.master_mode]
- else if(mode == "transfer") // austation begin -- Crew autotransfer vote
- var/factor = 1
- switch(world.time / (1 MINUTES))
- if(0 to 60)
- factor = 0.5
- if(61 to 120)
- factor = 0.8
- if(121 to 240)
- factor = 1
- if(241 to 300)
- factor = 1.2
- else
- factor = 1.4
- choices["Initiate Crew Transfer"] += round(non_voters.len * factor) // austation end
//get all options with that many votes and return them in a list
. = list()
if(greatest_votes)
@@ -370,7 +356,7 @@ SUBSYSTEM_DEF(vote)
var/list/runnable_storytellers = config.get_runnable_storytellers()
for(var/T in runnable_storytellers)
var/datum/dynamic_storyteller/S = T
- runnable_storytellers[S] *= scores[initial(S.name)]
+ runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
var/datum/dynamic_storyteller/S = pickweightAllowZero(runnable_storytellers)
GLOB.dynamic_storyteller_type = S
if("map")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index f1df2a80d8..144f5f7946 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -426,6 +426,7 @@
/datum/action/item_action/hands_free
check_flags = AB_CHECK_CONSCIOUS
+ required_mobility_flags = NONE
/datum/action/item_action/hands_free/activate
name = "Activate"
diff --git a/code/datums/components/README.md b/code/datums/components/README.md
index 05ce057721..509630bb0a 100644
--- a/code/datums/components/README.md
+++ b/code/datums/components/README.md
@@ -4,132 +4,6 @@
Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening.
-### In the code
+See [this thread](https://tgstation13.org/phpBB/viewtopic.php?f=5&t=22674) for an introduction to the system as a whole.
-#### Slippery things
-
-At the time of this writing, every object that is slippery overrides atom/Crossed does some checks, then slips the mob. Instead of all those Crossed overrides they could add a slippery component to all these objects. And have the checks in one proc that is run by the Crossed event
-
-#### Powercells
-
-A lot of objects have powercells. The `get_cell()` proc was added to give generic access to the cell var if it had one. This is just a specific use case of `GetComponent()`
-
-#### Radios
-
-The radio object as it is should not exist, given that more things use the _concept_ of radios rather than the object itself. The actual function of the radio can exist in a component which all the things that use it (Request consoles, actual radios, the SM shard) can add to themselves.
-
-#### Standos
-
-Stands have a lot of procs which mimic mob procs. Rather than inserting hooks for all these procs in overrides, the same can be accomplished with signals
-
-## API
-
-### Defines
-
-1. `COMPONENT_INCOMPATIBLE` Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. `parent` must not be modified if this is to be returned. This will be noted in the runtime logs
-
-### Vars
-
-1. `/datum/var/list/datum_components` (private)
- * Lazy associated list of type -> component/list of components.
-1. `/datum/var/list/comp_lookup` (private)
- * Lazy associated list of signal -> registree/list of registrees
-1. `/datum/var/list/signal_procs` (private)
- * Associated lazy list of signals -> `/datum/callback`s that will be run when the parent datum receives that signal
-1. `/datum/var/signal_enabled` (protected, boolean)
- * If the datum is signal enabled. If not, it will not react to signals
- * `FALSE` by default, set to `TRUE` when a signal is registered
-1. `/datum/component/var/dupe_mode` (protected, enum)
- * How duplicate component types are handled when added to the datum.
- * `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it
- * `COMPONENT_DUPE_ALLOWED`: The components will be treated as separate, `GetComponent()` will return the first added
- * `COMPONENT_DUPE_UNIQUE`: New component will be deleted, old component will first have `/datum/component/proc/InheritComponent(datum/component/new, TRUE)` on it
- * `COMPONENT_DUPE_UNIQUE_PASSARGS`: New component will never exist and instead its initialization arguments will be passed on to the old component.
-1. `/datum/component/var/dupe_type` (protected, type)
- * Definition of a duplicate component type
- * `null` means exact match on `type` (default)
- * Any other type means that and all subtypes
-1. `/datum/component/var/datum/parent` (protected, read-only)
- * The datum this component belongs to
- * Never `null` in child procs
-1. `report_signal_origin` (protected, boolean)
- * If `TRUE`, will invoke the callback when signalled with the signal type as the first argument.
- * `FALSE` by default.
-
-### Procs
-
-1. `/datum/proc/GetComponent(component_type(type)) -> datum/component?` (public, final)
- * Returns a reference to a component of component_type if it exists in the datum, null otherwise
-1. `/datum/proc/GetComponents(component_type(type)) -> list` (public, final)
- * Returns a list of references to all components of component_type that exist in the datum
-1. `/datum/proc/GetExactComponent(component_type(type)) -> datum/component?` (public, final)
- * Returns a reference to a component whose type MATCHES component_type if that component exists in the datum, null otherwise
-1. `GET_COMPONENT(varname, component_type)` OR `GET_COMPONENT_FROM(varname, component_type, src)`
- * Shorthand for `var/component_type/varname = src.GetComponent(component_type)`
-1. `SEND_SIGNAL(target, sigtype, ...)` (public, final)
- * Use to send signals to target datum
- * Extra arguments are to be specified in the signal definition
- * Returns a bitflag with signal specific information assembled from all activated components
- * Arguments are packaged in a list and handed off to _SendSignal()
-1. `/datum/proc/AddComponent(component_type(type), ...) -> datum/component` (public, final)
- * Creates an instance of `component_type` in the datum and passes `...` to its `Initialize()` call
- * Sends the `COMSIG_COMPONENT_ADDED` signal to the datum
- * All components a datum owns are deleted with the datum
- * Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set
- * If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
- * Properly handles duplicate situations based on the `dupe_mode` var
-1. `/datum/proc/LoadComponent(component_type(type), ...) -> datum/component` (public, final)
- * Equivalent to calling `GetComponent(component_type)` where, if the result would be `null`, returns `AddComponent(component_type, ...)` instead
-1. `/datum/proc/ComponentActivated(datum/component/C)` (abstract, async)
- * Called on a component's `parent` after a signal received causes it to activate. `src` is the parameter
- * Will only be called if a component's callback returns `TRUE`
-1. `/datum/proc/TakeComponent(datum/component/C)` (public, final)
- * Properly transfers ownership of a component from one datum to another
- * Signals `COMSIG_COMPONENT_REMOVING` on the parent
- * Called on the datum you want to own the component with another datum's component
-1. `/datum/proc/_SendSignal(signal, list/arguments)` (private, final)
- * Handles most of the actual signaling procedure
- * Will runtime if used on datums with an empty component list
-1. `/datum/proc/RegisterSignal(datum/target, signal(string/list of strings), proc_ref(type), override(boolean))` (protected, final)
- * If signal is a list it will be as if RegisterSignal was called for each of the entries with the same following arguments
- * Makes the datum listen for the specified `signal` on it's `parent` datum.
- * When that signal is received `proc_ref` will be called on the component, along with associated arguments
- * Example proc ref: `.proc/OnEvent`
- * If a previous registration is overwritten by the call, a runtime occurs. Setting `override` to TRUE prevents this
- * These callbacks run asyncronously
- * Returning `TRUE` from these callbacks will trigger a `TRUE` return from the `SendSignal()` that initiated it
-1. `/datum/component/New(datum/parent, ...)` (private, final)
- * Runs internal setup for the component
- * Extra arguments are passed to `Initialize()`
-1. `/datum/component/Initialize(...)` (abstract, no-sleep)
- * Called by `New()` with the same argments excluding `parent`
- * Component does not exist in `parent`'s `datum_components` list yet, although `parent` is set and may be used
- * Signals will not be received while this function is running
- * Component may be deleted after this function completes without being attached
- * Do not call `qdel(src)` from this function
-1. `/datum/component/Destroy(force(bool), silent(bool))` (virtual, no-sleep)
- * Sends the `COMSIG_COMPONENT_REMOVING` signal to the parent datum if the `parent` isn't being qdeleted
- * Properly removes the component from `parent` and cleans up references
- * Setting `force` makes it not check for and remove the component from the parent
- * Setting `silent` deletes the component without sending a `COMSIG_COMPONENT_REMOVING` signal
-1. `/datum/component/proc/InheritComponent(datum/component/C, i_am_original(boolean))` (abstract, no-sleep)
- * Called on a component when a component of the same type was added to the same parent
- * See `/datum/component/var/dupe_mode`
- * `C`'s type will always be the same of the called component
-1. `/datum/component/proc/AfterComponentActivated()` (abstract, async)
- * Called on a component that was activated after it's `parent`'s `ComponentActivated()` is called
-1. `/datum/component/proc/OnTransfer(datum/new_parent)` (abstract, no-sleep)
- * Called before `new_parent` is assigned to `parent` in `TakeComponent()`
- * Allows the component to react to ownership transfers
-1. `/datum/component/proc/_RemoveFromParent()` (private, final)
- * Clears `parent` and removes the component from it's component list
-1. `/datum/component/proc/_JoinParent` (private, final)
- * Tries to add the component to it's `parent`s `datum_components` list
-1. `/datum/component/proc/RegisterWithParent` (abstract, no-sleep)
- * Used to register the signals that should be on the `parent` object
- * Use this if you plan on the component transfering between parents
-1. `/datum/component/proc/UnregisterFromParent` (abstract, no-sleep)
- * Counterpart to `RegisterWithParent()`
- * Used to unregister the signals that should only be on the `parent` object
-
-### See/Define signals and their arguments in __DEFINES\components.dm
+### See/Define signals and their arguments in [__DEFINES\components.dm](..\..\__DEFINES\components.dm)
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index 0baf7d7d1b..7f7440daf4 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -1,21 +1,71 @@
+/**
+ * # Component
+ *
+ * The component datum
+ *
+ * A component should be a single standalone unit
+ * of functionality, that works by receiving signals from it's parent
+ * object to provide some single functionality (i.e a slippery component)
+ * that makes the object it's attached to cause people to slip over.
+ * Useful when you want shared behaviour independent of type inheritance
+ */
/datum/component
+ /// Defines how duplicate existing components are handled when added to a datum
+ /// See `COMPONENT_DUPE_*` definitions for available options
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
+
+ /// The type to check for duplication
+ /// `null` means exact match on `type` (default)
+ /// Any other type means that and all subtypes
var/dupe_type
+
+ /// The datum this components belongs to
var/datum/parent
- //only set to true if you are able to properly transfer this component
- //At a minimum RegisterWithParent and UnregisterFromParent should be used
- //Make sure you also implement PostTransfer for any post transfer handling
+
+ /// Only set to true if you are able to properly transfer this component
+ /// At a minimum RegisterWithParent and UnregisterFromParent should be used
+ /// Make sure you also implement PostTransfer for any post transfer handling
var/can_transfer = FALSE
-/datum/component/New(datum/P, ...)
- parent = P
- var/list/arguments = args.Copy(2)
+/**
+ * Create a new component.
+ * Additional arguments are passed to `Initialize()`
+ *
+ * Arguments:
+ * * datum/P the parent datum this component reacts to signals from
+ */
+/datum/component/New(list/raw_args)
+ parent = raw_args[1]
+ var/list/arguments = raw_args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
qdel(src, TRUE, TRUE)
- CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
+ CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
- _JoinParent(P)
+ _JoinParent(parent)
+/**
+ * Called during component creation with the same arguments as in new excluding parent.
+ * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
+ */
+/datum/component/proc/Initialize(...)
+ return
+
+/**
+ * Properly removes the component from `parent` and cleans up references
+ * Setting `force` makes it not check for and remove the component from the parent
+ * Setting `silent` deletes the component without sending a `COMSIG_COMPONENT_REMOVING` signal
+ */
+/datum/component/Destroy(force=FALSE, silent=FALSE)
+ if(!force && parent)
+ _RemoveFromParent()
+ if(!silent)
+ SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
+ parent = null
+ return ..()
+
+/**
+ * Internal proc to handle behaviour of components when joining a parent
+ */
/datum/component/proc/_JoinParent()
var/datum/P = parent
//lazy init the parent's dc list
@@ -51,21 +101,9 @@
RegisterWithParent()
-// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
-/datum/component/proc/RegisterWithParent()
- SEND_SIGNAL(src, COMSIG_COMPONENT_REGISTER_PARENT) //CITADEL EDIT
-
-/datum/component/proc/Initialize(...)
- return
-
-/datum/component/Destroy(force=FALSE, silent=FALSE)
- if(!force && parent)
- _RemoveFromParent()
- if(!silent)
- SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
- parent = null
- return ..()
-
+/**
+ * Internal proc to handle behaviour when being removed from a parent
+ */
/datum/component/proc/_RemoveFromParent()
var/datum/P = parent
var/list/dc = P.datum_components
@@ -84,9 +122,38 @@
UnregisterFromParent()
-/datum/component/proc/UnregisterFromParent()
- SEND_SIGNAL(src, COMSIG_COMPONENT_UNREGISTER_PARENT) //CITADEL EDIT
+/**
+ * Register the component with the parent object
+ *
+ * Use this proc to register with your parent object
+ * Overridable proc that's called when added to a new parent
+ */
+/datum/component/proc/RegisterWithParent()
+ return
+/**
+ * Unregister from our parent object
+ *
+ * Use this proc to unregister from your parent object
+ * Overridable proc that's called when removed from a parent
+ * *
+ */
+/datum/component/proc/UnregisterFromParent()
+ return
+
+/**
+ * Register to listen for a signal from the passed in target
+ *
+ * This sets up a listening relationship such that when the target object emits a signal
+ * the source datum this proc is called upon, will recieve a callback to the given proctype
+ * Return values from procs registered must be a bitfield
+ *
+ * Arguments:
+ * * datum/target The target to listen for signals from
+ * * sig_type_or_types Either a string signal name, or a list of signal names (strings)
+ * * proctype The proc to call back when the signal is emitted
+ * * override If a previous registration exists you must explicitly set this
+ */
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
@@ -119,6 +186,16 @@
signal_enabled = TRUE
+/**
+ * Stop listening to a given signal from target
+ *
+ * Breaks the relationship between target and source datum, removing the callback when the signal fires
+ * Doesn't care if a registration exists or not
+ *
+ * Arguments:
+ * * datum/target Datum to stop listening to signals from
+ * * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
+ */
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target.comp_lookup
if(!signal_procs || !signal_procs[target] || !lookup)
@@ -126,6 +203,8 @@
if(!islist(sig_type_or_types))
sig_type_or_types = list(sig_type_or_types)
for(var/sig in sig_type_or_types)
+ if(!signal_procs[target][sig])
+ continue
switch(length(lookup[sig]))
if(2)
lookup[sig] = (lookup[sig]-src)[1]
@@ -148,15 +227,47 @@
if(!signal_procs[target].len)
signal_procs -= target
+/**
+ * Called on a component when a component of the same type was added to the same parent
+ * See `/datum/component/var/dupe_mode`
+ * `C`'s type will always be the same of the called component
+ */
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return
+
+/**
+ * Called on a component when a component of the same type was added to the same parent with COMPONENT_DUPE_SELECTIVE
+ * See `/datum/component/var/dupe_mode`
+ * `C`'s type will always be the same of the called component
+ * return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component
+ */
+/datum/component/proc/CheckDupeComponent(datum/component/C, ...)
+ return
+
+
+/**
+ * Callback Just before this component is transferred
+ *
+ * Use this to do any special cleanup you might need to do before being deregged from an object
+ *
+ */
/datum/component/proc/PreTransfer()
return
+/**
+ * Callback Just after a component is transferred
+ *
+ * Use this to do any special setup you need to do after being moved to a new object
+ * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
+ *
+ */
/datum/component/proc/PostTransfer()
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
+/**
+ * Internal proc to create a list of our type and all parent types
+ */
/datum/component/proc/_GetInverseTypeList(our_type = type)
//we can do this one simple trick
var/current_type = parent_type
@@ -166,6 +277,11 @@
current_type = type2parent(current_type)
. += current_type
+/**
+ * Internal proc to handle most all of the signaling procedure
+ * Will runtime if used on datums with an empty component list
+ * Use the `SEND_SIGNAL` define instead
+ */
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = comp_lookup[sigtype]
if(!length(target))
@@ -183,9 +299,16 @@
. |= CallAsync(C, proctype, arguments)
// The type arg is casted so initial works, you shouldn't be passing a real instance into this
+/**
+ * Return any component assigned to this datum of the given type
+ * This will throw an error if it's possible to have more than one component of that type on the parent
+ *
+ * Arguments:
+ * * datum/component/c_type The typepath of the component you want to get a reference to
+ */
/datum/proc/GetComponent(datum/component/c_type)
RETURN_TYPE(c_type)
- if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED)
+ if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
@@ -194,7 +317,18 @@
if(length(.))
return .[1]
-/datum/proc/GetExactComponent(c_type)
+// The type arg is casted so initial works, you shouldn't be passing a real instance into this
+/**
+ * Return any component assigned to this datum of the exact given type
+ * This will throw an error if it's possible to have more than one component of that type on the parent
+ *
+ * Arguments:
+ * * datum/component/c_type The typepath of the component you want to get a reference to
+ */
+/datum/proc/GetExactComponent(datum/component/c_type)
+ RETURN_TYPE(c_type)
+ if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
+ stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
return null
@@ -206,6 +340,12 @@
return C
return null
+/**
+ * Get all components of a given type that are attached to this datum
+ *
+ * Arguments:
+ * * c_type The component type path
+ */
/datum/proc/GetComponents(c_type)
var/list/dc = datum_components
if(!dc)
@@ -214,7 +354,15 @@
if(!length(.))
return list(.)
-/datum/proc/AddComponent(new_type, ...)
+/**
+ * Creates an instance of `new_type` in the datum and attaches to it as parent
+ * Sends the `COMSIG_COMPONENT_ADDED` signal to the datum
+ * Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set
+ * If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
+ * Properly handles duplicate situations based on the `dupe_mode` var
+ */
+/datum/proc/_AddComponent(list/raw_args)
+ var/new_type = raw_args[1]
var/datum/component/nt = new_type
var/dm = initial(nt.dupe_mode)
var/dt = initial(nt.dupe_type)
@@ -229,7 +377,7 @@
new_comp = nt
nt = new_comp.type
- args[1] = src
+ raw_args[1] = src
if(dm != COMPONENT_DUPE_ALLOWED)
if(!dt)
@@ -240,37 +388,62 @@
switch(dm)
if(COMPONENT_DUPE_UNIQUE)
if(!new_comp)
- new_comp = new nt(arglist(args))
+ new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
old_comp.InheritComponent(new_comp, TRUE)
QDEL_NULL(new_comp)
if(COMPONENT_DUPE_HIGHLANDER)
if(!new_comp)
- new_comp = new nt(arglist(args))
+ new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
new_comp.InheritComponent(old_comp, FALSE)
QDEL_NULL(old_comp)
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
if(!new_comp)
- var/list/arguments = args.Copy(2)
- old_comp.InheritComponent(null, TRUE, arguments)
+ var/list/arguments = raw_args.Copy(2)
+ arguments.Insert(1, null, TRUE)
+ old_comp.InheritComponent(arglist(arguments))
else
old_comp.InheritComponent(new_comp, TRUE)
+ if(COMPONENT_DUPE_SELECTIVE)
+ var/list/arguments = raw_args.Copy()
+ arguments[1] = new_comp
+ var/make_new_component = TRUE
+ for(var/i in GetComponents(new_type))
+ var/datum/component/C = i
+ if(C.CheckDupeComponent(arglist(arguments)))
+ make_new_component = FALSE
+ QDEL_NULL(new_comp)
+ break
+ if(!new_comp && make_new_component)
+ new_comp = new nt(raw_args)
else if(!new_comp)
- new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
+ new_comp = new nt(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(!new_comp)
- new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
+ new_comp = new nt(raw_args) // Dupes are allowed, act like normal
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
return new_comp
return old_comp
+/**
+ * Get existing component of type, or create it and return a reference to it
+ *
+ * Use this if the item needs to exist at the time of this call, but may not have been created before now
+ *
+ * Arguments:
+ * * component_type The typepath of the component to create or return
+ * * ... additional arguments to be passed when creating the component if it does not exist
+ */
/datum/proc/LoadComponent(component_type, ...)
. = GetComponent(component_type)
if(!.)
- return AddComponent(arglist(args))
+ return _AddComponent(args)
+/**
+ * Removes the component from parent, ends up with a null parent
+ */
/datum/component/proc/RemoveComponent()
if(!parent)
return
@@ -280,6 +453,14 @@
parent = null
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
+/**
+ * Transfer this component to another parent
+ *
+ * Component is taken from source datum
+ *
+ * Arguments:
+ * * datum/component/target Target datum to transfer to
+ */
/datum/proc/TakeComponent(datum/component/target)
if(!target || target.parent == src)
return
@@ -296,6 +477,14 @@
if(target == AddComponent(target))
target._JoinParent()
+/**
+ * Transfer all components to target
+ *
+ * All components from source datum are taken
+ *
+ * Arguments:
+ * * /datum/target the target to move the components to
+ */
/datum/proc/TransferComponents(datum/target)
var/list/dc = datum_components
if(!dc)
@@ -310,5 +499,8 @@
if(C.can_transfer)
target.TakeComponent(comps)
+/**
+ * Return the object that is the host of any UI's that this component has
+ */
/datum/component/ui_host()
return parent
diff --git a/code/datums/components/fantasy/_fantasy.dm b/code/datums/components/fantasy/_fantasy.dm
index 9e8493b6f4..a203264fae 100644
--- a/code/datums/components/fantasy/_fantasy.dm
+++ b/code/datums/components/fantasy/_fantasy.dm
@@ -30,26 +30,23 @@
return ..()
/datum/component/fantasy/RegisterWithParent()
- . = ..()
var/obj/item/master = parent
originalName = master.name
modify()
/datum/component/fantasy/UnregisterFromParent()
- . = ..()
unmodify()
-/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, list/arguments)
+/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, quality, list/affixes, canFail, announce)
unmodify()
if(newComp)
- quality += newComp.quality
- canFail = newComp.canFail
- announce = newComp.announce
+ src.quality += newComp.quality
+ src.canFail = newComp.canFail
+ src.announce = newComp.announce
else
- arguments.len = 5 // This is done to replicate what happens when an arglist smaller than the necessary arguments is given
- quality += arguments[1]
- canFail = arguments[4] || canFail
- announce = arguments[5] || announce
+ src.quality += quality
+ src.canFail = canFail || src.canFail
+ src.announce = announce || src.announce
modify()
/datum/component/fantasy/proc/randomQuality()
diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm
index 49b2e437d4..7aacb745b6 100644
--- a/code/datums/components/nanites.dm
+++ b/code/datums/components/nanites.dm
@@ -101,11 +101,11 @@
host_mob = null
return ..()
-/datum/component/nanites/InheritComponent(datum/component/nanites/new_nanites, i_am_original, list/arguments)
+/datum/component/nanites/InheritComponent(datum/component/nanites/new_nanites, i_am_original, amount, cloud)
if(new_nanites)
adjust_nanites(null, new_nanites.nanite_volume)
else
- adjust_nanites(null, arguments[1]) //just add to the nanite volume
+ adjust_nanites(null, amount) //just add to the nanite volume
/datum/component/nanites/process()
adjust_nanites(null, regen_rate)
diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm
index 05174c196b..2be22d01e5 100644
--- a/code/datums/components/orbiter.dm
+++ b/code/datums/components/orbiter.dm
@@ -41,9 +41,9 @@
orbiters = null
return ..()
-/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, list/arguments)
- if(arguments)
- begin_orbit(arglist(arguments))
+/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
+ if(!newcomp)
+ begin_orbit(arglist(args.Copy(3)))
return
// The following only happens on component transfers
orbiters += newcomp.orbiters
diff --git a/code/datums/components/radioactive.dm b/code/datums/components/radioactive.dm
index 240eed7747..2853f800f5 100644
--- a/code/datums/components/radioactive.dm
+++ b/code/datums/components/radioactive.dm
@@ -47,7 +47,7 @@
if(strength <= RAD_BACKGROUND_RADIATION)
return PROCESS_KILL
-/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, list/arguments)
+/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate)
if(!i_am_original)
return
if(!hl3_release_date) // Permanently radioactive things don't get to grow stronger
@@ -56,7 +56,7 @@
var/datum/component/radioactive/other = C
strength = max(strength, other.strength)
else
- strength = max(strength, arguments[1])
+ strength = max(strength, _strength)
/datum/component/radioactive/proc/rad_examine(datum/source, mob/user, list/examine_list)
var/atom/master = parent
diff --git a/code/datums/components/remote_materials.dm b/code/datums/components/remote_materials.dm
index 245e89bd0c..ca62a70ee5 100644
--- a/code/datums/components/remote_materials.dm
+++ b/code/datums/components/remote_materials.dm
@@ -53,11 +53,21 @@ handles linking back and forth.
/datum/component/remote_materials/proc/_MakeLocal()
silo = null
- mat_container = parent.AddComponent(/datum/component/material_container,
- list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace, /datum/material/plastic),
- local_size,
- FALSE,
- /obj/item/stack)
+ var/static/list/allowed_mats = list(
+ /datum/material/iron,
+ /datum/material/glass,
+ /datum/material/silver,
+ /datum/material/gold,
+ /datum/material/diamond,
+ /datum/material/plasma,
+ /datum/material/uranium,
+ /datum/material/bananium,
+ /datum/material/titanium,
+ /datum/material/bluespace,
+ /datum/material/plastic,
+ )
+
+ mat_container = parent.AddComponent(/datum/component/material_container, allowed_mats, local_size, allowed_types=/obj/item/stack)
/datum/component/remote_materials/proc/set_local_size(size)
local_size = size
diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm
index 750120976f..e7f25a7724 100644
--- a/code/datums/components/riding.dm
+++ b/code/datums/components/riding.dm
@@ -341,7 +341,7 @@
var/mob/living/parent
var/selfdeleting = FALSE
-/obj/item/riding_offhand/dropped()
+/obj/item/riding_offhand/dropped(mob/user)
selfdeleting = TRUE
. = ..()
diff --git a/code/datums/components/stationloving.dm b/code/datums/components/stationloving.dm
index 91928656e1..13267e74c3 100644
--- a/code/datums/components/stationloving.dm
+++ b/code/datums/components/stationloving.dm
@@ -15,13 +15,13 @@
src.allow_death = allow_death
check_in_bounds() // Just in case something is being created outside of station/centcom
-/datum/component/stationloving/InheritComponent(datum/component/stationloving/newc, original, list/arguments)
+/datum/component/stationloving/InheritComponent(datum/component/stationloving/newc, original, inform_admins, allow_death)
if (original)
- if (istype(newc))
+ if (newc)
inform_admins = newc.inform_admins
allow_death = newc.allow_death
- else if (LAZYLEN(arguments))
- inform_admins = arguments[1]
+ else
+ inform_admins = inform_admins
/datum/component/stationloving/proc/relocate()
var/targetturf = find_safe_turf()
diff --git a/code/datums/components/thermite.dm b/code/datums/components/thermite.dm
index 916b3ed3b2..53323f1e3f 100644
--- a/code/datums/components/thermite.dm
+++ b/code/datums/components/thermite.dm
@@ -43,13 +43,13 @@
master.cut_overlay(overlay)
return ..()
-/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original, list/arguments)
+/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original, _amount)
if(!i_am_original)
return
if(newC)
amount += newC.amount
else
- amount += arguments[1]
+ amount += _amount
/datum/component/thermite/proc/thermite_melt(mob/user)
var/turf/master = parent
diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm
index 550ff95324..20f2c1bbb5 100644
--- a/code/datums/components/wet_floor.dm
+++ b/code/datums/components/wet_floor.dm
@@ -12,9 +12,9 @@
var/permanent = FALSE
var/last_process = 0
-/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, argslist)
+/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, strength, duration_minimum, duration_add, duration_maximum, _permanent)
if(!newcomp) //We are getting passed the arguments of a would-be new component, but not a new component
- add_wet(arglist(argslist))
+ add_wet(arglist(args.Copy(3)))
else //We are being passed in a full blown component
var/datum/component/wet_floor/WF = newcomp //Lets make an assumption
if(WF.gc()) //See if it's even valid, still. Also does LAZYLEN and stuff for us.
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 1622cc9dbf..84ea6b6458 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -287,7 +287,6 @@
G.fields["gender"] = "Other"
L.fields["blood_type"] = H.dna.blood_type
L.fields["b_dna"] = H.dna.unique_enzymes
- L.fields["enzymes"] = H.dna.struc_enzymes
L.fields["identity"] = H.dna.uni_identity
L.fields["species"] = H.dna.species.type
L.fields["features"] = H.dna.features
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index 34e4e7ac14..a856f2392d 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -1,12 +1,42 @@
+/**
+ * The absolute base class for everything
+ *
+ * A datum instantiated has no physical world prescence, use an atom if you want something
+ * that actually lives in the world
+ *
+ * Be very mindful about adding variables to this class, they are inherited by every single
+ * thing in the entire game, and so you can easily cause memory usage to rise a lot with careless
+ * use of variables at this level
+ */
/datum
+ /**
+ * Tick count time when this object was destroyed.
+ *
+ * If this is non zero then the object has been garbage collected and is awaiting either
+ * a hard del by the GC subsystme, or to be autocollected (if it has no references)
+ */
var/gc_destroyed //Time when this object was destroyed.
- var/list/active_timers //for SStimer
- var/list/datum_components //for /datum/components
+
+ /// Active timers with this datum as the target
+ var/list/active_timers
+ /// Status traits attached to this datum
var/list/status_traits
+ /// Components attached to this datum
+ /// Lazy associated list in the structure of `type:component/list of components`
+ var/list/datum_components
+ /// Any datum registered to receive signals from this datum is in this list
+ /// Lazy associated list in the structure of `signal:registree/list of registrees`
var/list/comp_lookup //it used to be for looking up components which had registered a signal but now anything can register
+ /// Lazy associated list in the structure of `signals:proctype` that are run when the datum receives that signal
var/list/list/datum/callback/signal_procs
+ /// Is this datum capable of sending signals?
+ /// Set to true when a signal has been registered
var/signal_enabled = FALSE
+
+ /// Datum level flags
var/datum_flags = NONE
+
+ /// A weak reference to another datum
var/datum/weakref/weak_reference
#ifdef TESTING
@@ -18,9 +48,22 @@
var/list/cached_vars
#endif
-// Default implementation of clean-up code.
-// This should be overridden to remove all references pointing to the object being destroyed.
-// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
+/**
+ * Default implementation of clean-up code.
+ *
+ * This should be overridden to remove all references pointing to the object being destroyed, if
+ * you do override it, make sure to call the parent and return it's return value by default
+ *
+ * Return an appropriate QDEL_HINT to modify handling of your deletion;
+ * in most cases this is QDEL_HINT_QUEUE.
+ *
+ * The base case is responsible for doing the following
+ * * Erasing timers pointing to this datum
+ * * Erasing compenents on this datum
+ * * Notifying datums listening to signals from this datum that we are going away
+ *
+ * Returns QDEL_HINT_QUEUE
+ */
/datum/proc/Destroy(force=FALSE, ...)
tag = null
datum_flags &= ~DF_USE_TAG //In case something tries to REF us
diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm
index f6780df3fa..9f19de7244 100644
--- a/code/datums/diseases/advance/symptoms/genetics.dm
+++ b/code/datums/diseases/advance/symptoms/genetics.dm
@@ -61,12 +61,12 @@ Bonus
symptom_delay_max = 60
if(A.properties["resistance"] >= 8) //mutate twice
power = 2
- possible_mutations = (GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.mutations_list[RACEMUT]
+ possible_mutations = (GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.all_mutations[RACEMUT]
var/mob/living/carbon/M = A.affected_mob
if(M)
if(!M.has_dna())
return
- archived_dna = M.dna.struc_enzymes
+ archived_dna = M.dna.mutation_index
// Give them back their old DNA when cured.
/datum/symptom/genetic_mutation/End(datum/disease/advance/A)
@@ -77,5 +77,5 @@ Bonus
if(M && archived_dna)
if(!M.has_dna())
return
- M.dna.struc_enzymes = archived_dna
+ M.dna.mutation_index = archived_dna
M.domutcheck()
diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm
index 84d652b77d..59afe163b4 100644
--- a/code/datums/diseases/advance/symptoms/heal.dm
+++ b/code/datums/diseases/advance/symptoms/heal.dm
@@ -257,6 +257,8 @@
/datum/symptom/heal/coma/End(datum/disease/advance/A)
if(!..())
return
+ if(active_coma)
+ uncoma()
REMOVE_TRAIT(A.affected_mob, TRAIT_NOCRITDAMAGE, DISEASE_TRAIT)
/datum/symptom/heal/coma/CanHeal(datum/disease/advance/A)
@@ -277,7 +279,7 @@
/datum/symptom/heal/coma/proc/coma(mob/living/M)
if(deathgasp)
M.emote("deathgasp")
- M.fakedeath("regenerative_coma")
+ M.fakedeath("regenerative_coma", TRUE)
M.update_stat()
M.update_mobility()
addtimer(CALLBACK(src, .proc/uncoma, M), 300)
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 2f4dba9c38..05a6e4f060 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -2,7 +2,6 @@
/////////////////////////// DNA DATUM
/datum/dna
var/unique_enzymes
- var/struc_enzymes
var/uni_identity
var/blood_type
var/datum/species/species = new /datum/species/human //The type of mutant race the player is if applicable (i.e. potato-man)
@@ -11,10 +10,13 @@
var/nameless = FALSE
var/custom_species //siiiiigh I guess this is important
var/list/mutations = list() //All mutations are from now on here
- var/list/temporary_mutations = list() //Timers for temporary mutations
+ var/list/temporary_mutations = list() //Temporary changes to the UE
var/list/previous = list() //For temporary name/ui/ue/blood_type modifications
var/mob/living/holder
var/delete_species = TRUE //Set to FALSE when a body is scanned by a cloner to fix #38875
+ var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block
+ var/stability = 100
+ var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers.
/datum/dna/New(mob/living/new_holder)
if(istype(new_holder))
@@ -42,8 +44,8 @@
destination.dna.unique_enzymes = unique_enzymes
destination.dna.uni_identity = uni_identity
destination.dna.blood_type = blood_type
- destination.dna.features = features.Copy()
destination.set_species(species.type, icon_update=0)
+ destination.dna.features = features.Copy()
destination.dna.real_name = real_name
destination.dna.nameless = nameless
destination.dna.custom_species = custom_species
@@ -51,13 +53,14 @@
if(ishuman(destination))
var/mob/living/carbon/human/H = destination
H.give_genitals(TRUE)//This gives the body the genitals of this DNA. Used for any transformations based on DNA
- destination.flavor_text = destination.dna.features["flavor_text"] //Update the flavor_text to use new dna text
if(transfer_SE)
- destination.dna.struc_enzymes = struc_enzymes
+ destination.dna.mutation_index = mutation_index
+
+ SEND_SIGNAL(destination, COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, src, transfer_SE)
/datum/dna/proc/copy_dna(datum/dna/new_dna)
new_dna.unique_enzymes = unique_enzymes
- new_dna.struc_enzymes = struc_enzymes
+ new_dna.mutation_index = mutation_index
new_dna.uni_identity = uni_identity
new_dna.blood_type = blood_type
new_dna.features = features.Copy()
@@ -67,26 +70,32 @@
new_dna.custom_species = custom_species
new_dna.mutations = mutations.Copy()
-/datum/dna/proc/add_mutation(mutation_name)
- var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
- HM.on_acquiring(holder)
+//See mutation.dm for what 'class' does. 'time' is time till it removes itself in decimals. 0 for no timer
+/datum/dna/proc/add_mutation(mutation, class = MUT_OTHER, time)
+ var/mutation_type = mutation
+ if(istype(mutation, /datum/mutation/human))
+ var/datum/mutation/human/HM = mutation
+ mutation_type = HM.type
+ if(get_mutation(mutation_type))
+ return
+ return force_give(new mutation_type (class, time, copymut = mutation))
-/datum/dna/proc/remove_mutation(mutation_name)
- var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
- HM.on_losing(holder)
+/datum/dna/proc/remove_mutation(mutation_type)
+ return force_lose(get_mutation(mutation_type))
-/datum/dna/proc/check_mutation(mutation_name)
- var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
- return mutations.Find(HM)
+/datum/dna/proc/check_mutation(mutation_type)
+ return get_mutation(mutation_type)
-/datum/dna/proc/remove_all_mutations()
- remove_mutation_group(mutations)
+/datum/dna/proc/remove_all_mutations(list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
+ remove_mutation_group(mutations, classes, mutadone)
+ scrambled = FALSE
-/datum/dna/proc/remove_mutation_group(list/group)
+/datum/dna/proc/remove_mutation_group(list/group, list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
if(!group)
return
for(var/datum/mutation/human/HM in group)
- HM.force_lose(holder)
+ if((HM.class in classes) && !(HM.mutadone_proof && mutadone))
+ force_lose(HM)
/datum/dna/proc/generate_uni_identity()
. = ""
@@ -136,19 +145,49 @@
. += random_string(DNA_BLOCK_SIZE,GLOB.hex_characters)
return .
-/datum/dna/proc/generate_struc_enzymes()
- var/list/sorting = new /list(DNA_STRUC_ENZYMES_BLOCKS)
- var/result = ""
- for(var/datum/mutation/human/A in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
- if(A.name == RACEMUT && ismonkey(holder))
- sorting[A.dna_block] = num2hex(A.lowest_value + rand(0, 256 * 6), DNA_BLOCK_SIZE)
- mutations |= A
- else
- sorting[A.dna_block] = random_string(DNA_BLOCK_SIZE, list("0","1","2","3","4","5","6"))
+/datum/dna/proc/generate_dna_blocks()
+ var/bonus
+ if(species && species.inert_mutation)
+ bonus = GET_INITIALIZED_MUTATION(species.inert_mutation)
+ var/list/mutations_temp = GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations + bonus
+ if(!LAZYLEN(mutations_temp))
+ return
+ mutation_index.Cut()
+ shuffle_inplace(mutations_temp)
+ if(ismonkey(holder))
+ mutations |= new RACEMUT(MUT_NORMAL)
+ mutation_index[RACEMUT] = GET_SEQUENCE(RACEMUT)
+ else
+ mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE)
+ for(var/i in 2 to DNA_MUTATION_BLOCKS)
+ var/datum/mutation/human/M = mutations_temp[i]
+ mutation_index[M.type] = create_sequence(M.type, FALSE,M.difficulty)
+ shuffle_inplace(mutation_index)
- for(var/B in sorting)
- result += B
- return result
+//Used to generate original gene sequences for every mutation
+/proc/generate_gene_sequence(length=4)
+ var/static/list/active_sequences = list("AT","TA","GC","CG")
+ var/sequence
+ for(var/i in 1 to length*DNA_SEQUENCE_LENGTH)
+ sequence += pick(active_sequences)
+ return sequence
+
+//Used to create a chipped gene sequence
+/proc/create_sequence(mutation, active, difficulty)
+ if(!difficulty)
+ var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation) //leaves the possibility to change difficulty mid-round
+ if(!A)
+ return
+ difficulty = A.difficulty
+ difficulty += rand(-2,4)
+ var/sequence = GET_SEQUENCE(mutation)
+ if(active)
+ return sequence
+ while(difficulty)
+ var/randnum = rand(1, length(sequence))
+ sequence = copytext(sequence, 1, randnum) + "X" + copytext(sequence, randnum+1, length(sequence)+1)
+ difficulty--
+ return sequence
/datum/dna/proc/generate_unique_enzymes()
. = ""
@@ -201,12 +240,57 @@
if(DNA_TAUR_BLOCK)
construct_block(GLOB.taur_list.Find(features["taur"]), GLOB.taur_list.len)
+//Please use add_mutation or activate_mutation instead
+/datum/dna/proc/force_give(datum/mutation/human/HM)
+ if(holder && HM)
+ if(HM.class == MUT_NORMAL)
+ set_se(1, HM)
+ . = HM.on_acquiring(holder)
+ if(.)
+ qdel(HM)
+ update_instability()
+
+//Use remove_mutation instead
+/datum/dna/proc/force_lose(datum/mutation/human/HM)
+ if(holder && (HM in mutations))
+ set_se(0, HM)
+ . = HM.on_losing(holder)
+ update_instability(FALSE)
+ return
+
/datum/dna/proc/is_same_as(datum/dna/D)
- if(uni_identity == D.uni_identity && struc_enzymes == D.struc_enzymes && real_name == D.real_name && nameless == D.nameless && custom_species == D.custom_species)
+ if(uni_identity == D.uni_identity && mutation_index == D.mutation_index && real_name == D.real_name && nameless == D.nameless && custom_species == D.custom_species)
if(species.type == D.species.type && features == D.features && blood_type == D.blood_type)
return 1
return 0
+/datum/dna/proc/update_instability(alert=TRUE)
+ stability = 100
+ for(var/datum/mutation/human/M in mutations)
+ if(M.class == MUT_EXTRA)
+ stability -= M.instability * GET_MUTATION_STABILIZER(M)
+ if(holder)
+ var/message
+ if(alert)
+ switch(stability)
+ if(70 to 90)
+ message = "You shiver."
+ if(60 to 69)
+ message = "You feel cold."
+ if(40 to 59)
+ message = "You feel sick."
+ if(20 to 39)
+ message = "It feels like your skin is moving."
+ if(1 to 19)
+ message = "You can feel your cells burning."
+ if(-INFINITY to 0)
+ message = "You can feel your DNA exploding, we need to do something fast!"
+ if(stability <= 0)
+ holder.apply_status_effect(STATUS_EFFECT_DNA_MELT)
+ if(message)
+ to_chat(holder, message)
+
+
//used to update dna UI, UE, and dna.real_name.
/datum/dna/proc/update_dna_identity()
uni_identity = generate_uni_identity()
@@ -217,7 +301,7 @@
blood_type = newblood_type
unique_enzymes = generate_unique_enzymes()
uni_identity = generate_uni_identity()
- struc_enzymes = generate_struc_enzymes()
+ generate_dna_blocks()
features = random_features(species?.id)
@@ -232,10 +316,10 @@
/datum/dna/stored/check_mutation(mutation_name)
return
-/datum/dna/stored/remove_all_mutations()
+/datum/dna/stored/remove_all_mutations(list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
return
-/datum/dna/stored/remove_mutation_group(list/group)
+/datum/dna/stored/remove_mutation_group(list/group, list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
return
/////////////////////////// DNA MOB-PROCS //////////////////////
@@ -270,6 +354,7 @@
if(icon_update)
update_body()
update_hair()
+ update_body_parts()
update_mutations_overlay()// no lizard with human hulk overlay please.
@@ -280,11 +365,10 @@
return dna
-/mob/living/carbon/human/proc/hardset_dna(ui, se, newreal_name, newblood_type, datum/species/mrace, newfeatures)
+/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures)
if(newfeatures)
dna.features = newfeatures
- flavor_text = dna.features["flavor_text"] //Update the flavor_text to use new dna text
if(mrace)
var/datum/species/newrace = new mrace.type
@@ -302,10 +386,12 @@
dna.uni_identity = ui
updateappearance(icon_update=0)
- if(se)
- dna.struc_enzymes = se
+ if(LAZYLEN(mutation_index))
+ dna.mutation_index = mutation_index.Copy()
domutcheck()
+ SEND_SIGNAL(src, COMSIG_HUMAN_HARDSET_DNA, ui, mutation_index, newreal_name, newblood_type, mrace, newfeatures)
+
if(mrace || newfeatures || ui)
update_body()
update_hair()
@@ -355,19 +441,63 @@
/mob/proc/domutcheck()
return
-/mob/living/carbon/domutcheck(force_powers=0) //Set force_powers to 1 to bypass the power chance
+/mob/living/carbon/domutcheck()
if(!has_dna())
return
- for(var/datum/mutation/human/A in GLOB.good_mutations | GLOB.bad_mutations | GLOB.not_good_mutations)
- if(ismob(A.check_block(src, force_powers)))
+ for(var/mutation in dna.mutation_index)
+ if(ismob(dna.check_block(mutation)))
return //we got monkeyized/humanized, this mob will be deleted, no need to continue.
update_mutations_overlay()
+/datum/dna/proc/check_block(mutation)
+ var/datum/mutation/human/HM = get_mutation(mutation)
+ if(check_block_string(mutation))
+ if(!HM)
+ . = add_mutation(mutation, MUT_NORMAL)
+ return
+ return force_lose(HM)
+
+//Return the active mutation of a type if there is one
+/datum/dna/proc/get_mutation(A)
+ for(var/datum/mutation/human/HM in mutations)
+ if(HM.type == A)
+ return HM
+
+/datum/dna/proc/check_block_string(mutation)
+ if((LAZYLEN(mutation_index) > DNA_MUTATION_BLOCKS) || !(mutation in mutation_index))
+ return 0
+ return is_gene_active(mutation)
+
+/datum/dna/proc/is_gene_active(mutation)
+ return (mutation_index[mutation] == GET_SEQUENCE(mutation))
+
+/datum/dna/proc/set_se(on=TRUE, datum/mutation/human/HM)
+ if(!HM || !(HM.type in mutation_index) || (LAZYLEN(mutation_index) < DNA_MUTATION_BLOCKS))
+ return
+ . = TRUE
+ if(on)
+ mutation_index[HM.type] = GET_SEQUENCE(HM.type)
+ else if(GET_SEQUENCE(HM.type) == mutation_index[HM.type])
+ mutation_index[HM.type] = create_sequence(HM.type, FALSE, HM.difficulty)
+
+/datum/dna/proc/activate_mutation(mutation) //note that this returns a boolean and not a new mob
+ if(!mutation)
+ return FALSE
+ var/mutation_type = mutation
+ if(istype(mutation, /datum/mutation/human))
+ var/datum/mutation/human/M = mutation
+ mutation_type = M.type
+ if(!mutation_in_sequence(mutation_type)) //cant activate what we dont have, use add_mutation
+ return FALSE
+ add_mutation(mutation, MUT_NORMAL)
+ return TRUE
+
/////////////////////////// DNA HELPER-PROCS //////////////////////////////
+
/proc/getleftblocks(input,blocknumber,blocksize)
if(blocknumber > 1)
return copytext_char(input,1,((blocksize*blocknumber)-(blocksize-1)))
@@ -384,29 +514,47 @@
return 0
return getleftblocks(istring, blocknumber, blocksize) + replacement + getrightblocks(istring, blocknumber, blocksize)
+/datum/dna/proc/mutation_in_sequence(mutation)
+ if(!mutation)
+ return
+ if(istype(mutation, /datum/mutation/human))
+ var/datum/mutation/human/HM = mutation
+ if(HM.type in mutation_index)
+ return TRUE
+ else if(mutation in mutation_index)
+ return TRUE
+
/mob/living/carbon/proc/randmut(list/candidates, difficulty = 2)
if(!has_dna())
return
- var/datum/mutation/human/num = pick(candidates)
- . = num.force_give(src)
+ var/mutation = pick(candidates)
+ . = dna.add_mutation(mutation)
-/mob/living/carbon/proc/randmutb()
+/mob/living/carbon/proc/easy_randmut(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE)
if(!has_dna())
return
- var/datum/mutation/human/HM = pick((GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.mutations_list[RACEMUT])
- . = HM.force_give(src)
+ var/list/mutations = list()
+ if(quality & POSITIVE)
+ mutations += GLOB.good_mutations
+ if(quality & NEGATIVE)
+ mutations += GLOB.bad_mutations
+ if(quality & MINOR_NEGATIVE)
+ mutations += GLOB.not_good_mutations
+ var/list/possible = list()
+ for(var/datum/mutation/human/A in mutations)
+ if((!sequence || dna.mutation_in_sequence(A.type)) && !dna.get_mutation(A.type))
+ possible += A.type
+ if(exclude_monkey)
+ possible.Remove(RACEMUT)
+ if(LAZYLEN(possible))
+ var/mutation = pick(possible)
+ . = dna.activate_mutation(mutation)
+ if(scrambled)
+ var/datum/mutation/human/HM = dna.get_mutation(mutation)
+ if(HM)
+ HM.scrambled = TRUE
+ return TRUE
-/mob/living/carbon/proc/randmutg()
- if(!has_dna())
- return
- var/datum/mutation/human/HM = pick(GLOB.good_mutations)
- . = HM.force_give(src)
-
-/mob/living/carbon/proc/randmutvg()
- if(!has_dna())
- return
- var/datum/mutation/human/HM = pick((GLOB.good_mutations) - GLOB.mutations_list[HULK] - GLOB.mutations_list[DWARFISM])
- . = HM.force_give(src)
/mob/living/carbon/proc/randmuti()
if(!has_dna())
@@ -429,9 +577,9 @@
if(!M.has_dna())
return 0
if(se)
- for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
+ for(var/i=1, i<=DNA_MUTATION_BLOCKS, i++)
if(prob(probability))
- M.dna.struc_enzymes = setblock(M.dna.struc_enzymes, i, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters))
+ M.dna.generate_dna_blocks()
M.domutcheck()
if(ui)
for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++)
@@ -458,3 +606,41 @@
return value
/////////////////////////// DNA HELPER-PROCS
+
+/mob/living/carbon/human/proc/something_horrible()
+ if(!has_dna()) //shouldn't ever happen anyway so it's just in really weird cases
+ return
+ if(dna.stability > 0)
+ return
+ var/instability = -dna.stability
+ dna.remove_all_mutations()
+ dna.stability = 100
+ if(prob(max(70-instability,0)))
+ switch(rand(0,3)) //not complete and utter death
+ if(0)
+ monkeyize()
+ if(1)
+ gain_trauma(/datum/brain_trauma/severe/paralysis)
+ if(2)
+ corgize()
+ if(3)
+ to_chat(src, "Oh, we actually feel quite alright!")
+ else
+ switch(rand(0,3))
+ if(0)
+ gib()
+ if(1)
+ dust()
+
+ if(2)
+ death()
+ petrify(INFINITY)
+ if(3)
+ if(prob(90))
+ var/obj/item/bodypart/BP = get_bodypart(pick(BODY_ZONE_CHEST,BODY_ZONE_HEAD))
+ if(BP)
+ BP.dismember()
+ else
+ gib()
+ else
+ set_species(/datum/species/dullahan)
diff --git a/code/datums/elements/_element.dm b/code/datums/elements/_element.dm
index 17e2b12232..f74dcd0b6e 100644
--- a/code/datums/elements/_element.dm
+++ b/code/datums/elements/_element.dm
@@ -1,4 +1,11 @@
+/**
+ * A holder for simple behaviour that can be attached to many different types
+ *
+ * Only one element of each type is instanced during game init.
+ * Otherwise acts basically like a lightweight component.
+ */
/datum/element
+ /// Option flags for element behaviour
var/element_flags = NONE
/**
* The index of the first attach argument to consider for duplicate elements
@@ -7,13 +14,17 @@
*/
var/id_arg_index = INFINITY
+/// Activates the functionality defined by the element on the given target datum
/datum/element/proc/Attach(datum/target)
+ SHOULD_CALL_PARENT(1)
if(type == /datum/element)
return ELEMENT_INCOMPATIBLE
if(element_flags & ELEMENT_DETACH)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Detach, override = TRUE)
+/// Deactivates the functionality defines by the element on the given datum
/datum/element/proc/Detach(datum/source, force)
+ SHOULD_CALL_PARENT(1)
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
/datum/element/Destroy(force)
@@ -24,16 +35,17 @@
//DATUM PROCS
-/datum/proc/AddElement(eletype, ...)
- var/datum/element/ele = SSdcs.GetElement(arglist(args))
- args[1] = src
- if(ele.Attach(arglist(args)) == ELEMENT_INCOMPATIBLE)
- CRASH("Incompatible [eletype] assigned to a [type]! args: [json_encode(args)]")
+/// Finds the singleton for the element type given and attaches it to src
+/datum/proc/_AddElement(list/arguments)
+ var/datum/element/ele = SSdcs.GetElement(arguments)
+ arguments[1] = src
+ if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
+ CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
/**
* Finds the singleton for the element type given and detaches it from src
* You only need additional arguments beyond the type if you're using ELEMENT_BESPOKE
*/
-/datum/proc/RemoveElement(eletype, ...)
- var/datum/element/ele = SSdcs.GetElement(arglist(args))
+/datum/proc/_RemoveElement(list/arguments)
+ var/datum/element/ele = SSdcs.GetElement(arguments)
ele.Detach(src)
diff --git a/code/datums/elements/flavor_text.dm b/code/datums/elements/flavor_text.dm
new file mode 100644
index 0000000000..f5ad72e731
--- /dev/null
+++ b/code/datums/elements/flavor_text.dm
@@ -0,0 +1,146 @@
+GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
+
+/datum/element/flavor_text
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
+ id_arg_index = 3
+ var/flavor_name = "Flavor Text"
+ var/list/texts_by_atom = list()
+ var/addendum = "This can also be used for OOC notes and preferences!"
+ var/always_show = FALSE
+ var/max_len = MAX_FAVOR_LEN
+ var/can_edit = TRUE
+
+/datum/element/flavor_text/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FAVOR_LEN, _always_show = FALSE, _edit = TRUE)
+ . = ..()
+
+ if(. == ELEMENT_INCOMPATIBLE || !isatom(target)) //no reason why this shouldn't work on atoms too.
+ return ELEMENT_INCOMPATIBLE
+
+ if(_max_len)
+ max_len = _max_len
+ texts_by_atom[target] = copytext(text, 1, max_len)
+ if(_name)
+ flavor_name = _name
+ if(!isnull(addendum))
+ addendum = _addendum
+ always_show = _always_show
+ can_edit = _edit
+
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/show_flavor)
+
+ if(can_edit && ismob(target)) //but only mobs receive the proc/verb for the time being
+ var/mob/M = target
+ LAZYOR(GLOB.mobs_with_editable_flavor_text[M], src)
+ M.verbs |= /mob/proc/manage_flavor_tests
+
+/datum/element/flavor_text/Detach(atom/A)
+ . = ..()
+ UnregisterSignal(A, COMSIG_PARENT_EXAMINE)
+ texts_by_atom -= A
+ if(can_edit && ismob(A))
+ var/mob/M = A
+ LAZYREMOVE(GLOB.mobs_with_editable_flavor_text[M], src)
+ if(!GLOB.mobs_with_editable_flavor_text[M])
+ GLOB.mobs_with_editable_flavor_text -= M
+ M.verbs -= /mob/proc/manage_flavor_tests
+
+/datum/element/flavor_text/proc/show_flavor(atom/target, mob/user, list/examine_list)
+ if(!always_show && isliving(target))
+ var/mob/living/L = target
+ var/unknown = L.get_visible_name() == "Unknown"
+ if(!unknown && iscarbon(target))
+ var/mob/living/carbon/C = L
+ unknown = (C.wear_mask && (C.wear_mask.flags_inv & HIDEFACE)) || (C.head && (C.head.flags_inv & HIDEFACE))
+ if(unknown)
+ if(!("...?" in examine_list)) //can't think of anything better in case of multiple flavor texts.
+ examine_list += "...?"
+ return
+ var/text = texts_by_atom[target]
+ if(!text)
+ return
+ var/msg = replacetext(text, "\n", " ")
+ if(length_char(msg) <= 40)
+ examine_list += "[msg]"
+ else
+ examine_list += "[copytext_char(msg, 1, 37)]... More..."
+
+/datum/element/flavor_text/Topic(href, href_list)
+ . = ..()
+ if(.)
+ return
+ if(href_list["show_flavor"])
+ var/atom/target = locate(href_list["show_flavor"])
+ var/text = texts_by_atom[target]
+ if(text)
+ usr << browse("
[target.name][replacetext(texts_by_atom[target], "\n", "
")]", "window=[target.name];size=500x200")
+ onclose(usr, "[target.name]")
+ return TRUE
+
+/mob/proc/manage_flavor_tests()
+ set name = "Manage Flavor Texts"
+ set desc = "Used to manage your various flavor texts."
+ set category = "IC"
+
+ var/list/L = GLOB.mobs_with_editable_flavor_text[src]
+
+ if(length(L) == 1)
+ var/datum/element/flavor_text/F = L[1]
+ F.set_flavor(src)
+ return
+
+ var/list/choices = list()
+
+ for(var/i in L)
+ var/datum/element/flavor_text/F = i
+ choices[F.flavor_name] = F
+
+ var/chosen = input(src, "Which flavor text would you like to modify?") as null|anything in choices
+ if(!chosen)
+ return
+ var/datum/element/flavor_text/F = choices[chosen]
+ F.set_flavor(src)
+
+/datum/element/flavor_text/proc/set_flavor(mob/user)
+ if(!(user in texts_by_atom))
+ return FALSE
+
+ var/lower_name = lowertext(flavor_name)
+ var/new_text = stripped_multiline_input(user, "Set the [lower_name] displayed on 'examine'. [addendum]", flavor_name, texts_by_atom[usr], max_len, TRUE)
+ if(!isnull(new_text) && (user in texts_by_atom))
+ texts_by_atom[user] = html_decode(new_text)
+ to_chat(src, "Your [lower_name] has been updated.")
+ return TRUE
+ return FALSE
+
+//subtypes with additional hooks for DNA and preferences.
+/datum/element/flavor_text/carbon
+
+/datum/element/flavor_text/carbon/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FAVOR_LEN, _always_show = FALSE, _edit = TRUE)
+ if(!iscarbon(target))
+ return ELEMENT_INCOMPATIBLE
+ . = ..()
+ if(. == ELEMENT_INCOMPATIBLE)
+ return
+ RegisterSignal(target, COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, .proc/update_dna_flavor_text)
+ if(ishuman(target))
+ RegisterSignal(target, COMSIG_HUMAN_PREFS_COPIED_TO, .proc/update_prefs_flavor_text)
+ RegisterSignal(target, COMSIG_HUMAN_HARDSET_DNA, .proc/update_dna_flavor_text)
+ RegisterSignal(target, COMSIG_HUMAN_ON_RANDOMIZE, .proc/unset_flavor)
+
+/datum/element/flavor_text/carbon/Detach(mob/living/carbon/C)
+ . = ..()
+ UnregisterSignal(C, list(COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, COMSIG_HUMAN_PREFS_COPIED_TO, COMSIG_HUMAN_HARDSET_DNA, COMSIG_HUMAN_ON_RANDOMIZE))
+
+/datum/element/flavor_text/carbon/proc/update_dna_flavor_text(mob/living/carbon/C)
+ texts_by_atom[C] = C.dna.features["flavor_text"]
+
+/datum/element/flavor_text/carbon/proc/update_prefs_flavor_text(mob/living/carbon/human/H, datum/preferences/P, icon_updates = TRUE, roundstart_checks = TRUE)
+ texts_by_atom[H] = P.features["flavor_text"]
+
+/datum/element/flavor_text/carbon/set_flavor(mob/living/carbon/user)
+ . = ..()
+ if(. && user.dna)
+ user.dna.features["flavor_text"] = texts_by_atom[user]
+
+/datum/element/flavor_text/carbon/proc/unset_flavor(mob/living/carbon/user)
+ texts_by_atom[user] = ""
diff --git a/code/datums/elements/ghost_role_eligibility.dm b/code/datums/elements/ghost_role_eligibility.dm
index ab87d7f1d2..d04cf36138 100644
--- a/code/datums/elements/ghost_role_eligibility.dm
+++ b/code/datums/elements/ghost_role_eligibility.dm
@@ -17,9 +17,10 @@
penalty += roundstart_quit_limit - world.time
if(penalty)
penalty += world.realtime
- var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
- if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
- penalty = CANT_REENTER_ROUND
+ if(SSautotransfer.can_fire && SSautotransfer.maxvotes)
+ var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
+ if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
+ penalty = CANT_REENTER_ROUND
if(!(M.ckey in timeouts))
timeouts += M.ckey
timeouts[M.ckey] = 0
diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm
index 06ff0c1386..3cd5367f65 100644
--- a/code/datums/elements/mob_holder.dm
+++ b/code/datums/elements/mob_holder.dm
@@ -8,18 +8,18 @@
var/inv_slots
var/proctype //if present, will be invoked on headwear generation.
-/datum/element/mob_holder/Attach(datum/target, _worn_state, _alt_worn, _right_hand, _left_hand, _inv_slots = NONE, _proctype)
+/datum/element/mob_holder/Attach(datum/target, worn_state, alt_worn, right_hand, left_hand, inv_slots = NONE, proctype)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
- worn_state = _worn_state
- alt_worn = _alt_worn
- right_hand = _right_hand
- left_hand = _left_hand
- inv_slots = _inv_slots
- proctype = _proctype
+ src.worn_state = worn_state
+ src.alt_worn = alt_worn
+ src.right_hand = right_hand
+ src.left_hand = left_hand
+ src.inv_slots = inv_slots
+ src.proctype = proctype
RegisterSignal(target, COMSIG_CLICK_ALT, .proc/mob_try_pickup)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
@@ -138,7 +138,7 @@
destination = get_turf(loc)
AM.forceMove(destination)
-/obj/item/clothing/head/mob_holder/dropped()
+/obj/item/clothing/head/mob_holder/dropped(mob/user)
. = ..()
if(held_mob && isturf(loc))//don't release on soft-drops
release()
diff --git a/code/datums/martial.dm b/code/datums/martial/_martial.dm
similarity index 60%
rename from code/datums/martial.dm
rename to code/datums/martial/_martial.dm
index cf2adfce8c..7b7975bedb 100644
--- a/code/datums/martial.dm
+++ b/code/datums/martial/_martial.dm
@@ -5,23 +5,20 @@
var/id = "" //ID, used by mind/has_martialartcode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proc
var/current_target
var/datum/martial_art/base // The permanent style. This will be null unless the martial art is temporary
- var/deflection_chance = 0 //Chance to deflect projectiles
- var/reroute_deflection = FALSE //Delete the bullet, or actually deflect it in some direction?
var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
- var/dodge_chance = 0
var/restraining = 0 //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
var/help_verb
var/pacifism_check = TRUE //are the martial arts combos/attacks unable to be used by pacifist.
var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts
/datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
- return 0
+ return FALSE
/datum/martial_art/proc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
- return 0
+ return FALSE
/datum/martial_art/proc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
- return 0
+ return FALSE
/datum/martial_art/proc/can_use(mob/living/carbon/human/H)
return TRUE
@@ -38,51 +35,6 @@
current_target = new_target
streak = ""
-/datum/martial_art/proc/basic_hit(mob/living/carbon/human/A,mob/living/carbon/human/D)
-
- var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh)
-
- var/atk_verb = A.dna.species.attack_verb
- if(D.lying)
- atk_verb = "kick"
-
- switch(atk_verb)
- if("kick")
- A.do_attack_animation(D, ATTACK_EFFECT_KICK)
- if("slash")
- A.do_attack_animation(D, ATTACK_EFFECT_CLAW)
- if("smash")
- A.do_attack_animation(D, ATTACK_EFFECT_SMASH)
- else
- A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
-
- if(!damage)
- playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1)
- D.visible_message("[A] has attempted to [atk_verb] [D]!", \
- "[A] has attempted to [atk_verb] [D]!", null, COMBAT_MESSAGE_RANGE)
- log_combat(A, D, "attempted to [atk_verb]")
- return 0
-
- var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
- var/armor_block = D.run_armor_check(affecting, "melee")
-
- playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1)
- D.visible_message("[A] has [atk_verb]ed [D]!", \
- "[A] has [atk_verb]ed [D]!", null, COMBAT_MESSAGE_RANGE)
-
- D.apply_damage(damage, BRUTE, affecting, armor_block)
-
- log_combat(A, D, "punched")
-
- if((D.stat != DEAD) && damage >= A.dna.species.punchstunthreshold)
- D.visible_message("[A] has knocked [D] down!!", \
- "[A] has knocked [D] down!")
- D.apply_effect(40, EFFECT_KNOCKDOWN, armor_block)
- D.forcesay(GLOB.hit_appends)
- else if(D.lying)
- D.forcesay(GLOB.hit_appends)
- return 1
-
/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE)
if(!istype(H) || !H.mind)
return FALSE
@@ -121,3 +73,7 @@
if(help_verb)
H.verbs -= help_verb
return
+
+///Gets called when a projectile hits the owner. Returning anything other than BULLET_ACT_HIT will stop the projectile from hitting the mob.
+/datum/martial_art/proc/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone)
+ return BULLET_ACT_HIT
\ No newline at end of file
diff --git a/code/datums/martial/plasma_fist.dm b/code/datums/martial/plasma_fist.dm
index e38a011db0..71fb00c467 100644
--- a/code/datums/martial/plasma_fist.dm
+++ b/code/datums/martial/plasma_fist.dm
@@ -12,16 +12,16 @@
if(findtext(streak,TORNADO_COMBO))
streak = ""
Tornado(A,D)
- return 1
+ return TRUE
if(findtext(streak,THROWBACK_COMBO))
streak = ""
Throwback(A,D)
- return 1
+ return TRUE
if(findtext(streak,PLASMA_COMBO))
streak = ""
Plasma(A,D)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/martial_art/plasma_fist/proc/TornadoAnimate(mob/living/carbon/human/A)
set waitfor = FALSE
@@ -66,23 +66,20 @@
/datum/martial_art/plasma_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
- return 1
- basic_hit(A,D)
- return 1
+ return TRUE
+ return FALSE
/datum/martial_art/plasma_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("D",D)
if(check_streak(A,D))
- return 1
- basic_hit(A,D)
- return 1
+ return TRUE
+ return FALSE
/datum/martial_art/plasma_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("G",D)
if(check_streak(A,D))
- return 1
- basic_hit(A,D)
- return 1
+ return TRUE
+ return FALSE
/mob/living/carbon/human/proc/plasma_fist_help()
set name = "Recall Teachings"
diff --git a/code/datums/martial/psychotic_brawl.dm b/code/datums/martial/psychotic_brawl.dm
index be8659949d..75053dbbfc 100644
--- a/code/datums/martial/psychotic_brawl.dm
+++ b/code/datums/martial/psychotic_brawl.dm
@@ -61,8 +61,8 @@
D.throw_at(throwtarget, 4, 2, A)//So stuff gets tossed around at the same time.
D.DefaultCombatKnockdown(60)
if(7,8)
- basic_hit(A,D)
+ return FALSE
if(atk_verb)
log_combat(A, D, "[atk_verb] (Psychotic Brawling)")
- return 1
\ No newline at end of file
+ return TRUE
\ No newline at end of file
diff --git a/code/datums/martial/rising_bass.dm b/code/datums/martial/rising_bass.dm
index 22605e5624..8055bd54e9 100644
--- a/code/datums/martial/rising_bass.dm
+++ b/code/datums/martial/rising_bass.dm
@@ -7,7 +7,6 @@
/datum/martial_art/the_rising_bass
name = "The Rising Bass"
id = MARTIALART_RISINGBASS
- dodge_chance = 100
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/rising_bass_help
var/datum/action/risingbassmove/sidekick = new/datum/action/risingbassmove/sidekick()
@@ -18,24 +17,24 @@
if(findtext(streak,SIDE_KICK_COMBO))
streak = ""
sideKick(A,D)
- return 1
+ return TRUE
if(findtext(streak,SHOULDER_FLIP_COMBO))
streak = ""
shoulderFlip(A,D)
- return 1
+ return TRUE
if(findtext(streak,REPULSE_PUNCH_COMBO))
streak = ""
repulsePunch(A,D)
- return 1
+ return TRUE
if(findtext(streak,FOOT_SMASH_COMBO))
streak = ""
footSmash(A,D)
- return 1
+ return TRUE
if(findtext(streak,DEFT_SWITCH_COMBO))
streak = ""
deftSwitch(A,D)
- return 1
- return 0
+ return TRUE
+ return FALSE
//Repulse Punch - Slams the opponent far away from you.
@@ -94,7 +93,7 @@
D.forceMove(L)
log_combat(A, D, "side kicked (Rising Bass)")
return TRUE
- return basic_hit(A,D)
+ return TRUE
/datum/martial_art/the_rising_bass/proc/shoulderFlip(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
@@ -112,7 +111,7 @@
D.forceMove(L)
log_combat(A, D, "shoulder flipped (Rising Bass)")
return TRUE
- return basic_hit(A,D)
+ return FALSE
/datum/martial_art/the_rising_bass/proc/repulsePunch(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND) && repulsecool < world.time)
@@ -127,7 +126,7 @@
log_combat(A, D, "repulse punched (Rising Bass)")
repulsecool = world.time + 3 SECONDS
return TRUE
- return basic_hit(A,D)
+ return FALSE
/datum/martial_art/the_rising_bass/proc/footSmash(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
@@ -139,7 +138,7 @@
D.dropItemToGround(D.get_active_held_item())
log_combat(A, D, "foot smashed (Rising Bass)")
return TRUE
- return basic_hit(A,D)
+ return FALSE
/datum/martial_art/the_rising_bass/proc/deftSwitch(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
@@ -158,19 +157,19 @@
/datum/martial_art/the_rising_bass/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("D",D)
if(check_streak(A,D))
- return 1
+ return TRUE
return ..()
/datum/martial_art/the_rising_bass/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
- return 1
+ return TRUE
return ..()
/datum/martial_art/the_rising_bass/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("G",D)
if(check_streak(A,D))
- return 1
+ return TRUE
return ..()
/datum/martial_art/the_rising_bass/add_to_streak(element,mob/living/carbon/human/D)
@@ -178,6 +177,20 @@
return
. = ..()
+/datum/martial_art/the_rising_bass/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone)
+ . = ..()
+ if(A.incapacitated(FALSE, TRUE)) //NO STUN
+ return BULLET_ACT_HIT
+ if(!(A.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE
+ return BULLET_ACT_HIT
+ if(A.dna && A.dna.check_mutation(HULK)) //NO HULK
+ return BULLET_ACT_HIT
+ if(!isturf(A.loc)) //NO MOTHERFLIPPIN MECHS!
+ return BULLET_ACT_HIT
+ A.visible_message("[A] dodges the projectile cleanly, they're immune to ranged weapons!", "You dodge out of the way of the projectile!")
+ playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
+ return BULLET_ACT_FORCE_PIERCE
+
/mob/living/carbon/human/proc/rising_bass_help()
set name = "Recall Teachings"
set desc = "Remember the martial techniques of the Rising Bass clan."
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index 464c362a44..2b7d9e0a09 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -1,141 +1,101 @@
-#define WRIST_WRENCH_COMBO "DD"
-#define BACK_KICK_COMBO "HG"
-#define STOMACH_KNEE_COMBO "GH"
-#define HEAD_KICK_COMBO "DHH"
-#define ELBOW_DROP_COMBO "HDHDH"
+#define STRONG_PUNCH_COMBO "HH"
+#define LAUNCH_KICK_COMBO "HD"
+#define DROP_KICK_COMBO "HG"
/datum/martial_art/the_sleeping_carp
name = "The Sleeping Carp"
id = MARTIALART_SLEEPINGCARP
- deflection_chance = 100
- reroute_deflection = TRUE
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
- var/old_grab_state = null
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(findtext(streak,WRIST_WRENCH_COMBO))
+ if(findtext(streak,STRONG_PUNCH_COMBO))
streak = ""
- wristWrench(A,D)
+ strongPunch(A,D)
return TRUE
- if(findtext(streak,BACK_KICK_COMBO))
+ if(findtext(streak,LAUNCH_KICK_COMBO))
streak = ""
- backKick(A,D)
+ launchKick(A,D)
return TRUE
- if(findtext(streak,STOMACH_KNEE_COMBO))
+ if(findtext(streak,DROP_KICK_COMBO))
streak = ""
- kneeStomach(A,D)
- return TRUE
- if(findtext(streak,HEAD_KICK_COMBO))
- streak = ""
- headKick(A,D)
- return TRUE
- if(findtext(streak,ELBOW_DROP_COMBO))
- streak = ""
- elbowDrop(A,D)
+ dropKick(A,D)
return TRUE
return FALSE
-/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(CHECK_MOBILITY(D, MOBILITY_USE))
- log_combat(A, D, "wrist wrenched (Sleeping Carp)")
- A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
- D.visible_message("[A] grabs [D]'s wrist and wrenches it sideways!", \
- "[A] grabs your wrist and violently wrenches it to the side!")
- playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- D.emote("scream")
- D.dropItemToGround(D.get_active_held_item())
- D.apply_damage(5, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
- D.DefaultCombatKnockdown(60)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
- return TRUE
- return basic_hit(A,D)
+///Gnashing Teeth: Harm Harm, consistent 20 force punch on every second harm punch, has a chance to crit
+/datum/martial_art/the_sleeping_carp/proc/strongPunch(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ ///this var is so that the strong punch is always aiming for the body part the user is targeting and not trying to apply to the chest before deviating
+ var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
+ A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
+ var/atk_verb = pick("precisely kick", "brutally chop", "cleanly hit", "viciously slam")
+ ///this is the critical hit damage added to the attack if it rolls, it starts at 0 because it'll be changed when rolled
+ var/crit_damage = 0
+ D.visible_message("[A] [atk_verb]s [D]!", \
+ "[A] [atk_verb]s you!", null, null, A)
+ to_chat(A, "You [atk_verb] [D]!")
+ if(prob(10))
+ crit_damage += 20
+ playsound(get_turf(D), 'sound/weapons/bite.ogg', 50, TRUE, -1)
+ D.visible_message("[D] sputters blood as the blow strikes them with inhuman force!", "You are struck with incredible precision by [A]!")
+ log_combat(A, D, "critcal strong punched (Sleeping Carp)")//log it here because a critical can swing for 40 force and it's important for the sake of how hard they hit
+ else
+ playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1)
+ log_combat(A, D, "strong punched (Sleeping Carp)")//so as to not double up on logging
+ D.apply_damage(20 + crit_damage, BRUTE, affecting)
+ return
-/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(CHECK_MOBILITY(D, MOBILITY_STAND))
- if(A.dir == D.dir)
- log_combat(A, D, "back-kicked (Sleeping Carp)")
- A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
- D.visible_message("[A] kicks [D] in the back!", \
- "[A] kicks you in the back, making you stumble and fall!")
- step_to(D,get_step(D,D.dir),1)
- D.DefaultCombatKnockdown(80)
- playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
- return TRUE
- else
- log_combat(A, D, "missed a back-kick (Sleeping Carp) on")
- D.visible_message("[A] tries to kick [D] in the back, but misses!", \
- "[A] tries to kick you in the back, but misses!")
- return basic_hit(A,D)
+///Crashing Wave Kick: Harm Disarm combo, throws people seven tiles backwards
+/datum/martial_art/the_sleeping_carp/proc/launchKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ A.do_attack_animation(D, ATTACK_EFFECT_KICK)
+ D.visible_message("[A] kicks [D] square in the chest, sending them flying!", \
+ "You are kicked square in the chest by [A], sending you flying!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A)
+ playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ var/atom/throw_target = get_edge_target_turf(D, A.dir)
+ D.throw_at(throw_target, 7, 14, A)
+ D.apply_damage(15, BRUTE, BODY_ZONE_CHEST)
+ log_combat(A, D, "launchkicked (Sleeping Carp)")
+ return
-/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(CHECK_MOBILITY(D, MOBILITY_STAND))
- log_combat(A, D, "stomach kneed (Sleeping Carp)")
- A.do_attack_animation(D, ATTACK_EFFECT_KICK)
- D.visible_message("[A] knees [D] in the stomach!", \
- "[A] winds you with a knee in the stomach!")
- D.audible_message("[D] gags!")
- D.losebreath += 3
- D.DefaultCombatKnockdown(40)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
- playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
- return TRUE
- return basic_hit(A,D)
-
-/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(CHECK_MOBILITY(D, MOBILITY_STAND))
- log_combat(A, D, "head kicked (Sleeping Carp)")
- A.do_attack_animation(D, ATTACK_EFFECT_KICK)
- D.visible_message("[A] kicks [D] in the head!", \
- "[A] kicks you in the jaw!")
- D.apply_damage(20, BRUTE, BODY_ZONE_HEAD)
+///Keelhaul: Harm Grab combo, knocks people down, deals stamina damage while they're on the floor
+/datum/martial_art/the_sleeping_carp/proc/dropKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ A.do_attack_animation(D, ATTACK_EFFECT_KICK)
+ playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ if((D.mobility_flags & MOBILITY_STAND))
+ D.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
+ D.DefaultCombatKnockdown(50)
+ D.adjustStaminaLoss(40) //A cit specific change form the tg port to really punish anyone who tries to stand up
+ D.visible_message("[A] kicks [D] in the head, sending them face first into the floor!", \
+ "You are kicked in the head by [A], sending you crashing to the floor!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A)
+ if(!(D.mobility_flags & MOBILITY_STAND))
+ D.apply_damage(5, BRUTE, BODY_ZONE_HEAD)
+ D.adjustStaminaLoss(40)
D.drop_all_held_items()
- playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
- D.DefaultCombatKnockdown(80)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
- return TRUE
- return basic_hit(A,D)
-
-/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(!CHECK_MOBILITY(D, MOBILITY_STAND))
- log_combat(A, D, "elbow dropped (Sleeping Carp)")
- A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
- D.visible_message("[A] elbow drops [D]!", \
- "[A] piledrives you with their elbow!")
- if(D.stat)
- D.death() //FINISH HIM!
- D.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
- playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1)
- return TRUE
- return basic_hit(A,D)
+ D.visible_message("[A] kicks [D] in the head!", \
+ "You are kicked in the head by [A]!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A)
+ log_combat(A, D, "dropkicked (Sleeping Carp)")
+ return
/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(A.a_intent == INTENT_GRAB && A!=D) // A!=D prevents grabbing yourself
- add_to_streak("G",D)
- if(check_streak(A,D)) //if a combo is made no grab upgrade is done
- return TRUE
- old_grab_state = A.grab_state
- D.grabbedby(A, 1)
- if(old_grab_state == GRAB_PASSIVE)
- D.drop_all_held_items()
- A.setGrabState(GRAB_AGGRESSIVE) //Instant agressive grab if on grab intent
- log_combat(A, D, "grabbed", addition="aggressively")
- D.visible_message("[A] violently grabs [D]!", \
- "[A] violently grabs you!")
+ add_to_streak("G",D)
+ if(check_streak(A,D))
return TRUE
- return FALSE
+ log_combat(A, D, "grabbed (Sleeping Carp)")
+ return ..()
/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
return TRUE
+ var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
- var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams")
- D.visible_message("[A] [atk_verb] [D]!", \
- "[A] [atk_verb] you!")
- D.apply_damage(rand(10,15), BRUTE)
- playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1)
- if(prob(D.getBruteLoss()) && !D.lying)
- D.visible_message("[D] stumbles and falls!", "The blow sends you to the ground!")
- D.DefaultCombatKnockdown(80)
- log_combat(A, D, "[atk_verb] (Sleeping Carp)")
+ var/atk_verb = pick("kick", "chop", "hit", "slam")
+ D.visible_message("[A] [atk_verb]s [D]!", \
+ "[A] [atk_verb]s you!", null, null, A)
+ to_chat(A, "You [atk_verb] [D]!")
+ D.apply_damage(rand(10,15), BRUTE, affecting)
+ playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1)
+ log_combat(A, D, "punched (Sleeping Carp)")
return TRUE
@@ -143,17 +103,58 @@
add_to_streak("D",D)
if(check_streak(A,D))
return TRUE
+ log_combat(A, D, "disarmed (Sleeping Carp)")
return ..()
+/datum/martial_art/the_sleeping_carp/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone)
+ . = ..()
+ if(A.incapacitated(FALSE, TRUE)) //NO STUN
+ return BULLET_ACT_HIT
+ if(!(A.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE
+ return BULLET_ACT_HIT
+ if(A.dna && A.dna.check_mutation(HULK)) //NO HULK
+ return BULLET_ACT_HIT
+ if(!isturf(A.loc)) //NO MOTHERFLIPPIN MECHS!
+ return BULLET_ACT_HIT
+ if(A.in_throw_mode)
+ A.visible_message("[A] effortlessly swats the projectile aside! They can deflect projectile with their bare hands!", "You deflect the projectile!")
+ playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
+ P.firer = A
+ P.setAngle(rand(0, 360))//SHING
+ return BULLET_ACT_FORCE_PIERCE
+ return BULLET_ACT_HIT
+
/datum/martial_art/the_sleeping_carp/teach(mob/living/carbon/human/H, make_temporary = FALSE)
. = ..()
if(!.)
return
ADD_TRAIT(H, TRAIT_NOGUNS, SLEEPING_CARP_TRAIT)
+ ADD_TRAIT(H, TRAIT_PIERCEIMMUNE, SLEEPING_CARP_TRAIT)
+ ADD_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT)
+ H.physiology.brute_mod *= 0.4 //brute is really not gonna cut it
+ H.physiology.burn_mod *= 0.7 //burn is distinctly more useful against them than brute but they're still resistant
+ H.physiology.stamina_mod *= 0.5 //stun batons prove to be one of the few ways to fight them. They have stun resistance already, so I think doubling down too hard on this resistance is a bit much.
+ H.physiology.stun_mod *= 0.3 //for those rare stuns
+ H.physiology.pressure_mod *= 0.3 //go hang out with carp
+ H.physiology.cold_mod *= 0.3 //cold mods are different to burn mods, they do stack however
+ H.physiology.heat_mod *= 2 //this is mostly so sleeping carp has a viable weakness. Cooking them alive. Setting them on fire and heating them will be their biggest weakness. The reason for this is....filet jokes.
+
+ H.faction |= "carp" //:D
/datum/martial_art/the_sleeping_carp/on_remove(mob/living/carbon/human/H)
. = ..()
REMOVE_TRAIT(H, TRAIT_NOGUNS, SLEEPING_CARP_TRAIT)
+ REMOVE_TRAIT(H, TRAIT_PIERCEIMMUNE, SLEEPING_CARP_TRAIT)
+ REMOVE_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT)
+ H.physiology.brute_mod = initial(H.physiology.brute_mod)
+ H.physiology.burn_mod = initial(H.physiology.burn_mod)
+ H.physiology.stamina_mod = initial(H.physiology.stamina_mod)
+ H.physiology.stun_mod = initial(H.physiology.stun_mod)
+ H.physiology.pressure_mod = initial(H.physiology.pressure_mod) //no more carpies
+ H.physiology.cold_mod = initial(H.physiology.cold_mod)
+ H.physiology.heat_mod = initial(H.physiology.heat_mod)
+
+ H.faction -= "carp" //:(
/mob/living/carbon/human/proc/sleeping_carp_help()
set name = "Recall Teachings"
@@ -162,11 +163,10 @@
to_chat(usr, "You retreat inward and recall the teachings of the Sleeping Carp...")
- to_chat(usr, "Wrist Wrench: Disarm Disarm. Forces opponent to drop item in hand.")
- to_chat(usr, "Back Kick: Harm Grab. Opponent must be facing away. Knocks down.")
- to_chat(usr, "Stomach Knee: Grab Harm. Knocks the wind out of opponent and stuns.")
- to_chat(usr, "Head Kick: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.")
- to_chat(usr, "Elbow Drop: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.")
+ to_chat(usr, "Gnashing Teeth: Harm Harm. Deal additional damage every second punch, with a chance for even more damage!")
+ to_chat(usr, "Crashing Wave Kick: Harm Disarm. Launch people brutally across rooms, and away from you.")
+ to_chat(usr, "Keelhaul: Harm Grab. Kick opponents to the floor. Against prone targets, deal additional stamina damage and disarm them.")
+ to_chat(usr, "In addition, your body has become incredibly resilient to most forms of attack. Weapons cannot readily pierce your hardened skin, and you are highly resistant to stuns and knockdowns, and can block all projectiles in Throw Mode. However, you are not invincible, and sustained damage will take it's toll. Avoid heat at all costs!")
/obj/item/twohanded/bostaff
name = "bo staff"
diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm
index 6511950c87..c006f97bff 100644
--- a/code/datums/materials/basemats.dm
+++ b/code/datums/materials/basemats.dm
@@ -93,12 +93,12 @@ Unless you know what you're doing, only use the first three numbers. They're in
/datum/material/plasma/on_applied(atom/source, amount, material_flags)
. = ..()
if(ismovableatom(source))
- source.AddElement(/datum/element/firestacker)
+ source.AddElement(/datum/element/firestacker, amount=1)
source.AddComponent(/datum/component/explodable, 0, 0, amount / 2500, amount / 1250)
/datum/material/plasma/on_removed(atom/source, material_flags)
. = ..()
- source.RemoveElement(/datum/element/firestacker)
+ source.RemoveElement(/datum/element/firestacker, amount=1)
qdel(source.GetComponent(/datum/component/explodable))
///Can cause bluespace effects on use. (Teleportation) (Not yet implemented)
diff --git a/code/datums/mutations.dm b/code/datums/mutations.dm
index 6397bbe66b..9234e124c5 100644
--- a/code/datums/mutations.dm
+++ b/code/datums/mutations.dm
@@ -1,58 +1,55 @@
-GLOBAL_LIST_EMPTY(mutations_list)
-
/datum/mutation
var/name
-/datum/mutation/New()
- GLOB.mutations_list[name] = src
-
/datum/mutation/human
- var/dna_block
+ name = "mutation"
+ var/desc = "A mutation."
+ var/locked
var/quality
var/get_chance = 100
var/lowest_value = 256 * 8
var/text_gain_indication = ""
var/text_lose_indication = ""
var/list/mutable_appearance/visual_indicators = list()
+ var/obj/effect/proc_holder/spell/power
var/layer_used = MUTATIONS_LAYER //which mutation layer to use
var/list/species_allowed = list() //to restrict mutation to only certain species
var/health_req //minimum health required to acquire the mutation
var/limb_req //required limbs to acquire this mutation
var/time_coeff = 1 //coefficient for timed mutations
+ var/datum/dna/dna
+ var/mob/living/carbon/human/owner
+ var/instability = 0 //instability the holder gets when the mutation is not native
+ var/blocks = 4 //Amount of those big blocks with gene sequences
+ var/difficulty = 8 //Amount of missing sequences. Sometimes it removes an entire pair for 2 points
+ var/timed = FALSE //Boolean to easily check if we're going to self destruct
+ var/alias //'Mutation #49', decided every round to get some form of distinction between undiscovered mutations
+ var/scrambled = FALSE //Wheter we can read it if it's active. To avoid cheesing with mutagen
+ var/class //Decides player accesibility, sorta
+ //MUT_NORMAL - A mutation that can be activated and deactived by completing a sequence
+ //MUT_EXTRA - A mutation that is in the mutations tab, and can be given and taken away through though the DNA console. Has a 0 before it's name in the mutation section of the dna console
+ //MUT_OTHER Cannot be interacted with by players through normal means. I.E. wizards mutate
-/datum/mutation/human/proc/force_give(mob/living/carbon/human/owner)
- set_block(owner)
- . = on_acquiring(owner)
+ var/can_chromosome = CHROMOSOME_NONE //can we take chromosomes? 0: CHROMOSOME_NEVER never, 1:CHROMOSOME_NONE yeah, 2: CHROMOSOME_USED no, already have one
+ var/chromosome_name //purely cosmetic
+ var/modified = FALSE //ugly but we really don't want chromosomes and on_acquiring to overlap and apply double the powers
+ var/mutadone_proof = FALSE
-/datum/mutation/human/proc/force_lose(mob/living/carbon/human/owner)
- set_block(owner, 0)
- . = on_losing(owner)
+ //Chromosome stuff - set to -1 to prevent people from changing it. Example: It'd be a waste to decrease cooldown on mutism
+ var/stabilizer_coeff = 1 //genetic stability coeff
+ var/synchronizer_coeff = -1 //makes the mutation hurt the user less
+ var/power_coeff = -1 //boosts mutation strength
+ var/energy_coeff = -1 //lowers mutation cooldown
-/datum/mutation/human/proc/set_se(se_string, on = 1)
- if(!se_string || length(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
- return
- var/before = copytext_char(se_string, 1, ((dna_block - 1) * DNA_BLOCK_SIZE) + 1)
- var/injection = num2hex(on ? rand(lowest_value, (256 * 16) - 1) : rand(0, lowest_value - 1), DNA_BLOCK_SIZE)
- var/after = copytext_char(se_string, (dna_block * DNA_BLOCK_SIZE) + 1, 0)
- return before + injection + after
-
-/datum/mutation/human/proc/set_block(mob/living/carbon/owner, on = 1)
- if(owner && owner.has_dna())
- owner.dna.struc_enzymes = set_se(owner.dna.struc_enzymes, on)
-
-/datum/mutation/human/proc/check_block_string(se_string)
- if(!se_string || length(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
- return 0
- if(hex2num(getblock(se_string, dna_block)) >= lowest_value)
- return 1
-
-/datum/mutation/human/proc/check_block(mob/living/carbon/human/owner, force_powers=0)
- if(check_block_string(owner.dna.struc_enzymes))
- if(prob(get_chance)||force_powers)
- . = on_acquiring(owner)
- else
- . = on_losing(owner)
+/datum/mutation/human/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
+ . = ..()
+ class = class_
+ if(timer)
+ addtimer(CALLBACK(src, .proc/remove), timer)
+ timed = TRUE
+ if(copymut && istype(copymut, /datum/mutation/human))
+ copy_mutation(copymut)
/datum/mutation/human/proc/on_acquiring(mob/living/carbon/human/owner)
if(!owner || !istype(owner) || owner.stat == DEAD || (src in owner.dna.mutations))
@@ -63,7 +60,8 @@ GLOBAL_LIST_EMPTY(mutations_list)
return TRUE
if(limb_req && !owner.get_bodypart(limb_req))
return TRUE
- owner.dna.mutations.Add(src)
+ dna = owner.dna
+ dna.mutations += src
if(text_gain_indication)
to_chat(owner, text_gain_indication)
if(visual_indicators.len)
@@ -75,6 +73,10 @@ GLOBAL_LIST_EMPTY(mutations_list)
owner.overlays_standing[layer_used] = mut_overlay
owner.apply_overlay(layer_used)
+ grant_spell(owner)
+ if(!modified)
+ addtimer(CALLBACK(src, .proc/modify, 5)) //gonna want children calling ..() to run first
+
/datum/mutation/human/proc/get_visual_indicator(mob/living/carbon/human/owner)
return
@@ -102,26 +104,80 @@ GLOBAL_LIST_EMPTY(mutations_list)
mut_overlay.Remove(get_visual_indicator(owner))
owner.overlays_standing[layer_used] = mut_overlay
owner.apply_overlay(layer_used)
+ if(power)
+ owner.RemoveSpell(power)
+ qdel(src)
return 0
return 1
+/datum/mutation/human/proc/say_mod(message)
+ if(message)
+ return message
+
+/datum/mutation/human/proc/get_spans()
+ return list()
+
/mob/living/carbon/proc/update_mutations_overlay()
return
/mob/living/carbon/human/update_mutations_overlay()
for(var/datum/mutation/human/CM in dna.mutations)
if(CM.species_allowed.len && !CM.species_allowed.Find(dna.species.id))
- CM.force_lose(src) //shouldn't have that mutation at all
+ dna.force_lose(CM) //shouldn't have that mutation at all
continue
if(CM.visual_indicators.len)
var/list/mut_overlay = list()
if(overlays_standing[CM.layer_used])
mut_overlay = overlays_standing[CM.layer_used]
- var/mutable_appearance/V = CM.get_visual_indicator(src)
+ var/mutable_appearance/V = CM.get_visual_indicator()
if(!mut_overlay.Find(V)) //either we lack the visual indicator or we have the wrong one
remove_overlay(CM.layer_used)
- for(var/mutable_appearance/MA in CM.visual_indicators)
+ for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
mut_overlay.Remove(MA)
mut_overlay |= V
overlays_standing[CM.layer_used] = mut_overlay
apply_overlay(CM.layer_used)
+
+
+/datum/mutation/human/proc/modify() //called when a genome is applied so we can properly update some stats without having to remove and reapply the mutation from someone
+ if(modified || !power || !owner)
+ return
+ power.charge_max *= GET_MUTATION_ENERGY(src)
+ power.charge_counter *= GET_MUTATION_ENERGY(src)
+ modified = TRUE
+
+/datum/mutation/human/proc/copy_mutation(datum/mutation/human/HM)
+ if(!HM)
+ return
+ chromosome_name = HM.chromosome_name
+ stabilizer_coeff = HM.stabilizer_coeff
+ synchronizer_coeff = HM.synchronizer_coeff
+ power_coeff = HM.power_coeff
+ energy_coeff = HM.energy_coeff
+ mutadone_proof = HM.mutadone_proof
+ can_chromosome = HM.can_chromosome
+
+/datum/mutation/human/proc/remove_chromosome()
+ stabilizer_coeff = initial(stabilizer_coeff)
+ synchronizer_coeff = initial(synchronizer_coeff)
+ power_coeff = initial(power_coeff)
+ energy_coeff = initial(energy_coeff)
+ mutadone_proof = initial(mutadone_proof)
+ can_chromosome = initial(can_chromosome)
+ chromosome_name = null
+
+/datum/mutation/human/proc/remove()
+ if(dna)
+ dna.force_lose(src)
+ else
+ qdel(src)
+
+/datum/mutation/human/proc/grant_spell(mob/living/carbon/human/owner)
+ if(!ispath(power) || !owner)
+ return FALSE
+
+ power = new power()
+ power.action_background_icon_state = "bg_tech_blue_on"
+ power.panel = "Genetic"
+ owner.AddSpell(power)
+ return TRUE
\ No newline at end of file
diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm
new file mode 100644
index 0000000000..9074b4e0c5
--- /dev/null
+++ b/code/datums/mutations/actions.dm
@@ -0,0 +1,503 @@
+/datum/mutation/human/telepathy
+ name = "Telepathy"
+ desc = "A rare mutation that allows the user to telepathically communicate to others."
+ quality = POSITIVE
+ text_gain_indication = "You can hear your own voice echoing in your mind!"
+ text_lose_indication = "You don't hear your mind echo anymore."
+ difficulty = 12
+ power = /obj/effect/proc_holder/spell/targeted/telepathy/genetic
+ instability = 10
+ energy_coeff = 1
+
+/datum/mutation/human/telepathy/on_acquiring(mob/living/carbon/human/owner)
+ . = ..()
+
+/datum/mutation/human/telepathy/on_losing(mob/living/carbon/human/owner)
+ . = ..()
+
+/obj/effect/proc_holder/spell/targeted/telepathy/genetic
+ magic_check = FALSE
+
+/datum/mutation/human/firebreath
+ name = "Fire Breath"
+ desc = "An ancient mutation that gives lizards breath of fire."
+ quality = POSITIVE
+ difficulty = 12
+ locked = TRUE
+ text_gain_indication = "Your throat is burning!"
+ text_lose_indication = "Your throat is cooling down."
+ power = /obj/effect/proc_holder/spell/aimed/firebreath
+ instability = 30
+ energy_coeff = 1
+ power_coeff = 1
+
+/datum/mutation/human/firebreath/modify()
+ if(power)
+ var/obj/effect/proc_holder/spell/aimed/firebreath/S = power
+ S.strength = 4 + GET_MUTATION_POWER(src)
+
+/obj/effect/proc_holder/spell/aimed/firebreath
+ name = "Fire Breath"
+ desc = "You can breathe fire at a target."
+ school = "evocation"
+ charge_max = 1200
+ clothes_req = FALSE
+ range = 20
+ base_icon_state = "fireball"
+ action_icon_state = "fireball0"
+ sound = 'sound/magic/demon_dies.ogg' //horrifying lizard noises
+ active_msg = "You built up heat in your mouth."
+ deactive_msg = "You swallow the flame."
+ var/strength = 4
+
+/obj/effect/proc_holder/spell/aimed/firebreath/before_cast(list/targets)
+ . = ..()
+ if(iscarbon(usr))
+ var/mob/living/carbon/C = usr
+ if(C.is_mouth_covered())
+ C.adjust_fire_stacks(2)
+ C.IgniteMob()
+ to_chat(C,"Something in front of your mouth caught fire!")
+ return FALSE
+
+/obj/effect/proc_holder/spell/aimed/firebreath/cast(list/targets, mob/living/user)
+ var/turf/T = user.loc
+ if(!isturf(T))
+ return FALSE
+ firecone(T,user.dir,strength)
+ remove_ranged_ability()
+ charge_counter = 0
+ start_recharge()
+ on_deactivation(user)
+
+/proc/firecone(loc,dir,length)
+ var/addsides = FALSE
+ var/list/turf/recentturf = list(loc)
+ for (var/i = 0;i < length;i++)
+ var/list/turf/h = list()
+ for (var/turf/g in recentturf)
+ var/frontturf = get_step(g,dir)
+ if (addsides)
+ var/rightturf = get_step(frontturf,turn(dir,90))
+ var/leftturf = get_step(frontturf,turn(dir,270))
+ if (!(rightturf in h))
+ h += rightturf
+ if (!(leftturf in h))
+ h += leftturf
+ if (!(frontturf in h))
+ h += frontturf
+ for (var/turf/j in h)
+ if (j.blocks_air)
+ h -= j
+ continue
+ for (var/obj/o in j)
+ if (o.CanAtmosPass == ATMOS_PASS_PROC ? !o.CanAtmosPass(loc) : !o.CanAtmosPass)
+ h -= j
+ continue
+ for (var/turf/l in h)
+ new /obj/effect/hotspot(l)
+ l.hotspot_expose(700,50,1)
+ sleep(1)
+ recentturf = h
+ addsides = !addsides
+
+/datum/mutation/human/void
+ name = "Void Magnet"
+ desc = "A rare genome that attracts odd forces not usually observed."
+ quality = MINOR_NEGATIVE //upsides and downsides
+ text_gain_indication = "You feel a heavy, dull force just beyond the walls watching you."
+ instability = 30
+ power = /obj/effect/proc_holder/spell/self/void
+ energy_coeff = 1
+ synchronizer_coeff = 1
+
+/datum/mutation/human/void/on_life(mob/living/carbon/human/owner)
+ if(!isturf(owner.loc))
+ return
+ if(prob((0.5+((100-dna.stability)/20))) * GET_MUTATION_SYNCHRONIZER(src)) //very rare, but enough to annoy you hopefully. +0.5 probability for every 10 points lost in stability
+ new /obj/effect/immortality_talisman/void(get_turf(owner), owner)
+
+/obj/effect/proc_holder/spell/self/void
+ name = "Convoke Void" //magic the gathering joke here
+ desc = "A rare genome that attracts odd forces not usually observed. May sometimes pull you in randomly."
+ school = "evocation"
+ clothes_req = FALSE
+ charge_max = 600
+ invocation = "DOOOOOOOOOOOOOOOOOOOOM!!!"
+ invocation_type = "shout"
+ action_icon_state = "void_magnet"
+ var/in_use = FALSE //so it doesnt cast while you are already deep innit
+
+/obj/effect/proc_holder/spell/self/void/can_cast(mob/user = usr)
+ . = ..()
+ if(!isturf(user.loc))
+ return FALSE
+
+/obj/effect/proc_holder/spell/self/void/cast(mob/user = usr)
+ . = ..()
+ new /obj/effect/immortality_talisman/void(get_turf(user), user)
+
+/datum/mutation/human/shock
+ name = "Shock Touch"
+ desc = "The affected can channel excess electricity through their hands without shocking themselves, allowing them to shock others."
+ quality = POSITIVE
+ locked = TRUE
+ difficulty = 16
+ text_gain_indication = "You feel power flow through your hands."
+ text_lose_indication = "The energy in your hands subsides."
+ power = /obj/effect/proc_holder/spell/targeted/touch/shock
+ instability = 30
+
+/obj/effect/proc_holder/spell/targeted/touch/shock
+ name = "Shock Touch"
+ desc = "Channel electricity to your hand to shock people with."
+ drawmessage = "You channel electricity into your hand."
+ dropmessage = "You let the electricity from your hand dissipate."
+ hand_path = /obj/item/melee/touch_attack/shock
+ charge_max = 400
+ clothes_req = FALSE
+ action_icon_state = "zap"
+
+/obj/item/melee/touch_attack/shock
+ name = "\improper shock touch"
+ desc = "This is kind of like when you rub your feet on a shag rug so you can zap your friends, only a lot less safe."
+ catchphrase = null
+ on_use_sound = 'sound/weapons/zapbang.ogg'
+ icon_state = "zapper"
+ item_state = "zapper"
+
+/obj/item/melee/touch_attack/shock/afterattack(atom/target, mob/living/carbon/user, proximity)
+ if(!proximity || !isliving(target))
+ return
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ if(C.electrocute_act(15, user, 1, SHOCK_NOSTUN))//doesnt stun. never let this stun
+ C.dropItemToGround(C.get_active_held_item())
+ C.dropItemToGround(C.get_inactive_held_item())
+ C.confused += 10
+ C.visible_message("[user] electrocutes [target]!","[user] electrocutes you!")
+ return ..()
+ else
+ user.visible_message("[user] fails to electrocute [target]!")
+ return ..()
+ else if(isliving(target))
+ var/mob/living/L = target
+ L.electrocute_act(15, user, 1, SHOCK_NOSTUN)
+ L.visible_message("[user] electrocutes [target]!","[user] electrocutes you!")
+ return ..()
+ else
+ to_chat(user,"The electricity doesn't seem to affect [target]...")
+ return ..()
+
+
+/datum/mutation/human/olfaction
+ name = "Transcendent Olfaction"
+ desc = "Your sense of smell is comparable to that of a canine."
+ quality = POSITIVE
+ difficulty = 12
+ text_gain_indication = "Smells begin to make more sense..."
+ text_lose_indication = "Your sense of smell goes back to normal."
+ power = /obj/effect/proc_holder/spell/targeted/olfaction
+ instability = 30
+ synchronizer_coeff = 1
+ var/reek = 200
+
+/datum/mutation/human/olfaction/modify()
+ if(power)
+ var/obj/effect/proc_holder/spell/targeted/olfaction/S = power
+ S.sensitivity = GET_MUTATION_SYNCHRONIZER(src)
+
+/obj/effect/proc_holder/spell/targeted/olfaction
+ name = "Remember the Scent"
+ desc = "Get a scent off of the item you're currently holding to track it. With an empty hand, you'll track the scent you've remembered."
+ charge_max = 100
+ clothes_req = FALSE
+ range = -1
+ include_user = TRUE
+ action_icon_state = "nose"
+ var/mob/living/carbon/tracking_target
+ var/list/mob/living/carbon/possible = list()
+ var/sensitivity = 1
+
+/obj/effect/proc_holder/spell/targeted/olfaction/cast(list/targets, mob/living/user = usr)
+ //can we sniff? is there miasma in the air?
+ var/datum/gas_mixture/air = user.loc.return_air()
+ var/list/cached_gases = air.gases
+
+ if(cached_gases[/datum/gas/miasma])
+ user.adjust_disgust(sensitivity * 45)
+ to_chat(user, "With your overly sensitive nose, you get a whiff of stench and feel sick! Try moving to a cleaner area!")
+ return
+
+ var/atom/sniffed = user.get_active_held_item()
+ if(sniffed)
+ var/old_target = tracking_target
+ possible = list()
+ for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
+ if(md5(H.dna.uni_identity) in sniffed.fingerprints)
+ possible |= H
+ if(!length(possible))
+ to_chat(user,"Despite your best efforts, there are no scents to be found on [sniffed]...")
+ return
+ tracking_target = input(user, "Choose a scent to remember.", "Scent Tracking") as null|anything in sortNames(possible)
+ if(!tracking_target)
+ if(!old_target)
+ to_chat(user,"You decide against remembering any scents. Instead, you notice your own nose in your peripheral vision. This goes on to remind you of that one time you started breathing manually and couldn't stop. What an awful day that was.")
+ return
+ tracking_target = old_target
+ on_the_trail(user)
+ return
+ to_chat(user,"You pick up the scent of [tracking_target]. The hunt begins.")
+ on_the_trail(user)
+ return
+
+ if(!tracking_target)
+ to_chat(user,"You're not holding anything to smell, and you haven't smelled anything you can track. You smell your skin instead; it's kinda salty.")
+ return
+
+ on_the_trail(user)
+
+/obj/effect/proc_holder/spell/targeted/olfaction/proc/on_the_trail(mob/living/user)
+ if(!tracking_target)
+ to_chat(user,"You're not tracking a scent, but the game thought you were. Something's gone wrong! Report this as a bug.")
+ return
+ if(tracking_target == user)
+ to_chat(user,"You smell out the trail to yourself. Yep, it's you.")
+ return
+ if(usr.z < tracking_target.z)
+ to_chat(user,"The trail leads... way up above you? Huh. They must be really, really far away.")
+ return
+ else if(usr.z > tracking_target.z)
+ to_chat(user,"The trail leads... way down below you? Huh. They must be really, really far away.")
+ return
+ var/direction_text = "[dir2text(get_dir(usr, tracking_target))]"
+ if(direction_text)
+ to_chat(user,"You consider [tracking_target]'s scent. The trail leads [direction_text].")
+
+
+/datum/mutation/human/self_amputation
+ name = "Autotomy"
+ desc = "Allows a creature to voluntary discard a random appendage."
+ quality = POSITIVE
+ text_gain_indication = "Your joints feel loose."
+ instability = 30
+ power = /obj/effect/proc_holder/spell/self/self_amputation
+
+ energy_coeff = 1
+ synchronizer_coeff = 1
+
+/obj/effect/proc_holder/spell/self/self_amputation
+ name = "Drop a limb"
+ desc = "Concentrate to make a random limb pop right off your body."
+ clothes_req = FALSE
+ human_req = FALSE
+ charge_max = 100
+ action_icon_state = "autotomy"
+
+/obj/effect/proc_holder/spell/self/self_amputation/cast(list/targets, mob/user = usr)
+ if(!iscarbon(user))
+ return
+
+ var/mob/living/carbon/C = user
+ if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
+ return
+
+ var/list/parts = list()
+ for(var/X in C.bodyparts)
+ var/obj/item/bodypart/BP = X
+ if(BP.body_part != HEAD && BP.body_part != CHEST)
+ if(BP.dismemberable)
+ parts += BP
+ if(!parts.len)
+ to_chat(usr, "You can't shed any more limbs!")
+ return
+
+ var/obj/item/bodypart/BP = pick(parts)
+ BP.dismember()
+
+//spider webs
+/datum/mutation/human/webbing
+ name = "Webbing Production"
+ desc = "Allows the user to lay webbing, and travel through it."
+ quality = POSITIVE
+ text_gain_indication = "Your skin feels webby."
+ instability = 15
+ power = /obj/effect/proc_holder/spell/self/lay_genetic_web
+
+/obj/effect/proc_holder/spell/self/lay_genetic_web
+ name = "Lay Web"
+ desc = "Drops a web. Only you will be able to traverse your web easily, making it pretty good for keeping you safe."
+ clothes_req = FALSE
+ human_req = FALSE
+ charge_max = 4 SECONDS //the same time to lay a web
+ action_icon = 'icons/mob/actions/actions_genetic.dmi'
+ action_icon_state = "lay_web"
+
+/obj/effect/proc_holder/spell/self/lay_genetic_web/cast(list/targets, mob/user = usr)
+ var/failed = FALSE
+ if(!isturf(user.loc))
+ to_chat(user, "You can't lay webs here!")
+ failed = TRUE
+ var/turf/T = get_turf(user)
+ var/obj/structure/spider/stickyweb/genetic/W = locate() in T
+ if(W)
+ to_chat(user, "There's already a web here!")
+ failed = TRUE
+ if(failed)
+ revert_cast(user)
+ return FALSE
+
+ user.visible_message("[user] begins to secrete a sticky substance.","You begin to lay a web.")
+ if(!do_after(user, 4 SECONDS, target = T))
+ to_chat(user, "Your web spinning was interrupted!")
+ return
+ else
+ new /obj/structure/spider/stickyweb/genetic(T, user)
+
+
+/datum/mutation/human/tongue_spike
+ name = "Tongue Spike"
+ desc = "Allows a creature to voluntary shoot their tongue out as a deadly weapon."
+ quality = POSITIVE
+ text_gain_indication = "Your feel like you can throw your voice."
+ instability = 15
+ power = /obj/effect/proc_holder/spell/self/tongue_spike
+
+ energy_coeff = 1
+ synchronizer_coeff = 1
+
+/obj/effect/proc_holder/spell/self/tongue_spike
+ name = "Launch spike"
+ desc = "Shoot your tongue out in the direction you're facing, embedding it and dealing damage until they remove it."
+ clothes_req = FALSE
+ human_req = TRUE
+ charge_max = 100
+ action_icon = 'icons/mob/actions/actions_genetic.dmi'
+ action_icon_state = "spike"
+ var/spike_path = /obj/item/hardened_spike
+
+/obj/effect/proc_holder/spell/self/tongue_spike/cast(list/targets, mob/user = usr)
+ if(!iscarbon(user))
+ return
+
+ var/mob/living/carbon/C = user
+ if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
+ return
+ var/obj/item/organ/tongue/tongue
+ for(var/org in C.internal_organs)
+ if(istype(org, /obj/item/organ/tongue))
+ tongue = org
+ break
+
+ if(!tongue)
+ to_chat(C, "You don't have a tongue to shoot!")
+ return
+
+ tongue.Remove(C, special = TRUE)
+ var/obj/item/hardened_spike/spike = new spike_path(get_turf(C), C)
+ tongue.forceMove(spike)
+ spike.throw_at(get_edge_target_turf(C,C.dir), 14, 4, C)
+
+/obj/item/hardened_spike
+ name = "biomass spike"
+ desc = "Hardened biomass, shaped into a spike. Very pointy!"
+ icon_state = "tonguespike"
+ force = 2
+ throwforce = 15 //15 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = i didnt do the math
+ throw_speed = 4
+ embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0)
+ w_class = WEIGHT_CLASS_SMALL
+ sharpness = IS_SHARP
+ var/mob/living/carbon/human/fired_by
+
+/obj/item/hardened_spike/Initialize(mapload, firedby)
+ . = ..()
+ fired_by = firedby
+ addtimer(CALLBACK(src, .proc/checkembedded), 5 SECONDS)
+
+/obj/item/hardened_spike/proc/checkembedded()
+ if(ishuman(loc))
+ var/mob/living/carbon/human/embedtest = loc
+ for(var/l in embedtest.bodyparts)
+ var/obj/item/bodypart/limb = l
+ if(src in limb.embedded_objects)
+ return limb
+ unembedded()
+
+/obj/item/hardened_spike/unembedded()
+ var/turf/T = get_turf(src)
+ visible_message("[src] cracks and twists, changing shape!")
+ for(var/i in contents)
+ var/obj/o = i
+ o.forceMove(T)
+ qdel(src)
+
+/datum/mutation/human/tongue_spike/chem
+ name = "Chem Spike"
+ desc = "Allows a creature to voluntary shoot their tongue out as biomass, allowing a long range transfer of chemicals."
+ quality = POSITIVE
+ text_gain_indication = "Your feel like you can really connect with people by throwing your voice."
+ instability = 15
+ locked = TRUE
+ power = /obj/effect/proc_holder/spell/self/tongue_spike/chem
+ energy_coeff = 1
+ synchronizer_coeff = 1
+
+/obj/effect/proc_holder/spell/self/tongue_spike/chem
+ name = "Launch chem spike"
+ desc = "Shoot your tongue out in the direction you're facing, embedding it for a very small amount of damage. While the other person has the spike embedded, you can transfer your chemicals to them."
+ action_icon_state = "spikechem"
+ spike_path = /obj/item/hardened_spike/chem
+
+/obj/item/hardened_spike/chem
+ name = "chem spike"
+ desc = "Hardened biomass, shaped into... something."
+ icon_state = "tonguespikechem"
+ throwforce = 2 //2 + 2 (WEIGHT_CLASS_SMALL) * 0 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = i didnt do the math again but very low or smthin
+ embedding = list("embedded_pain_multiplier" = 0, "embed_chance" = 100, "embedded_fall_chance" = 0, "embedded_pain_chance" = 0, "embedded_ignore_throwspeed_threshold" = TRUE) //never hurts once it's in you
+ var/been_places = FALSE
+ var/datum/action/innate/send_chems/chems
+
+/obj/item/hardened_spike/chem/embedded(mob/living/carbon/human/embedded_mob)
+ if(been_places)
+ return
+ been_places = TRUE
+ chems = new
+ chems.transfered = embedded_mob
+ chems.spikey = src
+ to_chat(fired_by, "Link established! Use the \"Transfer Chemicals\" ability to send your chemicals to the linked target!")
+ chems.Grant(fired_by)
+
+/obj/item/hardened_spike/chem/unembedded()
+ to_chat(fired_by, "Link lost!")
+ QDEL_NULL(chems)
+ ..()
+
+/datum/action/innate/send_chems
+ icon_icon = 'icons/mob/actions/actions_genetic.dmi'
+ background_icon_state = "bg_spell"
+ check_flags = AB_CHECK_CONSCIOUS
+ button_icon_state = "spikechemswap"
+ name = "Transfer Chemicals"
+ desc = "Send all of your reagents into whomever the chem spike is embedded in. One use."
+ var/obj/item/hardened_spike/chem/spikey
+ var/mob/living/carbon/human/transfered
+
+/datum/action/innate/send_chems/Activate()
+ if(!ishuman(transfered) || !ishuman(owner))
+ return
+ var/mob/living/carbon/human/transferer = owner
+
+ to_chat(transfered, "You feel a tiny prick!")
+ transferer.reagents.trans_to(transfered, transferer.reagents.total_volume, 1, 1, 0)
+
+ var/obj/item/bodypart/L = spikey.checkembedded()
+
+ L.embedded_objects -= spikey
+ //this is where it would deal damage, if it transfers chems it removes itself so no damage
+ spikey.forceMove(get_turf(L))
+ transfered.visible_message("[spikey] falls out of [transfered]!")
+ if(!transfered.has_embedded_objects())
+ transfered.clear_alert("embeddedobject")
+ SEND_SIGNAL(transfered, COMSIG_CLEAR_MOOD_EVENT, "embedded")
+ spikey.unembedded()
diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm
new file mode 100644
index 0000000000..a2700f7862
--- /dev/null
+++ b/code/datums/mutations/antenna.dm
@@ -0,0 +1,108 @@
+/datum/mutation/human/antenna
+ name = "Antenna"
+ desc = "The affected person sprouts an antenna. This is known to allow them to access common radio channels passively."
+ quality = POSITIVE
+ text_gain_indication = "You feel an antenna sprout from your forehead."
+ text_lose_indication = "Your antenna shrinks back down."
+ instability = 5
+ difficulty = 8
+ var/obj/item/implant/radio/antenna/linked_radio
+
+/obj/item/implant/radio/antenna
+ name = "internal antenna organ"
+ desc = "The internal organ part of the antenna. Science has not yet given it a good name."
+ icon = 'icons/obj/radio.dmi'//maybe make a unique sprite later. not important
+ icon_state = "walkietalkie"
+
+/obj/item/implant/radio/antenna/Initialize(mapload)
+ ..()
+ if (radio)
+ radio.name = "internal antenna"
+
+/datum/mutation/human/antenna/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ linked_radio = new(owner)
+ linked_radio.implant(owner, null, TRUE, TRUE)
+
+/datum/mutation/human/antenna/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ if(linked_radio)
+ linked_radio.Destroy()
+
+/datum/mutation/human/antenna/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
+ ..()
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "antenna", -FRONT_MUTATIONS_LAYER+1))//-MUTATIONS_LAYER+1
+
+/datum/mutation/human/antenna/get_visual_indicator(mob/living/carbon/human/owner)
+ return visual_indicators[type][1]
+
+/datum/mutation/human/mindreader
+ name = "Mind Reader"
+ desc = "The affected person can look into the recent memories of others."
+ quality = POSITIVE
+ text_gain_indication = "You hear distant voices at the corners of your mind."
+ text_lose_indication = "The distant voices fade."
+ power = /obj/effect/proc_holder/spell/targeted/mindread
+ instability = 40
+ difficulty = 8
+ locked = TRUE
+
+/obj/effect/proc_holder/spell/targeted/mindread
+ name = "Mindread"
+ desc = "Read the target's mind."
+ charge_max = 300
+ range = 7
+ clothes_req = FALSE
+ action_icon_state = "mindread"
+
+/obj/effect/proc_holder/spell/targeted/mindread/cast(list/targets, mob/living/carbon/human/user = usr)
+ for(var/mob/living/M in targets)
+ if(usr.anti_magic_check(FALSE, FALSE, TRUE, 0) || M.anti_magic_check(FALSE, FALSE, TRUE, 0))
+ to_chat(usr, "As you reach out with your mind, you're suddenly stopped by a vision of a massive tinfoil wall that streches beyond visible range. It seems you've been foiled.")
+ return
+ if(M.stat == DEAD)
+ to_chat(user, "[M] is dead!")
+ return
+ if(M.mind)
+ to_chat(user, "You plunge into [M]'s mind...")
+ if(prob(20))
+ to_chat(M, "You feel something foreign enter your mind.")//chance to alert the read-ee
+ var/list/recent_speech = list()
+ var/list/say_log = list()
+ var/log_source = M.logging
+ for(var/log_type in log_source)//this whole loop puts the read-ee's say logs into say_log in an easy to access way
+ var/nlog_type = text2num(log_type)
+ if(nlog_type & LOG_SAY)
+ var/list/reversed = log_source[log_type]
+ if(islist(reversed))
+ say_log = reverseRange(reversed.Copy())
+ break
+ if(LAZYLEN(say_log))
+ for(var/spoken_memory in say_log)
+ if(recent_speech.len >= 3)//up to 3 random lines of speech, favoring more recent speech
+ break
+ if(prob(50))
+ recent_speech[spoken_memory] = say_log[spoken_memory]
+ if(recent_speech.len)
+ to_chat(user, "You catch some drifting memories of their past conversations...")
+ for(var/spoken_memory in recent_speech)
+ to_chat(user, "[recent_speech[spoken_memory]]")
+ if(iscarbon(M))
+ var/mob/living/carbon/human/H = M
+ to_chat(user, "You find that their intent is to [H.a_intent]...")
+ var/datum/dna/the_dna = H.has_dna()
+ if(the_dna)
+ to_chat(user, "You uncover that [H.p_their()] true identity is [the_dna.real_name].")
+ else
+ to_chat(user, "You can't find a mind to read inside of [M]!")
+
+/datum/mutation/human/mindreader/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
+ ..()
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "antenna", -FRONT_MUTATIONS_LAYER+1))
+
+/datum/mutation/human/mindreader/get_visual_indicator(mob/living/carbon/human/owner)
+ return visual_indicators[type][1]
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index a32220aa43..e229fdde9a 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -3,16 +3,19 @@
//Epilepsy gives a very small chance to have a seizure every life tick, knocking you unconscious.
/datum/mutation/human/epilepsy
name = "Epilepsy"
+ desc = "A genetic defect that sporadically causes seizures."
quality = NEGATIVE
text_gain_indication = "You get a headache."
+ synchronizer_coeff = 1
+ power_coeff = 1
/datum/mutation/human/epilepsy/on_life(mob/living/carbon/human/owner)
- if(prob(1) && owner.stat == CONSCIOUS)
+ if(prob(1 * GET_MUTATION_SYNCHRONIZER(src)) && owner.stat == CONSCIOUS)
owner.visible_message("[owner] starts having a seizure!", "You have a seizure!")
- owner.Unconscious(200)
- owner.Jitter(1000)
+ owner.Unconscious(200 * GET_MUTATION_POWER(src))
+ owner.Jitter(1000 * GET_MUTATION_POWER(src))
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "epilepsy", /datum/mood_event/epilepsy)
- addtimer(CALLBACK(src, .proc/jitter_less, owner), 90)
+ addtimer(CALLBACK(src, .proc/jitter_less), 90)
/datum/mutation/human/epilepsy/proc/jitter_less(mob/living/carbon/human/owner)
if(owner)
@@ -22,19 +25,23 @@
//Unstable DNA induces random mutations!
/datum/mutation/human/bad_dna
name = "Unstable DNA"
+ desc = "Strange mutation that causes the holder to randomly mutate."
quality = NEGATIVE
text_gain_indication = "You feel strange."
+ locked = TRUE
/datum/mutation/human/bad_dna/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
to_chat(owner, text_gain_indication)
var/mob/new_mob
if(prob(95))
if(prob(50))
- new_mob = owner.randmutb()
+ new_mob = owner.easy_randmut(NEGATIVE + MINOR_NEGATIVE)
else
new_mob = owner.randmuti()
else
- new_mob = owner.randmutg()
+ new_mob = owner.easy_randmut(POSITIVE)
if(new_mob && ismob(new_mob))
owner = new_mob
. = owner
@@ -44,21 +51,31 @@
//Cough gives you a chronic cough that causes you to drop items.
/datum/mutation/human/cough
name = "Cough"
+ desc = "A chronic cough."
quality = MINOR_NEGATIVE
text_gain_indication = "You start coughing."
+ synchronizer_coeff = 1
+ power_coeff = 1
/datum/mutation/human/cough/on_life(mob/living/carbon/human/owner)
- if(prob(5) && owner.stat == CONSCIOUS)
+ if(prob(5 * GET_MUTATION_SYNCHRONIZER(src)) && owner.stat == CONSCIOUS)
owner.drop_all_held_items()
owner.emote("cough")
+ if(GET_MUTATION_POWER(src) > 1)
+ var/cough_range = GET_MUTATION_POWER(src) * 4
+ var/turf/target = get_ranged_target_turf(owner, turn(owner.dir, 180), cough_range)
+ owner.throw_at(target, cough_range, GET_MUTATION_POWER(src))
+
//Dwarfism shrinks your body and lets you pass tables.
/datum/mutation/human/dwarfism
name = "Dwarfism"
+ desc = "A mutation believed to be the cause of dwarfism."
quality = POSITIVE
- get_chance = 15
- lowest_value = 256 * 12
+ difficulty = 16
+ instability = 5
+ locked = TRUE // Default intert species for now, so locked from regular pool.
/datum/mutation/human/dwarfism/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -78,6 +95,7 @@
//Clumsiness has a very large amount of small drawbacks depending on item.
/datum/mutation/human/clumsy
name = "Clumsiness"
+ desc = "A genome that inhibits certain brain functions, causing the holder to appear clumsy. Honk"
quality = MINOR_NEGATIVE
text_gain_indication = "You feel lightheaded."
@@ -95,11 +113,13 @@
//Tourettes causes you to randomly stand in place and shout.
/datum/mutation/human/tourettes
name = "Tourette's Syndrome"
+ desc = "A chronic twitch that forces the user to use colorful language."
quality = NEGATIVE
text_gain_indication = "You twitch."
+ synchronizer_coeff = 1
/datum/mutation/human/tourettes/on_life(mob/living/carbon/human/owner)
- if(prob(10) && owner.stat == CONSCIOUS && !owner.IsStun())
+ if(prob(10 * GET_MUTATION_SYNCHRONIZER(src)) && owner.stat == CONSCIOUS && !owner.IsStun())
owner.Stun(200)
switch(rand(1, 3))
if(1)
@@ -117,6 +137,7 @@
//Deafness makes you deaf.
/datum/mutation/human/deaf
name = "Deafness"
+ desc = "The holder of this genome is completely deaf."
quality = NEGATIVE
text_gain_indication = "You can't seem to hear anything."
@@ -134,8 +155,10 @@
//Monified turns you into a monkey.
/datum/mutation/human/race
name = "Monkified"
+ desc = "A strange genome, believing to be what differentiates monkeys from humans."
quality = NEGATIVE
time_coeff = 2
+ locked = TRUE //Species specific, keep out of actual gene pool
/datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -145,3 +168,202 @@
/datum/mutation/human/race/on_losing(mob/living/carbon/monkey/owner)
if(owner && istype(owner) && owner.stat != DEAD && (owner.dna.mutations.Remove(src)))
. = owner.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
+
+
+/datum/mutation/human/glow
+ name = "Glowy"
+ desc = "You permanently emit a light with a random color and intensity."
+ quality = POSITIVE
+ text_gain_indication = "Your skin begins to glow softly."
+ instability = 5
+ var/obj/effect/dummy/luminescent_glow/glowth //shamelessly copied from luminescents
+ var/glow = 1.5
+ power_coeff = 1
+
+/datum/mutation/human/glow/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ glowth = new(owner)
+ glowth.set_light(glow, glow, dna.features["mcolor"])
+
+/datum/mutation/human/glow/modify(mob/living/carbon/human/owner)
+ if(glowth)
+ glowth.set_light(glow + GET_MUTATION_POWER(src) , glow + GET_MUTATION_POWER(src), dna.features["mcolor"])
+
+/datum/mutation/human/glow/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ qdel(glowth)
+
+/datum/mutation/human/strong
+ name = "Strength"
+ desc = "The user's muscles slightly expand."
+ quality = POSITIVE
+ text_gain_indication = "You feel strong."
+ difficulty = 16
+
+/datum/mutation/human/fire
+ name = "Fiery Sweat"
+ desc = "The user's skin will randomly combust, but is generally alot more resilient to burning."
+ quality = NEGATIVE
+ text_gain_indication = "You feel hot."
+ text_lose_indication = "You feel a lot cooler."
+ difficulty = 14
+ synchronizer_coeff = 1
+ power_coeff = 1
+
+/datum/mutation/human/fire/on_life(mob/living/carbon/human/owner)
+ if(prob((1+(100-dna.stability)/10)) * GET_MUTATION_SYNCHRONIZER(src))
+ owner.adjust_fire_stacks(2 * GET_MUTATION_POWER(src))
+ owner.IgniteMob()
+
+/datum/mutation/human/fire/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ owner.physiology.burn_mod *= 0.5
+
+/datum/mutation/human/fire/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ owner.physiology.burn_mod *= 2
+
+/datum/mutation/human/insulated
+ name = "Insulated"
+ desc = "The affected person does not conduct electricity."
+ quality = POSITIVE
+ text_gain_indication = "Your fingertips go numb."
+ text_lose_indication = "Your fingertips regain feeling."
+ difficulty = 16
+ instability = 25
+
+/datum/mutation/human/insulated/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, "genetics")
+
+/datum/mutation/human/insulated/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "genetics")
+
+
+/datum/mutation/human/glow/anti
+ name = "Anti-Glow"
+ desc = "Your skin seems to attract and absorb nearby light creating 'darkness' around you."
+ text_gain_indication = "Your light around you seems to disappear."
+ glow = -3.5 //Slightly stronger, since negating light tends to be harder than making it.
+ locked = TRUE
+
+/datum/mutation/human/stimmed
+ name = "Stimmed"
+ desc = "The user's chemical balance is more robust."
+ quality = POSITIVE
+ text_gain_indication = "You feel stimmed."
+ difficulty = 16
+
+/datum/mutation/human/paranoia
+ name = "Paranoia"
+ desc = "Subject is easily terrified, and may suffer from hallucinations."
+ quality = NEGATIVE
+ text_gain_indication = "You feel screams echo through your mind..."
+ text_lose_indication = "The screaming in your mind fades."
+
+/datum/mutation/human/paranoia/on_life(mob/living/carbon/human/owner)
+ if(prob(5) && owner.stat == CONSCIOUS)
+ owner.emote("scream")
+ owner.jitteriness = min(max(0, owner.jitteriness + 5), 30)
+ if(prob(25))
+ to_chat(owner,"You feel someone creeping in on you...")
+ owner.hallucination += 20
+
+
+/datum/mutation/human/badblink
+ name = "Spatial Instability"
+ desc = "The victim of the mutation has a very weak link to spatial reality, and may be displaced. Often causes extreme nausea."
+ quality = NEGATIVE
+ text_gain_indication = "The space around you twists sickeningly."
+ text_lose_indication = "The space around you settles back to normal."
+ difficulty = 18//high so it's hard to unlock and abuse
+ instability = 10
+ synchronizer_coeff = 1
+ energy_coeff = 1
+ power_coeff = 1
+ var/warpchance = 0
+
+/datum/mutation/human/badblink/on_life()
+ if (!owner)
+ return
+ if(prob(warpchance))
+ var/warpmessage = pick(
+ "With a sickening 720-degree twist of [owner.p_their()] back, [owner] vanishes into thin air.",
+ "[owner] does some sort of strange backflip into another dimension. It looks pretty painful.",
+ "[owner] does a jump to the left, a step to the right, and warps out of reality.",
+ "[owner]'s torso starts folding inside out until it vanishes from reality, taking [owner] with it.",
+ "One moment, you see [owner]. The next, [owner] is gone.")
+ owner.visible_message(warpmessage, "You feel a wave of nausea as you fall through reality!")
+ var/warpdistance = rand(10,15) * GET_MUTATION_POWER(src)
+ do_teleport(owner, get_turf(owner), warpdistance, channel = TELEPORT_CHANNEL_FREE)
+ owner.adjust_disgust(GET_MUTATION_SYNCHRONIZER(src) * (warpchance * warpdistance))
+ warpchance = 0
+ owner.visible_message("[owner] appears out of nowhere!")
+ else
+ warpchance += 0.25 * GET_MUTATION_ENERGY(src)
+
+/datum/mutation/human/acidflesh
+ name = "Acidic Flesh"
+ desc = "Subject has acidic chemicals building up underneath the skin. This is often lethal."
+ quality = NEGATIVE
+ text_gain_indication = "A horrible burning sensation envelops you as your flesh turns to acid!"
+ text_lose_indication = "A feeling of relief fills you as your flesh goes back to normal."
+ difficulty = 18//high so it's hard to unlock and use on others
+ var/msgcooldown = 0
+
+/datum/mutation/human/acidflesh/on_life()
+ if(prob(25))
+ if(world.time > msgcooldown)
+ to_chat(owner, "Your acid flesh bubbles...")
+ msgcooldown = world.time + 200
+ if(prob(15))
+ owner.acid_act(rand(30,50), 10)
+ owner.visible_message("[owner]'s skin bubbles and pops.", "Your bubbling flesh pops! It burns!")
+ playsound(owner,'sound/weapons/sear.ogg', 50, TRUE)
+
+/datum/mutation/human/gigantism
+ name = "Gigantism"//negative version of dwarfism
+ desc = "The cells within the subject spread out to cover more area, making the subject appear larger."
+ quality = MINOR_NEGATIVE
+ difficulty = 12
+
+/datum/mutation/human/gigantism/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ owner.resize = 1.25
+ owner.update_transform()
+ owner.visible_message("[owner] suddenly grows!", "Everything around you seems to shrink..")
+
+/datum/mutation/human/gigantism/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ owner.resize = 0.8
+ owner.update_transform()
+ owner.visible_message("[owner] suddenly shrinks!", "Everything around you seems to grow..")
+
+/datum/mutation/human/spastic
+ name = "Spastic"
+ desc = "Subject suffers from muscle spasms."
+ quality = NEGATIVE
+ text_gain_indication = "You flinch."
+ text_lose_indication = "Your flinching subsides."
+ difficulty = 16
+
+/datum/mutation/human/spastic/on_acquiring()
+ if(..())
+ return
+ if (owner)
+ owner.apply_status_effect(STATUS_EFFECT_SPASMS)
+
+/datum/mutation/human/spastic/on_losing()
+ if(..())
+ return
+ if (owner)
+ owner.remove_status_effect(STATUS_EFFECT_SPASMS)
diff --git a/code/datums/mutations/chameleon.dm b/code/datums/mutations/chameleon.dm
index da10510970..7682351ebf 100644
--- a/code/datums/mutations/chameleon.dm
+++ b/code/datums/mutations/chameleon.dm
@@ -1,12 +1,13 @@
//Chameleon causes the owner to slowly become transparent when not moving.
/datum/mutation/human/chameleon
name = "Chameleon"
+ desc = "A genome that causes the holder's skin to become transparent over time."
quality = POSITIVE
- get_chance = 20
- lowest_value = 256 * 12
+ difficulty = 16
text_gain_indication = "You feel one with your surroundings."
text_lose_indication = "You feel oddly exposed."
time_coeff = 5
+ instability = 25
/datum/mutation/human/chameleon/on_acquiring(mob/living/carbon/human/owner)
if(..())
diff --git a/code/datums/mutations/cold.dm b/code/datums/mutations/cold.dm
new file mode 100644
index 0000000000..55dc309eae
--- /dev/null
+++ b/code/datums/mutations/cold.dm
@@ -0,0 +1,43 @@
+/datum/mutation/human/geladikinesis
+ name = "Geladikinesis"
+ desc = "Allows the user to concentrate moisture and sub-zero forces into snow."
+ quality = POSITIVE
+ text_gain_indication = "Your hand feels cold."
+ instability = 10
+ difficulty = 10
+ synchronizer_coeff = 1
+ power = /obj/effect/proc_holder/spell/targeted/conjure_item/snow
+
+/obj/effect/proc_holder/spell/targeted/conjure_item/snow
+ name = "Create Snow"
+ desc = "Concentrates cryokinetic forces to create snow, useful for snow-like construction."
+ item_type = /obj/item/stack/sheet/mineral/snow
+ charge_max = 50
+ action_icon_state = "snow"
+ delete_old = FALSE
+
+
+/datum/mutation/human/cryokinesis
+ name = "Cryokinesis"
+ desc = "Draws negative energy from the sub-zero void to freeze surrounding temperatures at subject's will."
+ quality = POSITIVE //upsides and downsides
+ text_gain_indication = "Your hand feels cold."
+ instability = 20
+ difficulty = 12
+ synchronizer_coeff = 1
+ power = /obj/effect/proc_holder/spell/aimed/cryo
+
+/obj/effect/proc_holder/spell/aimed/cryo
+ name = "Cryobeam"
+ desc = "This power fires a frozen bolt at a target."
+ charge_max = 150
+ cooldown_min = 150
+ clothes_req = FALSE
+ range = 3
+ projectile_type = /obj/item/projectile/temp/cryo
+ base_icon_state = "icebeam"
+ action_icon_state = "icebeam"
+ active_msg = "You focus your cryokinesis!"
+ deactive_msg = "You relax."
+ active = FALSE
+
diff --git a/code/datums/mutations/cold_resistance.dm b/code/datums/mutations/cold_resistance.dm
deleted file mode 100644
index 3d8fbababa..0000000000
--- a/code/datums/mutations/cold_resistance.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
-/datum/mutation/human/cold_resistance
- name = "Cold Resistance"
- quality = POSITIVE
- get_chance = 25
- lowest_value = 256 * 12
- text_gain_indication = "Your body feels warm!"
- time_coeff = 5
-
-/datum/mutation/human/cold_resistance/New()
- ..()
- visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER)
-
-/datum/mutation/human/cold_resistance/get_visual_indicator(mob/living/carbon/human/owner)
- return visual_indicators[1]
-
-/datum/mutation/human/cold_resistance/on_acquiring(mob/living/carbon/human/owner)
- if(..())
- return
- ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
-// ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance") CITADEL CHANGE
-
-/datum/mutation/human/cold_resistance/on_losing(mob/living/carbon/human/owner)
- if(..())
- return
- REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
-// REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance") CITADEL CHANGE
-
-/datum/mutation/human/cold_resistance/on_life(mob/living/carbon/human/owner)
- if(owner.getFireLoss())
- if(prob(1))
- owner.heal_bodypart_damage(0,1) //Is this really needed?
diff --git a/code/datums/mutations/combined.dm b/code/datums/mutations/combined.dm
new file mode 100644
index 0000000000..5fc9df7550
--- /dev/null
+++ b/code/datums/mutations/combined.dm
@@ -0,0 +1,32 @@
+/datum/generecipe
+ var/required = "" //it hurts so bad but initial is not compatible with lists
+ var/result = null
+
+/proc/get_mixed_mutation(mutation1, mutation2)
+ if(!mutation1 || !mutation2)
+ return FALSE
+ if(mutation1 == mutation2) //this could otherwise be bad
+ return FALSE
+ for(var/A in GLOB.mutation_recipes)
+ if(findtext(A, "[mutation1]") && findtext(A, "[mutation2]"))
+ return GLOB.mutation_recipes[A]
+
+/datum/generecipe/x_ray
+ required = "/datum/mutation/human/thermal; /datum/mutation/human/radioactive"
+ result = /datum/mutation/human/thermal/x_ray
+
+/datum/generecipe/shock
+ required = "/datum/mutation/human/insulated; /datum/mutation/human/radioactive"
+ result = SHOCKTOUCH
+
+/datum/generecipe/mindread
+ required = "/datum/mutation/human/antenna; /datum/mutation/human/paranoia"
+ result = MINDREAD
+
+/datum/generecipe/antiglow
+ required = "/datum/mutation/human/glow; /datum/mutation/human/void"
+ result = ANTIGLOWY
+
+/datum/generecipe/tonguechem
+ required = "/datum/mutation/human/tongue_spike; /datum/mutation/human/stimmed"
+ result = TONGUESPIKECHEM
\ No newline at end of file
diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm
index c536196e95..eb6c9665f3 100644
--- a/code/datums/mutations/hulk.dm
+++ b/code/datums/mutations/hulk.dm
@@ -1,12 +1,13 @@
//Hulk turns your skin green, and allows you to punch through walls.
/datum/mutation/human/hulk
name = "Hulk"
+ desc = "A poorly understood genome that causes the holder's muscles to expand, inhibit speech and gives the person a bad skin condition."
quality = POSITIVE
- get_chance = 15
- lowest_value = 256 * 12
+ locked = TRUE
+ difficulty = 16
text_gain_indication = "Your muscles hurt!"
- species_allowed = list("fly") //no skeleton/lizard hulk
health_req = 25
+ instability = 40
/datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -18,7 +19,7 @@
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "hulk", /datum/mood_event/hulk)
RegisterSignal(owner, COMSIG_MOB_SAY, .proc/handle_speech)
-/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
+/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner,atom/target, proximity)
if(proximity) //no telekinetic hulk attack
return target.attack_hulk(owner)
@@ -32,7 +33,7 @@
return
REMOVE_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_HULK)
REMOVE_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_HULK)
- ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK)
+ REMOVE_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK)
owner.update_body_parts()
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "hulk")
UnregisterSignal(owner, COMSIG_MOB_SAY)
diff --git a/code/datums/mutations/radioactive.dm b/code/datums/mutations/radioactive.dm
new file mode 100644
index 0000000000..f76e6885d0
--- /dev/null
+++ b/code/datums/mutations/radioactive.dm
@@ -0,0 +1,19 @@
+/datum/mutation/human/radioactive
+ name = "Radioactivity"
+ desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings."
+ quality = NEGATIVE
+ text_gain_indication = "You can feel it in your bones!"
+ time_coeff = 5
+ instability = 5
+ difficulty = 8
+
+/datum/mutation/human/radioactive/on_life(mob/living/carbon/human/owner)
+ radiation_pulse(owner, 20)
+
+/datum/mutation/human/radioactive/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
+ ..()
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "radiation", -MUTATIONS_LAYER))
+
+/datum/mutation/human/radioactive/get_visual_indicator()
+ return visual_indicators[type][1]
\ No newline at end of file
diff --git a/code/datums/mutations/sight.dm b/code/datums/mutations/sight.dm
index 1f23a92906..c8bc1a1597 100644
--- a/code/datums/mutations/sight.dm
+++ b/code/datums/mutations/sight.dm
@@ -1,6 +1,7 @@
//Nearsightedness restricts your vision by several tiles.
/datum/mutation/human/nearsight
name = "Near Sightness"
+ desc = "The holder of this mutation has poor eyesight."
quality = MINOR_NEGATIVE
text_gain_indication = "You can't see very well."
@@ -18,6 +19,7 @@
//Blind makes you blind. Who knew?
/datum/mutation/human/blind
name = "Blindness"
+ desc = "Renders the subject completely blind."
quality = NEGATIVE
text_gain_indication = "You can't seem to see anything."
@@ -32,43 +34,58 @@
owner.cure_blind(GENETIC_MUTATION)
-//X-ray Vision lets you see through walls.
-/datum/mutation/human/x_ray
- name = "X Ray Vision"
+///Thermal Vision lets you see mobs through walls
+/datum/mutation/human/thermal
+ name = "Thermal Vision"
+ desc = "The user of this genome can visually percieve the unique human thermal signature."
quality = POSITIVE
- get_chance = 25
- lowest_value = 256 * 12
- text_gain_indication = "The walls suddenly disappear!"
+ difficulty = 18
+ text_gain_indication = "You can see the heat rising off of your skin..."
time_coeff = 2
+ instability = 25
+ var/visionflag = TRAIT_THERMAL_VISION
-/datum/mutation/human/x_ray/on_acquiring(mob/living/carbon/human/owner)
+/datum/mutation/human/thermal/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
+ ADD_TRAIT(owner, visionflag, GENETIC_MUTATION)
owner.update_sight()
-/datum/mutation/human/x_ray/on_losing(mob/living/carbon/human/owner)
+/datum/mutation/human/thermal/on_losing(mob/living/carbon/human/owner)
if(..())
return
+ REMOVE_TRAIT(owner, visionflag, GENETIC_MUTATION)
owner.update_sight()
+///X-ray Vision lets you see through walls.
+/datum/mutation/human/thermal/x_ray
+ name = "X Ray Vision"
+ desc = "A strange genome that allows the user to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, wich might be fun for later //hmb
+ text_gain_indication = "The walls suddenly disappear!"
+ instability = 35
+ locked = TRUE
+ visionflag = TRAIT_XRAY_VISION
+
//Laser Eyes lets you shoot lasers from your eyes!
/datum/mutation/human/laser_eyes
name = "Laser Eyes"
+ desc = "Reflects concentrated light back from the eyes."
quality = POSITIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
+ difficulty = 16
text_gain_indication = "You feel pressure building up behind your eyes."
layer_used = FRONT_MUTATIONS_LAYER
limb_req = BODY_ZONE_HEAD
-/datum/mutation/human/laser_eyes/New()
+/datum/mutation/human/laser_eyes/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "lasereyes", -FRONT_MUTATIONS_LAYER)
-/datum/mutation/human/laser_eyes/get_visual_indicator(mob/living/carbon/human/owner)
+/datum/mutation/human/laser_eyes/get_visual_indicator()
return visual_indicators[1]
-/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target, mouseparams)
+/datum/mutation/human/laser_eyes/on_ranged_attack(atom/target, mouseparams)
if(owner.a_intent == INTENT_HARM)
owner.LaserEyes(target, mouseparams)
diff --git a/code/datums/mutations/space_adaptation.dm b/code/datums/mutations/space_adaptation.dm
new file mode 100644
index 0000000000..90535fbf6e
--- /dev/null
+++ b/code/datums/mutations/space_adaptation.dm
@@ -0,0 +1,30 @@
+//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
+/datum/mutation/human/space_adaptation
+ name = "Space Adaptation"
+ desc = "A strange mutation that renders the host immune to the vacuum if space. Will still need an oxygen supply."
+ quality = POSITIVE
+ difficulty = 16
+ text_gain_indication = "Your body feels warm!"
+ time_coeff = 5
+ instability = 30
+
+/datum/mutation/human/space_adaptation/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
+ ..()
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER))
+
+/datum/mutation/human/space_adaptation/get_visual_indicator(mob/living/carbon/human/owner)
+ return visual_indicators[type][1]
+
+/datum/mutation/human/space_adaptation/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
+ ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
+
+/datum/mutation/human/space_adaptation/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
+ REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
+
diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm
index 82138e6e34..5716b2595b 100644
--- a/code/datums/mutations/speech.dm
+++ b/code/datums/mutations/speech.dm
@@ -3,6 +3,7 @@
/datum/mutation/human/nervousness
name = "Nervousness"
+ desc = "Causes the holder to stutter."
quality = MINOR_NEGATIVE
text_gain_indication = "You feel nervous."
@@ -13,6 +14,7 @@
/datum/mutation/human/wacky
name = "Wacky"
+ desc = "Unknown."
quality = MINOR_NEGATIVE
text_gain_indication = "You feel an off sensation in your voicebox."
text_lose_indication = "The off sensation passes."
@@ -34,6 +36,7 @@
/datum/mutation/human/mute
name = "Mute"
+ desc = "Completely inhibits the vocal section of the brain."
quality = NEGATIVE
text_gain_indication = "You feel unable to express yourself at all."
text_lose_indication = "You feel able to speak freely again."
@@ -53,8 +56,8 @@
/datum/mutation/human/smile
name = "Smile"
+ desc = "Causes the user to be in constant mania."
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "You feel so happy. Nothing can be wrong with anything. :)"
text_lose_indication = "Everything is terrible again. :("
@@ -95,7 +98,6 @@
message = replacetext(message," ugly "," beautiful ")
message = replacetext(message," douchbag "," nice guy ")
message = replacetext(message," whore "," lady ")
- message = replacetext(message," gamer "," intellectual ")
message = replacetext(message," nerd "," smarty pants ")
message = replacetext(message," moron "," fun person ")
message = replacetext(message," IT'S LOOSE "," EVERYTHING IS FINE ")
@@ -121,8 +123,8 @@
/datum/mutation/human/unintelligible
name = "Unintelligible"
+ desc = "Partially inhibits the vocal center of the brain, severely distorting speech."
quality = NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "You can't seem to form any coherent thoughts!"
text_lose_indication = "Your mind feels more clear."
@@ -141,8 +143,9 @@
/datum/mutation/human/swedish
name = "Swedish"
+ desc = "A horrible mutation originating from the distant past. Thought to be eradicated after the incident in 2037."
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
text_gain_indication = "You feel Swedish, however that works."
text_lose_indication = "The feeling of Swedishness passes."
@@ -173,8 +176,8 @@
/datum/mutation/human/chav
name = "Chav"
+ desc = "Unknown"
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "Ye feel like a reet prat like, innit?"
text_lose_indication = "You no longer feel like being rude and sassy."
@@ -220,12 +223,13 @@
/datum/mutation/human/elvis
name = "Elvis"
+ desc = "A terrifying mutation named after its 'patient-zero'."
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
text_gain_indication = "You feel pretty good, honeydoll."
text_lose_indication = "You feel a little less conversation would be great."
-/datum/mutation/human/elvis/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/elvis/on_life()
switch(pick(1,2))
if(1)
if(prob(15))
@@ -266,8 +270,9 @@
/datum/mutation/human/stoner
name = "Stoner"
+ desc = "A common mutation that severely decreases intelligence."
quality = NEGATIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
text_gain_indication = "You feel...totally chill, man!"
text_lose_indication = "You feel like you have a better sense of time."
diff --git a/code/datums/mutations/telekinesis.dm b/code/datums/mutations/telekinesis.dm
index a5b15eacb8..b03ac04027 100644
--- a/code/datums/mutations/telekinesis.dm
+++ b/code/datums/mutations/telekinesis.dm
@@ -1,18 +1,20 @@
//Telekinesis lets you interact with objects from range, and gives you a light blue halo around your head.
/datum/mutation/human/telekinesis
name = "Telekinesis"
+ desc = "A strange mutation that allows the holder to interact with objects through thought."
quality = POSITIVE
- get_chance = 20
- lowest_value = 256 * 12
+ difficulty = 18
text_gain_indication = "You feel smarter!"
limb_req = BODY_ZONE_HEAD
+ instability = 30
-/datum/mutation/human/telekinesis/New()
+/datum/mutation/human/telekinesis/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
- visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER)
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER))
/datum/mutation/human/telekinesis/get_visual_indicator(mob/living/carbon/human/owner)
- return visual_indicators[1]
+ return visual_indicators[type][1]
/datum/mutation/human/telekinesis/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
- target.attack_tk(owner)
+ target.attack_tk(owner)
\ No newline at end of file
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index 02c12a34f4..06d62dfd96 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -168,7 +168,8 @@
id = "alien-nest"
description = "Not even Necropolis is safe from alien infestation. The competition for hosts has locked the legion and aliens in an endless conflict that can only be resolved by a PKA."
suffix = "lavaland_surface_alien_nest.dmm"
- cost = 20
+ cost = 10
+ allow_duplicates = FALSE
/datum/map_template/ruin/lavaland/fountain
name = "Fountain Hall"
@@ -176,6 +177,7 @@
description = "The fountain has a warning on the side. DANGER: May have undeclared side effects that only become obvious when implemented."
suffix = "lavaland_surface_fountain_hall.dmm"
cost = 5
+ allow_duplicates = FALSE
/datum/map_template/ruin/lavaland/survivalcapsule
name = "Survival Capsule Ruins"
@@ -198,7 +200,7 @@
description = "A place of vile worship, the scrawling of blood in the middle glowing eerily. A demonic laugh echoes throughout the caverns"
suffix = "lavaland_surface_cultaltar.dmm"
allow_duplicates = FALSE
- cost = 10
+ cost = 5
/datum/map_template/ruin/lavaland/hermit
name = "Makeshift Shelter"
@@ -244,6 +246,7 @@
description = "A strange tumor which houses a powerful beast..."
suffix = "lavaland_surface_elite_tumor.dmm"
cost = 5
+ placement_weight = 3
always_place = TRUE
allow_duplicates = TRUE
@@ -255,4 +258,4 @@
suffix = "miningbase.dmm"
cost = 0
always_place = TRUE
- unpickable = TRUE
\ No newline at end of file
+ unpickable = TRUE
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index d04cf3939f..ce71bdb212 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -128,6 +128,7 @@
/datum/status_effect/electrode
id = "tased"
+ alert_type = null
var/slowdown = 1.5
var/slowdown_priority = 50 //to make sure the stronger effect overrides
var/affect_crawl = FALSE
@@ -155,7 +156,11 @@
/datum/status_effect/electrode/tick()
var/diff = world.time - last_tick
if(owner)
- owner.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
+ var/mob/living/carbon/C = owner
+ if(HAS_TRAIT(C, TRAIT_TASED_RESISTANCE))
+ qdel(src)
+ else
+ C.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
last_tick = world.time
/datum/status_effect/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
@@ -172,6 +177,8 @@
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
+ if(HAS_TRAIT(C, TRAIT_TASED_RESISTANCE))
+ return
if(C.combatmode)
C.toggle_combat_mode(TRUE)
@@ -794,3 +801,26 @@ datum/status_effect/pacify
to_chat(owner, "Your arm spasms!")
owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK)
owner.throw_item(pick(targets))
+
+/datum/status_effect/dna_melt
+ id = "dna_melt"
+ duration = 600
+ status_type = STATUS_EFFECT_REPLACE
+ alert_type = /obj/screen/alert/status_effect/dna_melt
+ var/kill_either_way = FALSE //no amount of removing mutations is gonna save you now
+
+/datum/status_effect/dna_melt/on_creation(mob/living/new_owner, set_duration, updating_canmove)
+ . = ..()
+ to_chat(new_owner, "My body can't handle the mutations! I need to get my mutations removed fast!")
+
+/datum/status_effect/dna_melt/on_remove()
+ if(!ishuman(owner))
+ owner.gib() //fuck you in particular
+ return
+ var/mob/living/carbon/human/H = owner
+ H.something_horrible(kill_either_way)
+
+/obj/screen/alert/status_effect/dna_melt
+ name = "Genetic Breakdown"
+ desc = "I don't feel so good. Your body can't handle the mutations! You have one minute to remove your mutations, or you will be met with a horrible fate."
+ icon_state = "dna_melt"
\ No newline at end of file
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 00dc0d98d6..ac56d6f17c 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -63,6 +63,9 @@
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with
+ /// Color on minimaps, if it's null (which is default) it makes one at random.
+ var/minimap_color
+
/**
* These two vars allow for multiple unique areas to be linked to a master area
* and share some functionalities such as APC powernet nodes, fire alarms etc, without sacrificing
@@ -96,7 +99,14 @@ GLOBAL_LIST_EMPTY(teleportlocs)
// ===
/area/New()
- // This interacts with the map loader, so it needs to be set immediately
+ if(!minimap_color) // goes in New() because otherwise it doesn't fucking work
+ // generate one using the icon_state
+ if(icon_state && icon_state != "unknown")
+ var/icon/I = new(icon, icon_state, dir)
+ I.Scale(1,1)
+ minimap_color = I.GetPixel(1,1)
+ else // no icon state? use random.
+ minimap_color = rgb(rand(50,70),rand(50,70),rand(50,70)) // This interacts with the map loader, so it needs to be set immediately
// rather than waiting for atoms to initialize.
if (unique)
GLOB.areas_by_type[type] = src
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm
index 23e94c4065..1ee226875b 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm
@@ -2,6 +2,7 @@
ruletype = "Event"
var/typepath // typepath of the event
var/triggering
+ var/earliest_start = 20 MINUTES
/datum/dynamic_ruleset/event/get_blackbox_info()
var/list/ruleset_data = list()
@@ -25,8 +26,10 @@
return E
-/datum/dynamic_ruleset/event/ready(forced = FALSE) // same as midround cause we're still using enemy system
+/datum/dynamic_ruleset/event/ready(forced = FALSE)
if (!forced)
+ if(earliest_start >= world.time-SSticker.round_start_time)
+ return FALSE
var/job_check = 0
if (enemy_roles.len > 0)
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
@@ -56,6 +59,7 @@
required_enemies = list(2,2,1,1,0,0,0,0,0,0)
weight = 5
cost = 10
+ earliest_start = 30 MINUTES
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
requirements = list(70,60,50,50,40,40,40,30,20,15)
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
@@ -157,17 +161,18 @@
required_enemies = list(3,3,3,3,3,3,3,3,3,3)
cost = 15
weight = 3
+ earliest_start = 25 MINUTES
repeatable_weight_decrease = 2
requirements = list(60,50,40,30,30,30,30,30,30,30)
high_population_requirement = 30
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/meteor_wave/ready()
- if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
+ if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30))
name = "Meteor Wave: Threatening"
cost = 25
typepath = /datum/round_event/meteor_wave/threatening
- else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30))
+ else if(world.time-SSticker.round_start_time > 45 MINUTES && mode.threat_level > 50 && mode.threat >= 40 && prob(30))
name = "Meteor Wave: Catastrophic"
cost = 40
typepath = /datum/round_event/meteor_wave/catastrophic
@@ -280,6 +285,7 @@
cost = 4
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
+ earliest_start = 10 MINUTES
repeatable = TRUE
property_weights = list("extended" = 1)
@@ -324,6 +330,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
+ earliest_start = 0 MINUTES
property_weights = list("extended" = 1)
always_max_weight = TRUE
@@ -381,3 +388,67 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1,"chaos" = 1)
+
+/datum/dynamic_ruleset/event/portal_storm_syndicate
+ name = "Portal Storm"
+ config_tag = "portal_storm"
+ typepath = /datum/round_event/portal_storm/syndicate_shocktroop
+ cost = 10
+ weight = 1
+ enemy_roles = list("Head of Security","Security Officer","AI","Captain","Shaft Miner")
+ required_enemies = list(2,2,2,2,2,2,2,2,2,2)
+ requirements = list(101,101,101,30,30,30,30,30,30,30)
+ high_population_requirement = 30
+ earliest_start = 30 MINUTES
+ property_weights = list("teamwork" = 1,"chaos" = 1, "extended" = -1)
+
+/datum/dynamic_ruleset/event/wormholes
+ name = "Wormholes"
+ config_tag = "wormhole"
+ typepath = /datum/round_event/wormholes
+ cost = 3
+ weight = 4
+ enemy_roles = list("AI","Medical Doctor","Station Engineer","Head of Personnel","Captain")
+ required_enemies = list(2,2,2,2,2,2,2,2,2,2)
+ requirements = list(5,5,5,5,5,5,5,5,5,5)
+ high_population_requirement = 5
+ property_weights = list("extended" = 1)
+
+/datum/dynamic_ruleset/event/swarmers
+ name = "Swarmers"
+ config_tag = "swarmer"
+ typepath = /datum/round_event/spawn_swarmer
+ cost = 10
+ weight = 1
+ earliest_start = 30 MINUTES
+ enemy_roles = list("AI","Security Officer","Head of Security","Captain","Station Engineer","Atmos Technician","Chief Engineer")
+ required_enemies = list(4,4,4,4,3,3,2,2,1,1)
+ requirements = list(101,101,101,101,101,101,101,101,101,101)
+ high_population_requirement = 5
+ property_weights = list("extended" = -2)
+
+/datum/dynamic_ruleset/event/sentient_disease
+ name = "Sentient Disease"
+ config_tag = "sentient_disease"
+ typepath = /datum/round_event/ghost_role/sentient_disease
+ enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
+ required_enemies = list(2,1,1,1,0,0,0,0,0,0)
+ required_candidates = 1
+ weight = 4
+ cost = 5
+ requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
+ property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
+ high_population_requirement = 5
+
+/datum/dynamic_ruleset/event/revenant
+ name = "Revenant"
+ config_tag = "revenant"
+ typepath = /datum/round_event/ghost_role/revenant
+ enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
+ required_enemies = list(2,1,1,1,0,0,0,0,0,0)
+ required_candidates = 1
+ weight = 4
+ cost = 5
+ requirements = list(30,30,30,30,20,15,15,15,15,15)
+ high_population_requirement = 15
+ property_weights = list("story_potential" = -2, "extended" = -1)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 6d1933c0fd..36b81afc26 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -273,10 +273,14 @@
var/ion_announce = 33
var/removeDontImproveChance = 10
+/datum/dynamic_ruleset/midround/malf/ready()
+ if(!candidates || !candidates.len)
+ return FALSE
+ return ..()
+
/datum/dynamic_ruleset/midround/malf/trim_candidates()
..()
- living_players = candidates[CURRENT_LIVING_PLAYERS]
- for(var/mob/living/player in candidates)
+ for(var/mob/living/player in living_players)
if(!isAI(player))
candidates -= player
continue
@@ -287,8 +291,6 @@
candidates -= player
/datum/dynamic_ruleset/midround/malf/execute()
- if(!candidates || !candidates.len)
- return FALSE
var/mob/living/silicon/ai/M = pick_n_take(candidates)
assigned += M.mind
var/datum/antagonist/traitor/AI = new
@@ -337,6 +339,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard/finish_setup(mob/new_character, index)
..()
new_character.forceMove(pick(GLOB.wizardstart))
+ wizard = new_character.mind
/datum/dynamic_ruleset/midround/from_ghosts/wizard/rule_process() // i can literally copy this from are_special_antags_dead it's great
if(isliving(wizard.current) && wizard.current.stat!=DEAD)
@@ -447,9 +450,7 @@
property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
var/list/vents = list()
-/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
- // 50% chance of being incremented by one
- required_candidates += prob(50)
+/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready()
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in GLOB.machines)
if(QDELETED(temp_vent))
continue
@@ -463,6 +464,12 @@
vents += temp_vent
if(!vents.len)
return FALSE
+ return ..()
+
+
+/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
+ // 50% chance of being incremented by one
+ required_candidates += prob(50)
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant)
@@ -497,7 +504,7 @@
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 1)
var/list/spawn_locs = list()
-/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
+/datum/dynamic_ruleset/midround/from_ghosts/nightmare/ready()
for(var/X in GLOB.xeno_spawn)
var/turf/T = X
var/light_amount = T.get_lumcount()
@@ -505,7 +512,7 @@
spawn_locs += T
if(!spawn_locs.len)
return FALSE
- . = ..()
+ return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/generate_ruleset_body(mob/applicant)
var/datum/mind/player_mind = new /datum/mind(applicant.key)
@@ -523,88 +530,6 @@
log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.")
return S
-//////////////////////////////////////////////
-// //
-// SENTIENT DISEASE //
-// //
-//////////////////////////////////////////////
-
-
-/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
- name = "Sentient Disease"
- config_tag = "sentient_disease"
- antag_flag = ROLE_ALIEN
- enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
- required_enemies = list(2,1,1,1,0,0,0,0,0,0)
- required_candidates = 1
- weight = 4
- cost = 5
- requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
- property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
- high_population_requirement = 5
-
-/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant)
- var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
- applicant.transfer_ckey(virus, FALSE)
- INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
- message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by the midround ruleset.")
- log_game("[key_name(virus)] was spawned as a sentient disease by the midround ruleset.")
- return virus
-
-//////////////////////////////////////////////
-// //
-// REVENANT //
-// //
-//////////////////////////////////////////////
-
-
-/datum/dynamic_ruleset/midround/from_ghosts/revenant
- name = "Revenant"
- config_tag = "revenant"
- antag_flag = ROLE_REVENANT
- enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
- required_enemies = list(2,1,1,1,0,0,0,0,0,0)
- required_candidates = 1
- weight = 4
- cost = 5
- requirements = list(30,30,30,30,20,15,15,15,15,15)
- high_population_requirement = 15
- property_weights = list("story_potential" = -2, "extended" = -1)
- var/list/spawn_locs = list()
-
-/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0)
- var/deadMobs = 0
- for(var/mob/M in GLOB.dead_mob_list)
- deadMobs++
- if(deadMobs < REVENANT_SPAWN_THRESHOLD)
- return FALSE
- return ..()
-
-/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
- for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
- var/turf/T = get_turf(L)
- if(T && is_station_level(T.z))
- spawn_locs += T
- if(!spawn_locs.len || spawn_locs.len < 15) //look for any morgue trays, crematoriums, ect if there weren't alot of dead bodies on the station to pick from
- for(var/obj/structure/bodycontainer/bc in GLOB.bodycontainers)
- var/turf/T = get_turf(bc)
- if(T && is_station_level(T.z))
- spawn_locs += T
- if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns
- for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
- if(isturf(L.loc))
- spawn_locs += L.loc
- if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
- return FALSE
- return ..()
-
-/datum/dynamic_ruleset/midround/from_ghosts/revenant/generate_ruleset_body(mob/applicant)
- var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs))
- applicant.transfer_ckey(revvie, FALSE)
- message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by the midround ruleset.")
- log_game("[key_name(revvie)] was spawned as a revenant by the midround ruleset.")
- return revvie
-
//////////////////////////////////////////////
// //
// SLAUGHTER DEMON //
@@ -668,11 +593,11 @@
required_candidates = 2
weight = 8
cost = 10
- requirements = list(101,101,70,50,40,30,30,20,15,15)
+ requirements = list(101,101,70,50,40,30,30,30,30,30)
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
high_population_requirement = 15
var/datum/team/abductor_team/team
- property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
+ property_weights = list("extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
repeatable_weight_decrease = 4
repeatable = TRUE
@@ -742,7 +667,8 @@
var/datum/antagonist/ninja/ninjadatum = new
ninjadatum.helping_station = pick(TRUE,FALSE)
if(ninjadatum.helping_station)
- mode.refund_threat(5)
+ mode.refund_threat(cost+5)
+ mode.log_threat("Ninja was helping station; [cost+5] cost refunded.")
Mind.add_antag_datum(ninjadatum)
if(Ninja.mind != Mind) //something has gone wrong!
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index abd40a1756..2527c83077 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -623,8 +623,7 @@
config_tag = "clownops"
antag_datum = /datum/antagonist/nukeop/clownop
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
- requirements = list(101,101,101,101,101,101,101,101,101,101)
- high_population_requirement = 101
+ weight = 1
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2)
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index 069c73a421..d25416a4d1 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -1,16 +1,22 @@
/datum/dynamic_storyteller
- var/name = "none"
- var/config_tag = null
- var/desc = "A coder's idiocy."
- var/list/property_weights = list()
- var/curve_centre = 0
- var/curve_width = 1.8
+ var/name = "none" // Name for voting.
+ var/config_tag = null // Config tag for config weights.
+ var/desc = "A coder's idiocy." // Description for voting.
+ var/list/property_weights = list() // See below.
+ var/curve_centre = 0 // As GLOB.dynamic_curve_centre.
+ var/curve_width = 1.8 // As GLOB.dynamic_curve_width.
var/forced_threat_level = -1
+ /*
+ NO_ASSASSIN: Will not have permanent assassination targets.
+ WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level.
+ USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre.
+ */
var/flags = 0
- var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
- var/event_frequency_lower = 6 MINUTES
- var/event_frequency_upper = 20 MINUTES
- var/datum/game_mode/dynamic/mode = null
+ var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
+ var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
+ var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
+ var/pop_antag_ratio = 5 // How many non-antags there should be vs antags.
+ var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
Property weights are:
@@ -80,7 +86,7 @@ Property weights are:
var/chance = 0
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
- var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
+ var/max_pop_per_antag = max(pop_antag_ratio,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
chance += 80 // No antags at all? let's boost those odds!
else
@@ -173,12 +179,13 @@ Property weights are:
name = "Chaotic"
config_tag = "chaotic"
curve_centre = 10
- desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
+ desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round."
property_weights = list("extended" = -1, "chaos" = 10)
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
flags = WAROPS_ALWAYS_ALLOWED
+ pop_antag_ratio = 4
var/refund_cooldown = 0
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
@@ -189,14 +196,14 @@ Property weights are:
/datum/dynamic_storyteller/cowabunga/do_process()
if(refund_cooldown < world.time)
- mode.refund_threat(20)
- mode.log_threat("Cowabunga it is. Refunded 20 threat. Threat is now [mode.threat].")
- refund_cooldown = world.time + 600 SECONDS
+ mode.refund_threat(40)
+ mode.log_threat("Chaotic storyteller refunded 40 threat. Threat is now [mode.threat].")
+ refund_cooldown = world.time + 1200 SECONDS
/datum/dynamic_storyteller/team
name = "Teamwork"
config_tag = "teamwork"
- desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
+ desc = "Modes where the crew must band together. Nukies, xenos, blob. Only one antag threat at once."
curve_centre = 2
curve_width = 1.5
weight = 2
@@ -209,56 +216,71 @@ Property weights are:
/datum/dynamic_storyteller/conversion
name = "Conversion"
config_tag = "conversion"
- desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
+ desc = "Conversion antags. Cults, revs."
curve_centre = 3
curve_width = 1
- weight = 2
+ weight = 0
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
/datum/dynamic_storyteller/classic
name = "Random"
config_tag = "random"
- desc = "Chaos: varies. Variation: highest. No special weights attached."
- weight = 6
- flags = USE_PREF_WEIGHTS
+ desc = "No special weights attached. Anything goes."
+ weight = 4
curve_width = 4
+ pop_antag_ratio = 7
+ flags = USE_PREF_WEIGHTS
-/datum/dynamic_storyteller/memes
+/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
- desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
- weight = 4
- flags = USE_PREF_WEIGHTS
- curve_width = 4
+ desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
+ weight = 2
+ curve_width = 2
+ pop_antag_ratio = 7
property_weights = list("story_potential" = 10)
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
config_tag = "intrigue"
- desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
- weight = 4
- flags = USE_PREF_WEIGHTS
- curve_width = 4
+ desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
+ weight = 2
+ curve_width = 2
+ pop_antag_ratio = 7
property_weights = list("trust" = -5)
/datum/dynamic_storyteller/liteextended
name = "Calm"
config_tag = "calm"
- desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
+ desc = "Low-chaos round. Few antags. No conversion."
+ curve_centre = -3
+ curve_width = 0.5
+ flags = NO_ASSASSIN
+ weight = 1
+ pop_antag_ratio = 10
+ property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
+
+/datum/dynamic_storyteller/no_antag
+ name = "Extended"
+ config_tag = "semiextended"
+ desc = "No standard antags. Threatening events may still spawn."
curve_centre = -5
curve_width = 0.5
flags = NO_ASSASSIN
- weight = 2
- property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
+ weight = 1
+ property_weights = list("extended" = 2)
-/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE)
- return ..()/2
+/datum/dynamic_storyteller/no_antag/roundstart_draft()
+ return list()
+
+/datum/dynamic_storyteller/no_antag/get_injection_chance(dry_run)
+ return 0
/datum/dynamic_storyteller/extended
- name = "Extended"
+ name = "Super Extended"
config_tag = "extended"
- desc = "Chaos: none. Variation: none. Likely antags: none."
+ desc = "No antags. No dangerous events."
curve_centre = -20
weight = 0
curve_width = 0.5
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index b74e4303e5..6fa7fa30c0 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -47,8 +47,8 @@
)
/obj/machinery/autolathe/Initialize()
- AddComponent(/datum/component/material_container,
- list(/datum/material/iron,
+ var/static/list/allowed_types = list(
+ /datum/material/iron,
/datum/material/glass,
/datum/material/gold,
/datum/material/silver,
@@ -62,10 +62,9 @@
/datum/material/plastic,
/datum/material/adamantine,
/datum/material/mythril
- ),
- 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
+ )
+ AddComponent(/datum/component/material_container, allowed_types, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
-
wires = new /datum/wires/autolathe(src)
stored_research = new /datum/techweb/specialized/autounlocking/autolathe
matching_designs = list()
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 426e68c17b..eac94081cb 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -77,6 +77,8 @@
name = "cloning data disk"
icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk.
var/list/fields = list()
+ var/list/mutations = list()
+ var/max_mutations = 6
var/read_only = 0 //Well,it's still a floppy disk
//Disk stuff.
@@ -129,7 +131,7 @@
return examine(user)
//Start growing a human clone in the pod!
-/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions, list/quirks)
+/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, mutation_index, mindref, datum/species/mrace, list/features, factions, list/quirks)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -165,8 +167,9 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
- H.hardset_dna(ui, se, H.real_name, null, mrace, features)
- H.randmutb() //100% bad mutation. Can be cured with mutadone.
+ H.hardset_dna(ui, mutation_index, H.real_name, null, mrace, features)
+
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) //100% bad mutation. Can be cured with mutadone.
H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment.
occupant = H
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 4d21ae77b0..07cc252b87 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -485,7 +485,7 @@
R.fields["id"] = copytext_char(md5(mob_occupant.real_name), 2, 6)
R.fields["UE"] = dna.unique_enzymes
R.fields["UI"] = dna.uni_identity
- R.fields["SE"] = dna.struc_enzymes
+ R.fields["SE"] = dna.mutation_index
R.fields["blood_type"] = dna.blood_type
R.fields["features"] = dna.features
R.fields["factions"] = mob_occupant.faction
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 5fe141d2cd..bd81b79694 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -15,6 +15,7 @@
var/message_cooldown = 0
var/ai_message_cooldown = 0
var/tmp_alertlevel = 0
+ var/static/security_level_cd // used to stop mass spam.
var/const/STATE_DEFAULT = 1
var/const/STATE_CALLSHUTTLE = 2
var/const/STATE_CANCELSHUTTLE = 3
@@ -94,16 +95,18 @@
I = pda.id
if (I && istype(I))
if(ACCESS_CAPTAIN in I.access)
+ if(security_level_cd > world.time)
+ to_chat(usr, "Security level protocols are currently on cooldown. Please stand by.")
+ return
var/old_level = GLOB.security_level
if(!tmp_alertlevel)
tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel < SEC_LEVEL_GREEN)
tmp_alertlevel = SEC_LEVEL_GREEN
- if(tmp_alertlevel == SEC_LEVEL_BLUE)
- tmp_alertlevel = SEC_LEVEL_BLUE
if(tmp_alertlevel > SEC_LEVEL_AMBER)
tmp_alertlevel = SEC_LEVEL_AMBER //Cannot engage delta with this
set_security_level(tmp_alertlevel)
+ security_level_cd = world.time + 15 SECONDS
if(GLOB.security_level != old_level)
to_chat(usr, "Authorization confirmed. Modifying security level.")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
@@ -376,19 +379,21 @@
if("ai-announce")
make_announcement(usr, 1)
if("ai-securitylevel")
+ if(security_level_cd > world.time)
+ to_chat(usr, "Security level protocols are currently on cooldown. Please stand by.")
+ return
tmp_alertlevel = text2num( href_list["newalertlevel"] )
if(!tmp_alertlevel)
- tmp_alertlevel = 0
+ tmp_alertlevel = SEC_LEVEL_GREEN
var/old_level = GLOB.security_level
if(!tmp_alertlevel)
tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel < SEC_LEVEL_GREEN)
tmp_alertlevel = SEC_LEVEL_GREEN
- if(tmp_alertlevel == SEC_LEVEL_BLUE)
- tmp_alertlevel = SEC_LEVEL_BLUE
if(tmp_alertlevel > SEC_LEVEL_AMBER)
tmp_alertlevel = SEC_LEVEL_AMBER //Cannot engage delta with this
set_security_level(tmp_alertlevel)
+ security_level_cd = world.time + 15 SECONDS
if(GLOB.security_level != old_level)
//Only notify people if an actual change happened
var/security_level = get_security_level()
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 5049a10d2b..5662737ff7 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -1,14 +1,16 @@
#define INJECTOR_TIMEOUT 100
-#define REJUVENATORS_INJECT 15
-#define REJUVENATORS_MAX 90
#define NUMBER_OF_BUFFERS 3
+#define SCRAMBLE_TIMEOUT 600
+#define JOKER_TIMEOUT 12000 //20 minutes
+#define JOKER_UPGRADE 1800
#define RADIATION_STRENGTH_MAX 15
-#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has a more range
+#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has more range
#define RADIATION_DURATION_MAX 30
#define RADIATION_ACCURACY_MULTIPLIER 3 //larger is less accurate
+
#define RADIATION_IRRADIATION_MULTIPLIER 1 //multiplier for how much radiation a test subject receives
#define SCANNER_ACTION_SE 1
@@ -23,22 +25,44 @@
icon_keyboard = "med_key"
density = TRUE
circuit = /obj/item/circuitboard/computer/scan_consolenew
- var/radduration = 2
- var/radstrength = 1
- var/list/buffer[NUMBER_OF_BUFFERS]
-
- var/injectorready = 0 //world timer cooldown var
- var/current_screen = "mainmenu"
- var/obj/machinery/dna_scannernew/connected = null
- var/obj/item/disk/data/diskette = null
- var/list/delayed_action = null
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 400
-
light_color = LIGHT_COLOR_BLUE
+ var/datum/techweb/stored_research
+ var/max_storage = 6
+ var/combine
+ var/radduration = 2
+ var/radstrength = 1
+ var/max_chromosomes = 6
+
+ ///Amount of mutations we can store
+ var/list/buffer[NUMBER_OF_BUFFERS]
+ ///mutations we have stored
+ var/list/stored_mutations = list()
+ ///chromosomes we have stored
+ var/list/stored_chromosomes = list()
+ ///combinations of injectors for the 'injector selection'. format is list("Elsa" = list(Cryokinesis, Geladikinesis), "The Hulk" = list(Hulk, Gigantism), etc) Glowy and the gang being an initialized datum
+ var/list/injector_selection = list()
+ ///max amount of selections you can make
+ var/max_injector_selections = 2
+ ///hard-cap on the advanced dna injector
+ var/max_injector_mutations = 10
+ ///the max instability of the advanced injector.
+ var/max_injector_instability = 50
+
+ var/injectorready = 0 //world timer cooldown var
+ var/jokerready = 0
+ var/scrambleready = 0
+ var/current_screen = "mainmenu"
+ var/current_mutation //what block are we inspecting? only used when screen = "info"
+ var/current_storage //what storage block are we looking at?
+ var/obj/machinery/dna_scannernew/connected = null
+ var/obj/item/disk/data/diskette = null
+ var/list/delayed_action = null
+
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if (istype(I, /obj/item/disk/data)) //INSERT SOME DISKETTES
if (!src.diskette)
@@ -48,6 +72,28 @@
to_chat(user, "You insert [I].")
src.updateUsrDialog()
return
+ if (istype(I, /obj/item/chromosome))
+ if(LAZYLEN(stored_chromosomes) < max_chromosomes)
+ I.forceMove(src)
+ stored_chromosomes += I
+ to_chat(user, "You insert [I].")
+ else
+ to_chat(user, "You cannot store any more chromosomes!")
+ return
+ if(istype(I, /obj/item/dnainjector/activator))
+ var/obj/item/dnainjector/activator/A = I
+ if(A.used)
+ to_chat(user,"Recycled [I].")
+ if(A.research)
+ var/c_typepath = generate_chromosome()
+ var/obj/item/chromosome/CM = new c_typepath (drop_location())
+ to_chat(user,"Recycled [I].")
+ if((LAZYLEN(stored_chromosomes) < max_chromosomes) && prob(60))
+ CM.forceMove(src)
+ stored_chromosomes += CM
+ to_chat(user,"[capitalize(CM.name)] added to storage.")
+ qdel(I)
+ return
else
return ..()
@@ -58,12 +104,26 @@
if(!isnull(connected))
break
injectorready = world.time + INJECTOR_TIMEOUT
+ scrambleready = world.time + SCRAMBLE_TIMEOUT
+ jokerready = world.time + JOKER_TIMEOUT
+
+ stored_research = SSresearch.science_tech
+
+/obj/machinery/computer/scan_consolenew/examine(mob/user)
+ . = ..()
+ if(jokerready < world.time)
+ . += "JOKER algorithm available."
+ else
+ . += "JOKER algorithm available in about [round(0.00166666667 * (jokerready - world.time))] minutes."
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, last_change)
. = ..()
if(!user)
return
var/datum/browser/popup = new(user, "scannernew", "DNA Modifier Console", 800, 630) // Set up the popup browser window
+ if(user.client)
+ var/datum/asset/simple/assets = get_asset_datum(/datum/asset/simple/genetics)
+ assets.send(user.client)
if(!(in_range(src, user) || hasSiliconAccessInArea(user)))
popup.close()
return
@@ -88,8 +148,6 @@
occupant_status += ""
occupant_status += "Health:
[viable_occupant.health] %
"
occupant_status += "Radiation Level:
[viable_occupant.radiation/(RAD_MOB_SAFE/100)] %
"
- var/rejuvenators = viable_occupant.reagents.get_reagent_amount(/datum/reagent/medicine/potass_iodide)
- occupant_status += "Rejuvenators:
[rejuvenators] units
"
occupant_status += "Unique Enzymes :
[viable_occupant.dna.unique_enzymes]
"
occupant_status += "Last Operation:
[last_change ? last_change : "----"]
"
else
@@ -147,28 +205,61 @@
buttons += "[connected.locked ? "Unlock" : "Lock"] Scanner"
else
buttons += "Open Scanner Lock Scanner"
- if(viable_occupant)
- buttons += "Inject Rejuvenators"
+ if(viable_occupant && (scrambleready < world.time))
+ buttons += "Scramble DNA"
else
- buttons += "Inject Rejuvenators"
+ buttons += "Scramble DNA"
if(diskette)
- buttons += "Eject Disk"
+ buttons += "Disk"
else
- buttons += "Eject Disk"
- if(current_screen == "buffer")
- buttons += "Radiation Emitter Menu"
+ buttons += "Disk"
+ if(current_screen == "mutations")
+ buttons += "Mutations"
else
- buttons += "Buffer Menu"
-
+ buttons += "Mutations"
+ if((current_screen == "mainmenu") || !current_screen)
+ buttons += "Genetic Sequencer"
+ else
+ buttons += "Genetic Sequencer"
+ if(current_screen == "ui")
+ buttons += "Unique Identifiers"
+ else
+ buttons += "Unique Identifiers"
+ if(current_screen == "advinjector")
+ buttons += "Adv. Injectors"
+ else
+ buttons += "Adv. Injectors"
switch(current_screen)
if("working")
temp_html += status
temp_html += "System Busy
"
temp_html += "Working ... Please wait ([DisplayTimeText(radduration*10)])"
- if("buffer")
+ if("ui")
temp_html += status
temp_html += buttons
- temp_html += "Buffer Menu
"
+ temp_html += "Unique Identifiers
"
+ temp_html += "-- Output Level ++"
+ temp_html += "
-- Pulse Duration ++"
+ temp_html += "Irradiate Subject
"
+ temp_html += "Unique Identifier:
"
+ var/max_line_len = 7*DNA_BLOCK_SIZE
+ if(viable_occupant)
+ temp_html += "
1
"
+ var/char = ""
+ var/ui_text = viable_occupant.dna.uni_identity
+ var/len_byte = length(ui_text)
+ var/char_it = 0
+ for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
+ char_it++
+ char = ui_text[byte_it]
+ temp_html += "
[char]"
+ if((char_it % max_line_len) == 0)
+ temp_html += "
"
+ if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
+ temp_html += "
[(char_it / DNA_BLOCK_SIZE) + 1]
"
+ else
+ temp_html += "---------"
+ temp_html += "
Buffer Menu
"
if(istype(buffer))
for(var/i=1, i<=buffer.len, i++)
@@ -188,7 +279,6 @@
temp_html += "
Save to Disk"
else
var/ui = buffer_slot["UI"]
- var/se = buffer_slot["SE"]
var/ue = buffer_slot["UE"]
var/name = buffer_slot["name"]
var/label = buffer_slot["label"]
@@ -234,19 +324,6 @@
temp_html += "
UI+UE Injector"
else
temp_html += "
UI+UE Injector"
- if(se)
- temp_html += "
\tSE: [se] "
- if(viable_occupant)
- temp_html += "
Occupant"
- else
- temp_html += "
Occupant"
- temp_html += "
Occupant:Delayed"
- if(injectorready < world.time )
- temp_html += "
Injector"
- else
- temp_html += "
Injector"
- else
- temp_html += "
\tSE: No Data"
if(viable_occupant)
temp_html += "
Save to Buffer"
else
@@ -260,58 +337,225 @@
temp_html += "
Save to Disk"
else
temp_html += "
Save to Disk"
+ if("disk")
+ temp_html += status
+ temp_html += buttons
+ if(diskette)
+ temp_html += "
[diskette.name]
"
+ temp_html += "
Eject Disk"
+ if(LAZYLEN(diskette.mutations))
+ temp_html += "
"
+ for(var/datum/mutation/human/A in diskette.mutations)
+ temp_html += "| [A.name] | "
+ temp_html += "Delete | "
+ if(LAZYLEN(stored_mutations) < max_storage)
+ temp_html += "Import | "
+ else
+ temp_html += " | Import | "
+ temp_html += "
"
+ temp_html += "
"
+ else
+ temp_html += "
Load diskette to start ----------"
+ if("info")
+ if(LAZYLEN(stored_mutations))
+ if(LAZYLEN(stored_mutations) >= current_storage)
+ var/datum/mutation/human/HM = stored_mutations[current_storage]
+ if(HM)
+ temp_html += display_sequence(HM.type, current_storage)
+ else
+ current_screen = "mainmenu"
+ if("mutations")
+ temp_html += status
+ temp_html += buttons
+ temp_html += "
Mutation Storage:
"
+ temp_html += "
"
+ for(var/datum/mutation/human/HM in stored_mutations)
+ var/i = stored_mutations.Find(HM)
+ temp_html += "| [HM.name] | "
+ temp_html += "Export | "
+ temp_html += "Delete | "
+ if(combine == HM.type)
+ temp_html += "Combine |
"
+ else
+ temp_html += "Combine | "
+ temp_html += "
"
+ temp_html += "
Chromosome Storage:
"
+ temp_html += "
"
+ for(var/i in 1 to stored_chromosomes.len)
+ var/obj/item/chromosome/CM = stored_chromosomes[i]
+ temp_html += "| [CM.name] |
"
+ temp_html += "
"
+ if("advinjector")
+ temp_html += status
+ temp_html += buttons
+ temp_html += "
"
+ temp_html += "
"
+ for(var/A in injector_selection)
+ temp_html += "
[A]"
+ var/list/true_selection = injector_selection[A]
+ temp_html += "
"
+ for(var/B in true_selection)
+ var/datum/mutation/human/HM = B
+ var/mutcolor
+ switch(HM.quality)
+ if(POSITIVE)
+ mutcolor = "good"
+ if(MINOR_NEGATIVE)
+ mutcolor = "average"
+ if(NEGATIVE)
+ mutcolor = "bad"
+ temp_html += "
[HM.name] "
+ temp_html += "
Remove "
+ if (injectorready < world.time)
+ temp_html += "
"
else
temp_html += status
temp_html += buttons
- temp_html += "
Radiation Emitter Menu
"
-
- temp_html += "
-- Output Level ++"
- temp_html += "
-- Pulse Duration ++"
-
- temp_html += "
Irradiate Subject
"
- temp_html += "
Unique Identifier:
"
-
- var/max_line_len = 7*DNA_BLOCK_SIZE
+ temp_html += "
Genetic Sequence:
"
if(viable_occupant)
- temp_html += "
1
"
- var/char = ""
- var/ui_text = viable_occupant.dna.uni_identity
- var/len_byte = length(ui_text)
- var/char_it = 0
- for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
- char_it++
- char = ui_text[byte_it]
- temp_html += "
[char]"
- if((char_it % max_line_len) == 0)
- temp_html += "
"
- if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
- temp_html += "
[(char_it / DNA_BLOCK_SIZE) + 1]
"
+ if(viable_occupant)
+ for(var/A in get_mutation_list())
+ temp_html += display_inactive_sequence(A)
+ temp_html += "
"
+ else
+ temp_html += "----"
+ if(viable_occupant && (current_mutation in get_mutation_list(viable_occupant)))
+ temp_html += display_sequence(current_mutation)
+ temp_html += "
"
else
- temp_html += "----"
- temp_html += "
"
-
- temp_html += "
Structural Enzymes:
"
- if(viable_occupant)
- temp_html += "
1
"
- var/char = ""
- var/se_text = viable_occupant.dna.struc_enzymes
- var/len_byte = length(se_text)
- var/char_it = 0
- for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
- char_it++
- char = se_text[byte_it]
- temp_html += "
[char]"
- if((char_it % max_line_len) == 0)
- temp_html += "
"
- if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
- temp_html += "
[(char_it / DNA_BLOCK_SIZE) + 1]
"
- else
- temp_html += "----"
- temp_html += "
"
+ temp_html += "----------"
popup.set_content(temp_html.Join())
popup.open()
+/obj/machinery/computer/scan_consolenew/proc/display_inactive_sequence(mutation)
+ var/temp_html = ""
+ var/class = "unselected"
+ var/mob/living/carbon/viable_occupant = get_viable_occupant()
+ if(!viable_occupant)
+ return
+
+ var/location = viable_occupant.dna.mutation_index.Find(mutation) //We do this because we dont want people using sysexp or similair tools to just read the mutations.
+
+ if(!location) //Do this only when needed, dont make a list with mutations for every iteration if you dont need to
+ var/list/mutations = get_mutation_list(TRUE)
+ if(mutation in mutations)
+ location = mutations.Find(mutation)
+ if(mutation == current_mutation)
+ class = "selected"
+ if(location > DNA_MUTATION_BLOCKS)
+ temp_html += "

"
+ else if(mutation in stored_research.discovered_mutations)
+ temp_html += "

"
+ else
+ temp_html += "

"
+ return temp_html
+
+/obj/machinery/computer/scan_consolenew/proc/display_sequence(mutation, storage_slot) //Storage slot is for when viewing from the stored mutations
+ var/temp_html = ""
+ if(!mutation)
+ temp_html += "ERR-"
+ return
+ var/mut_name = "Unknown gene"
+ var/mut_desc = "No information available."
+ var/alias
+ var/discovered = FALSE
+ var/active = FALSE
+ var/scrambled = FALSE
+ var/instability
+ var/mob/living/carbon/viable_occupant = get_viable_occupant()
+ var/datum/mutation/human/HM = get_valid_mutation(mutation)
+
+ if(viable_occupant)
+ var/datum/mutation/human/M = viable_occupant.dna.get_mutation(mutation)
+ if(M)
+ scrambled = M.scrambled
+ active = TRUE
+ var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation)
+ alias = A.alias
+ if(active && !scrambled)
+ discover(mutation)
+ if(stored_research && (mutation in stored_research.discovered_mutations))
+ mut_name = A.name
+ mut_desc = A.desc
+ discovered = TRUE
+ instability = A.instability
+ var/extra
+ if(viable_occupant && !(storage_slot || viable_occupant.dna.mutation_in_sequence(mutation)))
+ extra = TRUE
+
+ if(discovered && !scrambled)
+ var/mutcolor
+ switch(A.quality)
+ if(POSITIVE)
+ mutcolor = "good"
+ if(MINOR_NEGATIVE)
+ mutcolor = "average"
+ if(NEGATIVE)
+ mutcolor = "bad"
+ if(HM)
+ instability *= GET_MUTATION_STABILIZER(HM)
+ temp_html += "
[mut_name] ([alias])"
+ temp_html += "
Instability : [round(instability)]
"
+ else
+ temp_html += "
[alias]"
+ temp_html += "
[mut_desc]
"
+ if(active && !storage_slot)
+ if(HM?.can_chromosome && (HM in viable_occupant.dna.mutations))
+ var/i = viable_occupant.dna.mutations.Find(HM)
+ var/chromosome_name = "
----"
+ if(HM.chromosome_name)
+ chromosome_name = HM.chromosome_name
+ temp_html += "
Chromosome status: [chromosome_name]
"
+ temp_html += "
Sequence:
"
+ if(!scrambled)
+ for(var/block in 1 to A.blocks)
+ var/whole_sequence = get_valid_gene_string(mutation)
+ var/sequence = copytext(whole_sequence, 1+(block-1)*(DNA_SEQUENCE_LENGTH*2),(DNA_SEQUENCE_LENGTH*2*block+1))
+ temp_html += "
"
+ for(var/i in 1 to DNA_SEQUENCE_LENGTH)
+ var/num = 1+(i-1)*2
+ var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
+ temp_html += " | "
+ temp_html += "
"
+ for(var/i in 1 to DNA_SEQUENCE_LENGTH)
+ temp_html += "| | "
+ temp_html += "
"
+ for(var/i in 1 to DNA_SEQUENCE_LENGTH)
+ var/num = i*2
+ var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
+ temp_html += " | "
+ temp_html += "
"
+ temp_html += "
"
+ else
+ temp_html = "
Sequence unreadable due to unpredictable mutation.
"
+ if((active || storage_slot) && (injectorready < world.time) && !scrambled)
+ temp_html += "
Print Activator"
+ temp_html += "
Print Mutator"
+ else
+ temp_html += "
Print Activator"
+ temp_html += "
Print Mutator"
+ temp_html += "
"
+ if(storage_slot)
+ temp_html += "
Delete"
+ if((LAZYLEN(stored_mutations) < max_storage) && diskette && !diskette.read_only)
+ temp_html += "
Export"
+ else
+ temp_html += "
Export"
+ temp_html += "
Back"
+ else if(active && !scrambled)
+ temp_html += "
Store"
+ temp_html += "
Adv. Injector"
+ if(extra || scrambled)
+ temp_html += "
Nullify"
+ else
+ temp_html += "
Nullify"
+ temp_html += "
"
+ return temp_html
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
if(..())
@@ -350,13 +594,15 @@
radstrength = WRAP(num, 1, RADIATION_STRENGTH_MAX+1)
if("screen")
current_screen = href_list["text"]
- if("rejuv")
- if(viable_occupant && viable_occupant.reagents)
- var/potassiodide_amount = viable_occupant.reagents.get_reagent_amount(/datum/reagent/medicine/potass_iodide)
- var/can_add = max(min(REJUVENATORS_MAX - potassiodide_amount, REJUVENATORS_INJECT), 0)
- viable_occupant.reagents.add_reagent(/datum/reagent/medicine/potass_iodide, can_add)
+ if("scramble")
+ if(viable_occupant && (scrambleready < world.time))
+ viable_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA))
+ viable_occupant.dna.generate_dna_blocks()
+ scrambleready = world.time + SCRAMBLE_TIMEOUT
+ to_chat(usr,"
DNA scrambled.")
+ viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected.damage_coeff ** 2)
if("setbufferlabel")
- var/text = sanitize(input(usr, "Input a new label:", "Input an Text", null) as text|null)
+ var/text = sanitize(input(usr, "Input a new label:", "Input a Text", null) as text|null)
if(num && text)
num = CLAMP(num, 1, NUMBER_OF_BUFFERS)
var/list/buffer_slot = buffer[num]
@@ -368,7 +614,6 @@
buffer[num] = list(
"label"="Buffer[num]:[viable_occupant.real_name]",
"UI"=viable_occupant.dna.uni_identity,
- "SE"=viable_occupant.dna.struc_enzymes,
"UE"=viable_occupant.dna.unique_enzymes,
"name"=viable_occupant.real_name,
"blood_type"=viable_occupant.dna.blood_type
@@ -382,8 +627,6 @@
if("transferbuffer")
if(num && viable_occupant)
switch(href_list["text"]) //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
- if("se")
- apply_buffer(SCANNER_ACTION_SE,num)
if("ui")
apply_buffer(SCANNER_ACTION_UI,num)
if("ue")
@@ -397,28 +640,6 @@
if(istype(buffer_slot))
var/obj/item/dnainjector/timed/I
switch(href_list["text"])
- if("se")
- if(buffer_slot["SE"])
- I = new /obj/item/dnainjector/timed(loc)
- var/powers = 0
- for(var/datum/mutation/human/HM in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
- if(HM.check_block_string(buffer_slot["SE"]))
- I.add_mutations.Add(HM)
- if(HM in GLOB.good_mutations)
- powers += 1
- if(HM in GLOB.bad_mutations + GLOB.not_good_mutations)
- powers -= 1 //To prevent just unlocking everything to get all powers to a syringe for max tech
- else
- I.remove_mutations.Add(HM)
- var/time_coeff
- for(var/datum/mutation/human/HM in I.add_mutations)
- if(!time_coeff)
- time_coeff = HM.time_coeff
- continue
- time_coeff = min(time_coeff,HM.time_coeff)
- if(connected)
- I.duration = I.duration * time_coeff * connected.damage_coeff
- I.damage_coeff = connected.damage_coeff
if("ui")
if(buffer_slot["UI"])
I = new /obj/item/dnainjector/timed(loc)
@@ -457,7 +678,7 @@
if("setdelayed")
if(num)
delayed_action = list("action"=text2num(href_list["delayaction"]),"buffer"=num)
- if("pulseui","pulsese")
+ if("pulseui")
if(num && viable_occupant && connected)
radduration = WRAP(radduration, 1, RADIATION_DURATION_MAX+1)
radstrength = WRAP(radstrength, 1, RADIATION_STRENGTH_MAX+1)
@@ -469,7 +690,7 @@
ui_interact(usr)
sleep(radduration*10)
- current_screen = "mainmenu"
+ current_screen = "ui"
if(viable_occupant && connected && connected.occupant==viable_occupant)
viable_occupant.radiation += (RADIATION_IRRADIATION_MULTIPLIER*radduration*radstrength)/(connected.damage_coeff ** 2) //Read comment in "transferbuffer" section above for explanation
@@ -478,7 +699,7 @@
var/len = length_char(viable_occupant.dna.uni_identity)
num = WRAP(num, 1, len+1)
num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2
- //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
+ //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
var/block = round((num-1)/DNA_BLOCK_SIZE)+1
var/subblock = num - block*DNA_BLOCK_SIZE
last_change = "UI #[block]-[subblock]; "
@@ -490,35 +711,229 @@
viable_occupant.dna.uni_identity = copytext_char(viable_occupant.dna.uni_identity, 1, num) + hex + copytext_char(viable_occupant.dna.uni_identity, num + 1)
viable_occupant.updateappearance(mutations_overlay_update=1)
- if("pulsese")
- var/len = length(viable_occupant.dna.struc_enzymes)
- num = WRAP(num, 1, len+1)
- num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len)
-
- var/block = round((num-1)/DNA_BLOCK_SIZE)+1
- var/subblock = num - block*DNA_BLOCK_SIZE
- last_change = "SE #[block]-[subblock]; "
-
- var/hex = copytext_char(viable_occupant.dna.struc_enzymes, num, num+1)
- last_change += "[hex]"
- hex = scramble(hex, radstrength, radduration)
- last_change += "->[hex]"
-
- viable_occupant.dna.struc_enzymes = copytext_char(viable_occupant.dna.struc_enzymes, 1, num) + hex + copytext_char(viable_occupant.dna.struc_enzymes, num + 1)
- viable_occupant.domutcheck()
else
current_screen = "mainmenu"
if(connected)
connected.locked = locked_state
+ if("inspect")
+ if(viable_occupant)
+ var/list/mutations = get_mutation_list(TRUE)
+ if(current_mutation == mutations[num])
+ current_mutation = null
+ else
+ current_mutation = mutations[num]
+
+ if("inspectstorage")
+ current_storage = num
+ current_screen = "info"
+ if("savemut")
+ if(viable_occupant)
+ var/succes
+ if(LAZYLEN(stored_mutations) < max_storage)
+ var/mutation = text2path(href_list["path"])
+ if(ispath(mutation, /datum/mutation/human)) //sanity checks
+ var/datum/mutation/human/HM = viable_occupant.dna.get_mutation(mutation)
+ if(HM)
+ var/datum/mutation/human/A = new HM.type()
+ A.copy_mutation(HM)
+ succes = TRUE
+ stored_mutations += A
+ to_chat(usr,"
Mutation succesfully stored.")
+ if(!succes) //we can exactly return here
+ to_chat(usr,"
Mutation storage is full.")
+ if("deletemut")
+ var/datum/mutation/human/HM = stored_mutations[num]
+ if(HM)
+ stored_mutations.Remove(HM)
+ qdel(HM)
+ current_screen = "mutations"
+ if("activator")
+ if(injectorready < world.time)
+ var/mutation = text2path(href_list["path"])
+ if(ispath(mutation, /datum/mutation/human))
+ var/datum/mutation/human/HM = get_valid_mutation(mutation)
+ if(HM)
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+ I.add_mutations += new HM.type (copymut = HM)
+ I.name = "[HM.name] activator"
+ I.research = TRUE
+ if(connected)
+ I.damage_coeff = connected.damage_coeff*4
+ injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected.precision_coeff) //precision_coeff being the matter bin rating
+ else
+ injectorready = world.time + INJECTOR_TIMEOUT
+ if("mutator")
+ if(injectorready < world.time)
+ var/mutation = text2path(href_list["path"])
+ if(ispath(mutation, /datum/mutation/human))
+ var/datum/mutation/human/HM = get_valid_mutation(mutation)
+ if(HM)
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+ I.add_mutations += new HM.type (copymut = HM)
+ I.doitanyway = TRUE
+ I.name = "[HM.name] injector"
+ if(connected)
+ I.damage_coeff = connected.damage_coeff
+ injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected.precision_coeff)
+ else
+ injectorready = world.time + INJECTOR_TIMEOUT * 5
+
+ if("advinjector")
+ var/selection = href_list["injector"]
+ if(injectorready < world.time)
+ if(injector_selection.Find(selection))
+ var/list/true_selection = injector_selection[selection]
+ if(LAZYLEN(injector_selection))
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+ for(var/A in true_selection)
+ var/datum/mutation/human/HM = A
+ I.add_mutations += new HM.type (copymut = HM)
+ I.doitanyway = TRUE
+ I.name = "Advanced [selection] injector"
+ if(connected)
+ I.damage_coeff = connected.damage_coeff
+ injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected.precision_coeff)
+ else
+ injectorready = world.time + INJECTOR_TIMEOUT * 8
+ if("nullify")
+ if(viable_occupant)
+ var/datum/mutation/human/A = viable_occupant.dna.get_mutation(current_mutation)
+ if(A && (!viable_occupant.dna.mutation_in_sequence(current_mutation) || A.scrambled))
+ viable_occupant.dna.remove_mutation(current_mutation)
+ current_screen = "mainmenu"
+ current_mutation = null
+ if("pulsegene")
+ if(current_screen != "info")
+ var/path = text2path(href_list["path"])
+ if(viable_occupant && num && (path in viable_occupant.dna.mutation_index))
+ var/list/genes = list("A","T","G","C","X")
+ if(jokerready < world.time)
+ genes += "JOKER"
+ var/sequence = GET_GENE_STRING(path, viable_occupant.dna)
+ var/original = sequence[num]
+ var/new_gene = input("From [original] to-", "New block", original) as null|anything in genes
+ if(!new_gene)
+ new_gene = original
+ if(viable_occupant == get_viable_occupant()) //No cheesing
+ if((new_gene == "JOKER") && (jokerready < world.time))
+ var/true_genes = GET_SEQUENCE(current_mutation)
+ new_gene = true_genes[num]
+ jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected.precision_coeff-1))
+ sequence = copytext(sequence, 1, num) + new_gene + copytext(sequence, num+1, length(sequence)+1)
+ viable_occupant.dna.mutation_index[path] = sequence
+ viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected.damage_coeff
+ viable_occupant.domutcheck()
+ if("exportdiskmut")
+ if(diskette && !diskette.read_only)
+ var/path = text2path(href_list["path"])
+ if(ispath(path, /datum/mutation/human))
+ var/datum/mutation/human/A = get_valid_mutation(path)
+ if(A && diskette && (LAZYLEN(diskette.mutations) < diskette.max_mutations))
+ var/datum/mutation/human/HM = new A.type()
+ diskette.mutations += HM
+ HM.copy_mutation(A)
+ to_chat(usr, "
Succesfully written [A.name] to [diskette.name].")
+ if("deletediskmut")
+ if(diskette && !diskette.read_only)
+ if(num && (LAZYLEN(diskette.mutations) >= num))
+ var/datum/mutation/human/A = diskette.mutations[num]
+ diskette.mutations.Remove(A)
+ qdel(A)
+ if("importdiskmut")
+ if(diskette && (LAZYLEN(diskette.mutations) >= num))
+ if(LAZYLEN(stored_mutations) < max_storage)
+ var/datum/mutation/human/A = diskette.mutations[num]
+ var/datum/mutation/human/HM = new A.type()
+ HM.copy_mutation(A)
+ stored_mutations += HM
+ to_chat(usr,"
Succesfully written [A.name] to storage.")
+ if("combine")
+ if(num && (LAZYLEN(stored_mutations) >= num))
+ if(LAZYLEN(stored_mutations) < max_storage)
+ var/datum/mutation/human/A = stored_mutations[num]
+ var/path = A.type
+ if(combine)
+ var/result_path = get_mixed_mutation(combine, path)
+ if(result_path)
+ stored_mutations += new result_path()
+ to_chat(usr, "Succes! New mutation has been added to storage")
+ discover(result_path)
+ combine = null
+ else
+ to_chat(usr, "Failed. No mutation could be created.")
+ combine = null
+ else
+ combine = path
+ to_chat(usr,"Selected [A.name] for combining")
+ else
+ to_chat(usr, "Not enough space to store potential mutation.")
+ if("ejectchromosome")
+ if(LAZYLEN(stored_chromosomes) <= num)
+ var/obj/item/chromosome/CM = stored_chromosomes[num]
+ CM.forceMove(drop_location())
+ adjust_item_drop_location(CM)
+ stored_chromosomes -= CM
+ if("applychromosome")
+ if(viable_occupant && (LAZYLEN(viable_occupant.dna.mutations) <= num))
+ var/datum/mutation/human/HM = viable_occupant.dna.mutations[num]
+ var/list/chromosomes = list()
+ for(var/obj/item/chromosome/CM in stored_chromosomes)
+ if(CM.can_apply(HM))
+ chromosomes += CM
+ if(chromosomes.len)
+ var/obj/item/chromosome/CM = input("Select a chromosome to apply", "Apply Chromosome") as null|anything in sortNames(chromosomes)
+ if(CM)
+ to_chat(usr, "You apply [CM] to [HM.name].")
+ stored_chromosomes -= CM
+ CM.apply(HM)
+ if("expand_advinjector")
+ var/mutation = text2path(href_list["path"])
+ var/datum/mutation/human/HM = get_valid_mutation(mutation)
+ if(HM && LAZYLEN(injector_selection))
+ var/which_injector = input(usr, "Select Adv. Injector", "Advanced Injectors") as null|anything in injector_selection
+ if(injector_selection.Find(which_injector))
+ var/list/true_selection = injector_selection[which_injector]
+ var/total_instability
+ for(var/B in true_selection)
+ var/datum/mutation/human/mootacion = B
+ total_instability += mootacion.instability
+ total_instability += HM.instability
+ if((total_instability > max_injector_instability) || (true_selection.len + 1) > max_injector_mutations)
+ to_chat(usr, "Adding more mutations would make the advanced injector too unstable!")
+ else
+ true_selection += HM //reminder that this works. because I keep forgetting this works
+ if("remove_from_advinjector")
+ var/mutation = text2path(href_list["path"])
+ var/selection = href_list["injector"]
+ if(injector_selection.Find(selection))
+ var/list/true_selection = injector_selection[selection]
+ for(var/B in true_selection)
+ var/datum/mutation/human/HM = B
+ if(HM.type == mutation)
+ true_selection -= HM
+ break
+
+ if("remove_advinjector")
+ var/selection = href_list["injector"]
+ for(selection in injector_selection)
+ if(selection == selection)
+ injector_selection.Remove(selection)
+
+ if("add_advinjector")
+ if(LAZYLEN(injector_selection) < max_injector_selections)
+ var/new_selection = input(usr, "Enter Adv. Injector name", "Advanced Injectors") as text|null
+ if(new_selection && !(new_selection in injector_selection))
+ injector_selection[new_selection] = list()
+
ui_interact(usr,last_change)
-/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd)
+/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd) //hexadecimal genetics. dont confuse with scramble button
var/length = length(input)
var/ran = gaussian(0, rs*RADIATION_STRENGTH_MULTIPLIER)
if(ran == 0)
- ran = pick(-1,1) //hacky, statistically should almost never happen. 0-change makes people mad though
+ ran = pick(-1,1) //hacky, statistically should almost never happen. 0-chance makes people mad though
else if(ran < 0)
ran = round(ran) //negative, so floor it
else
@@ -547,10 +962,6 @@
//Each laser level reduces damage by lvl^2, so no effect on 1 lvl, 4 times less damage on 2 and 9 times less damage on 3
//Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
switch(action)
- if(SCANNER_ACTION_SE)
- if(buffer_slot["SE"])
- viable_occupant.dna.struc_enzymes = buffer_slot["SE"]
- viable_occupant.domutcheck()
if(SCANNER_ACTION_UI)
if(buffer_slot["UI"])
viable_occupant.dna.uni_identity = buffer_slot["UI"]
@@ -572,15 +983,54 @@
viable_occupant.dna.blood_type = buffer_slot["blood_type"]
/obj/machinery/computer/scan_consolenew/proc/on_scanner_close()
- if(delayed_action && connected)
+ if(delayed_action && get_viable_occupant())
to_chat(connected.occupant, "[src] activates!")
apply_buffer(delayed_action["action"],delayed_action["buffer"])
delayed_action = null //or make it stick + reset button ?
+/obj/machinery/computer/scan_consolenew/proc/get_valid_mutation(mutation)
+ var/mob/living/carbon/C = get_viable_occupant()
+ if(C)
+ var/datum/mutation/human/HM = C.dna.get_mutation(mutation)
+ if(HM)
+ return HM
+ for(var/datum/mutation/human/A in stored_mutations)
+ if(A.type == mutation)
+ return A
+
+
+/obj/machinery/computer/scan_consolenew/proc/get_mutation_list(include_storage) //Returns a list of the mutation index types and any extra mutations
+ var/mob/living/carbon/viable_occupant = get_viable_occupant()
+ var/list/paths = list()
+ if(viable_occupant)
+ for(var/A in viable_occupant.dna.mutation_index)
+ paths += A
+ for(var/datum/mutation/human/A in viable_occupant.dna.mutations)
+ if(A.class == MUT_EXTRA)
+ paths += A.type
+ if(include_storage)
+ for(var/datum/mutation/human/A in stored_mutations)
+ paths += A.type
+ return paths
+
+/obj/machinery/computer/scan_consolenew/proc/get_valid_gene_string(mutation)
+ var/mob/living/carbon/C = get_viable_occupant()
+ if(C && (mutation in C.dna.mutation_index))
+ return GET_GENE_STRING(mutation, C.dna)
+ else if(C && (LAZYLEN(C.dna.mutations)))
+ for(var/datum/mutation/human/A in C.dna.mutations)
+ if(A.type == mutation)
+ return GET_SEQUENCE(mutation)
+ for(var/datum/mutation/human/A in stored_mutations)
+ if(A.type == mutation)
+ return GET_SEQUENCE(mutation)
+
+/obj/machinery/computer/scan_consolenew/proc/discover(mutation)
+ if(stored_research && !(mutation in stored_research.discovered_mutations))
+ stored_research.discovered_mutations += mutation
+ return TRUE
/////////////////////////// DNA MACHINES
#undef INJECTOR_TIMEOUT
-#undef REJUVENATORS_INJECT
-#undef REJUVENATORS_MAX
#undef NUMBER_OF_BUFFERS
#undef RADIATION_STRENGTH_MAX
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index ed61e13596..4b2ba85d11 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -22,7 +22,7 @@
precision_coeff = 0
for(var/obj/item/stock_parts/scanning_module/P in component_parts)
scan_level += P.rating
- for(var/obj/item/stock_parts/manipulator/P in component_parts)
+ for(var/obj/item/stock_parts/matter_bin/P in component_parts)
precision_coeff = P.rating
for(var/obj/item/stock_parts/micro_laser/P in component_parts)
damage_coeff = P.rating
diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm
index 25e5948b51..a88cba851f 100644
--- a/code/game/machinery/exp_cloner.dm
+++ b/code/game/machinery/exp_cloner.dm
@@ -9,7 +9,7 @@
internal_radio = FALSE
//Start growing a human clone in the pod!
-/obj/machinery/clonepod/experimental/growclone(ckey, clonename, ui, se, datum/species/mrace, list/features, factions)
+/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -20,15 +20,15 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
- H.hardset_dna(ui, se, H.real_name, null, mrace, features)
+ H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features)
if(efficiency > 2)
var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations)
H.dna.remove_mutation_group(unclean_mutations)
if(efficiency > 5 && prob(20))
- H.randmutvg()
+ H.easy_randmut(POSITIVE)
if(efficiency < 3 && prob(50))
- var/mob/M = H.randmutb()
+ var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
if(ismob(M))
H = M
@@ -42,18 +42,17 @@
icon_state = "pod_1"
//Get the clone body ready
maim_clone(H)
- ADD_TRAIT(H, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_MUTE, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_NOBREATH, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, CLONING_POD_TRAIT)
+ ADD_TRAIT(H, TRAIT_STABLEHEART, "cloning")
+ ADD_TRAIT(H, TRAIT_EMOTEMUTE, "cloning")
+ ADD_TRAIT(H, TRAIT_MUTE, "cloning")
+ ADD_TRAIT(H, TRAIT_NOBREATH, "cloning")
+ ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning")
H.Unconscious(80)
- var/list/candidates = pollCandidatesForMob("Do you want and agree to play as a [clonename]'s defective clone, respect their character and not engage in ERP without permission from the original?", null, null, null, 100, H, POLL_IGNORE_CLONE)
+ var/list/candidates = pollCandidatesForMob("Do you want to play as [clonename]'s defective clone?", null, null, null, 100, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- C.transfer_ckey(H)
+ H.key = C.key
if(grab_ghost_when == CLONER_FRESH_CLONE)
H.grab_ghost()
@@ -293,6 +292,7 @@
temp = "Cloning cycle already in progress."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
- pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.struc_enzymes, clone_species, dna.features, mob_occupant.faction)
+ pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
temp = "[mob_occupant.real_name] => Cloning data sent to pod."
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index 8b6c2180d9..97c89c9168 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -63,7 +63,7 @@ Buildable meters
if(make_from.flipped)
do_a_flip()
-/obj/item/pipe/dropped()
+/obj/item/pipe/dropped(mob/user)
if(loc)
setPipingLayer(piping_layer)
return ..()
@@ -225,7 +225,7 @@ Buildable meters
to_chat(user, "You fasten the meter to the [loc.name].")
qdel(src)
-/obj/item/pipe_meter/dropped()
+/obj/item/pipe_meter/dropped(mob/user)
. = ..()
if(loc)
setAttachLayer(piping_layer)
diff --git a/code/game/machinery/toylathe.dm b/code/game/machinery/toylathe.dm
index 87ab3cadd8..c679f3f983 100644
--- a/code/game/machinery/toylathe.dm
+++ b/code/game/machinery/toylathe.dm
@@ -46,7 +46,12 @@
)
/obj/machinery/autoylathe/Initialize()
- AddComponent(/datum/component/material_container, list(/datum/material/iron, /datum/material/glass, /datum/material/plastic), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
+ var/static/list/allowed_materials = list(
+ /datum/material/iron,
+ /datum/material/glass,
+ /datum/material/plastic
+ )
+ AddComponent(/datum/component/material_container, allowed_materials, 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autoylathe(src)
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index 38ef518f49..4ddb5281ea 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -283,13 +283,13 @@
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_charge()
if(equip_ready) //disabled
return
- var/area/A = get_base_area(chassis)
- var/pow_chan = get_power_channel(A)
+ var/area/A = get_area(chassis)
+ var/pow_chan = get_MUTATION_POWER_channel(A)
if(pow_chan)
return 1000 //making magic
-/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_power_channel(var/area/A)
+/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_MUTATION_POWER_channel(var/area/A)
var/pow_chan
if(A)
for(var/c in use_channels)
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index f9b8f31bca..2ebb2f6ded 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -35,9 +35,19 @@
)
/obj/machinery/mecha_part_fabricator/Initialize()
- var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
- list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace), 0,
- TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
+ var/static/list/allowed_types = list(
+ /datum/material/iron,
+ /datum/material/glass,
+ /datum/material/silver,
+ /datum/material/gold,
+ /datum/material/diamond,
+ /datum/material/plasma,
+ /datum/material/uranium,
+ /datum/material/bananium,
+ /datum/material/titanium,
+ /datum/material/bluespace
+ )
+ var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, allowed_types, 0, TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
materials.precise_insertion = TRUE
stored_research = new
return ..()
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index 08843c9202..581283d339 100644
--- a/code/game/mecha/mecha_defense.dm
+++ b/code/game/mecha/mecha_defense.dm
@@ -320,11 +320,7 @@
clearInternalDamage(MECHA_INT_CONTROL_LOST)
/obj/mecha/narsie_act()
- if(occupant)
- var/mob/living/L = occupant
- go_out(TRUE)
- if(L)
- L.narsie_act()
+ emp_act(EMP_HEAVY)
/obj/mecha/ratvar_act()
if((GLOB.ratvar_awakens || GLOB.clockwork_gateway_activated) && occupant)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 8c3ffa3cfc..e75a18dca8 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -138,13 +138,13 @@
name = "flux wave anomaly"
icon_state = "electricity2"
density = TRUE
- var/canshock = 0
+ var/canshock = FALSE
var/shockdamage = 20
var/explosive = TRUE
/obj/effect/anomaly/flux/anomalyEffect()
..()
- canshock = 1
+ canshock = TRUE
for(var/mob/living/M in range(0, src))
mobShock(M)
@@ -159,18 +159,8 @@
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
if(canshock && istype(M))
- canshock = 0 //Just so you don't instakill yourself if you slam into the anomaly five times in a second.
- if(iscarbon(M))
- if(ishuman(M))
- M.electrocute_act(shockdamage, "[name]", safety=1)
- return
- M.electrocute_act(shockdamage, "[name]")
- return
- else
- M.adjustFireLoss(shockdamage)
- M.visible_message("[M] was shocked by \the [name]!", \
- "You feel a powerful shock coursing through your body!", \
- "You hear a heavy electrical crack.")
+ canshock = FALSE //Just so you don't instakill yourself if you slam into the anomaly five times in a second.
+ M.electrocute_act(shockdamage, "[name]", flags = SHOCK_NOGLOVES)
/obj/effect/anomaly/flux/detonate()
if(explosive)
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 20c85f37a1..48fba2687d 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -28,6 +28,7 @@
take_damage(5, BURN, 0, 0)
/obj/structure/spider/stickyweb
+ var/genetic = FALSE
icon_state = "stickyweb1"
/obj/structure/spider/stickyweb/Initialize()
@@ -36,6 +37,8 @@
. = ..()
/obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target)
+ if (genetic)
+ return
if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider))
return TRUE
else if(isliving(mover))
@@ -48,6 +51,27 @@
return prob(30)
return TRUE
+/obj/structure/spider/stickyweb/genetic //for the spider genes in genetics
+ genetic = TRUE
+ var/mob/living/allowed_mob
+
+/obj/structure/spider/stickyweb/genetic/Initialize(mapload, allowedmob)
+ allowed_mob = allowedmob
+ . = ..()
+
+/obj/structure/spider/stickyweb/genetic/CanPass(atom/movable/mover, turf/target)
+ . = ..() //this is the normal spider web return aka a spider would make this TRUE
+ if(mover == allowed_mob)
+ return TRUE
+ else if(isliving(mover)) //we change the spider to not be able to go through here
+ if(mover.pulledby == allowed_mob)
+ return TRUE
+ if(prob(50))
+ to_chat(mover, "You get stuck in \the [src] for a moment.")
+ return FALSE
+ else if(istype(mover, /obj/item/projectile))
+ return prob(30)
+
/obj/structure/spider/eggcluster
name = "egg cluster"
desc = "They seem to pulse slightly with an inner life."
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index fd6ae1bb43..5c2f151e38 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -850,3 +850,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if (HAS_TRAIT(src, TRAIT_NODROP))
return
return ..()
+
+/obj/item/proc/embedded(mob/living/carbon/human/embedded_mob)
+ return
+
+/obj/item/proc/unembedded()
+ return
\ No newline at end of file
diff --git a/code/game/objects/items/RCL.dm b/code/game/objects/items/RCL.dm
index 9513519fa9..72e6ccbbe2 100644
--- a/code/game/objects/items/RCL.dm
+++ b/code/game/objects/items/RCL.dm
@@ -117,7 +117,7 @@
. = ..()
if(!isinhands || !(loaded?.amount))
return
- var/mutable_appearance/cable_overlay = mutable_appearance(icon, "rcl-[CEILING(loaded.amount/(max_amount/3), 1)]")
+ var/mutable_appearance/cable_overlay = mutable_appearance(icon_file, "rcl-[CEILING(loaded.amount/(max_amount/3), 1)]")
cable_overlay.color = GLOB.cable_colors[colors[current_color_index]]
. += cable_overlay
diff --git a/code/game/objects/items/chromosome.dm b/code/game/objects/items/chromosome.dm
new file mode 100644
index 0000000000..1340e7f35b
--- /dev/null
+++ b/code/game/objects/items/chromosome.dm
@@ -0,0 +1,92 @@
+/obj/item/chromosome
+ name = "blank chromosome"
+ icon = 'icons/obj/chromosomes.dmi'
+ icon_state = ""
+ desc = "A tube holding chromosomic data."
+ force = 0
+ w_class = WEIGHT_CLASS_SMALL
+
+ var/stabilizer_coeff = 1 //lower is better, affects genetic stability
+ var/synchronizer_coeff = 1 //lower is better, affects chance to backfire
+ var/power_coeff = 1 //higher is better, affects "strength"
+ var/energy_coeff = 1 //lower is better. affects recharge time
+
+ var/weight = 5
+
+/obj/item/chromosome/proc/can_apply(datum/mutation/human/HM)
+ if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
+ return FALSE
+ if((stabilizer_coeff != 1) && (HM.stabilizer_coeff != -1)) //if the chromosome is 1, we dont change anything. If the mutation is -1, we cant change it. sorry
+ return TRUE
+ if((synchronizer_coeff != 1) && (HM.synchronizer_coeff != -1))
+ return TRUE
+ if((power_coeff != 1) && (HM.power_coeff != -1))
+ return TRUE
+ if((energy_coeff != 1) && (HM.energy_coeff != -1))
+ return TRUE
+
+/obj/item/chromosome/proc/apply(datum/mutation/human/HM)
+ if(HM.stabilizer_coeff != -1)
+ HM.stabilizer_coeff = stabilizer_coeff
+ if(HM.synchronizer_coeff != -1)
+ HM.synchronizer_coeff = synchronizer_coeff
+ if(HM.power_coeff != -1)
+ HM.power_coeff = power_coeff
+ if(HM.energy_coeff != -1)
+ HM.energy_coeff = energy_coeff
+ HM.can_chromosome = 2
+ HM.chromosome_name = name
+ HM.modify()
+ qdel(src)
+
+/proc/generate_chromosome()
+ var/static/list/chromosomes
+ if(!chromosomes)
+ chromosomes = list()
+ for(var/A in subtypesof(/obj/item/chromosome))
+ var/obj/item/chromosome/CM = A
+ if(!initial(CM.weight))
+ break
+ chromosomes[A] = initial(CM.weight)
+ return pickweight(chromosomes)
+
+
+/obj/item/chromosome/stabilizer
+ name = "stabilizer chromosome"
+ desc = "A chromosome that adjusts to the body to reduce genetic damage by 20%."
+ icon_state = "stabilizer"
+ stabilizer_coeff = 0.8
+ weight = 1
+
+/obj/item/chromosome/synchronizer
+ name = "synchronizer chromosome"
+ desc = "A chromosome that gives the mind more controle over the mutation, reducing knockback and downsides by 50%."
+ icon_state = "synchronizer"
+ synchronizer_coeff = 0.5
+
+/obj/item/chromosome/power
+ name = "power chromosome"
+ desc = "A power chromosome for boosting certain mutation's power by 50%."
+ icon_state = "power"
+ power_coeff = 1.5
+
+/obj/item/chromosome/energy
+ name = "energetic chromosome"
+ desc = "A chromosome that reduces cooldown on action based mutations by 50%."
+ icon_state = "energy"
+ energy_coeff = 0.5
+
+/obj/item/chromosome/reinforcer
+ name = "reinforcement chromosome"
+ desc = "Renders the mutation immune to mutadone."
+ icon_state = "reinforcer"
+ weight = 3
+
+/obj/item/chromosome/reinforcer/can_apply(datum/mutation/human/HM)
+ if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
+ return FALSE
+ return !HM.mutadone_proof
+
+/obj/item/chromosome/reinforcer/apply(datum/mutation/human/HM)
+ HM.mutadone_proof = TRUE
+ ..()
\ No newline at end of file
diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm
index 7b8c53484a..6b3428698b 100644
--- a/code/game/objects/items/chrono_eraser.dm
+++ b/code/game/objects/items/chrono_eraser.dm
@@ -18,15 +18,11 @@
/obj/item/chrono_eraser/proc/pass_mind(datum/mind/M)
erased_minds += M
-/obj/item/chrono_eraser/dropped()
+/obj/item/chrono_eraser/dropped(mob/user)
..()
if(PA)
qdel(PA)
-/obj/item/chrono_eraser/Destroy()
- dropped()
- return ..()
-
/obj/item/chrono_eraser/ui_action_click(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index df0a4e6cf3..b13cfd9f64 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -74,7 +74,7 @@
build_path = /obj/machinery/dna_scannernew
req_components = list(
/obj/item/stock_parts/scanning_module = 1,
- /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/sheet/glass = 1,
/obj/item/stack/cable_coil = 2)
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 677502e069..9d0d5a0445 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -230,7 +230,7 @@ GLOBAL_LIST_EMPTY(PDAs)
var/mutable_appearance/overlay = mutable_appearance(icon, screen_state)
overlay.pixel_x = overlays_x_offset
overlay.pixel_y = overlays_y_offset
- . += overlay
+ . += new /mutable_appearance(overlay)
if(id)
overlay.icon_state = current_overlays[PDA_OVERLAY_ID]
. += new /mutable_appearance(overlay)
@@ -242,7 +242,7 @@ GLOBAL_LIST_EMPTY(PDAs)
. += new /mutable_appearance(overlay)
if(pai)
overlay.icon_state = "[current_overlays[PDA_OVERLAY_PAI]][pai.pai ? "" : "_off"]"
- . += new /mutable_appearance(overlay)
+ . += overlay
new_overlays = FALSE
new_alert = FALSE
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index e87987600a..57443de973 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -21,7 +21,7 @@
var/obj/item/cigbutt/butt = /obj/item/cigbutt
saved_appearance = initial(butt.appearance)
-/obj/item/chameleon/dropped()
+/obj/item/chameleon/dropped(mob/user)
..()
disrupt()
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index 9eb51ffaeb..5cab9e33de 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -218,7 +218,7 @@
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
rad_act(amount)
-/obj/item/geiger_counter/cyborg/dropped()
+/obj/item/geiger_counter/cyborg/dropped(mob/user)
. = ..()
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_ATOM_RAD_ACT)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 556080168f..8802698b5d 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -150,7 +150,10 @@ SLIME SCANNER
msg += "\n\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage."
if(advanced)
msg += "\n\tCellular Damage Level: [M.getCloneLoss()]."
-
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(advanced && H.has_dna())
+ msg += "\n\tGenetic Stability: [H.dna.stability]%."
to_chat(user, msg)
msg = ""
@@ -326,8 +329,8 @@ SLIME SCANNER
breathes = FALSE
if(NOBLOOD in C.dna.species.species_traits)
blooded = FALSE
- var/has_liver = (!(NOLIVER in C.dna.species.species_traits))
- var/has_stomach = (!(NOSTOMACH in C.dna.species.species_traits))
+ var/has_liver = C.dna && !(NOLIVER in C.dna.species.species_traits)
+ var/has_stomach = C.dna && !(NOSTOMACH in C.dna.species.species_traits)
if(!M.getorganslot(ORGAN_SLOT_EYES))
msg += "\tSubject does not have eyes.\n"
if(!M.getorganslot(ORGAN_SLOT_EARS))
@@ -776,3 +779,104 @@ SLIME SCANNER
var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE)
if(!response)
to_chat(user, "No nanites detected in the subject.")
+
+/obj/item/sequence_scanner
+ name = "genetic sequence scanner"
+ icon = 'icons/obj/device.dmi'
+ icon_state = "gene"
+ item_state = "healthanalyzer"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ desc = "A hand-held scanner for analyzing someones gene sequence on the fly. Hold near a DNA console to update the internal database."
+ flags_1 = CONDUCT_1
+ item_flags = NOBLUDGEON
+ slot_flags = ITEM_SLOT_BELT
+ throwforce = 3
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ custom_materials = list(/datum/material/iron=200)
+ var/list/discovered = list() //hit a dna console to update the scanners database
+ var/list/buffer
+ var/ready = TRUE
+ var/cooldown = 200
+
+/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user)
+ add_fingerprint(user)
+ if (!HAS_TRAIT(M, TRAIT_RADIMMUNE)) //no scanning if its a husk or DNA-less Species
+ user.visible_message("[user] analyzes [M]'s genetic sequence.", \
+ "You analyze [M]'s genetic sequence.")
+ gene_scan(M, user)
+
+ else
+ user.visible_message("[user] failed to analyse [M]'s genetic sequence.", "[M] has no readable genetic sequence!")
+
+/obj/item/sequence_scanner/attack_self(mob/user)
+ display_sequence(user)
+
+/obj/item/sequence_scanner/attack_self_tk(mob/user)
+ return
+
+/obj/item/sequence_scanner/afterattack(obj/O, mob/user, proximity)
+ . = ..()
+ if(!istype(O) || !proximity)
+ return
+
+ if(istype(O, /obj/machinery/computer/scan_consolenew))
+ var/obj/machinery/computer/scan_consolenew/C = O
+ if(C.stored_research)
+ to_chat(user, "[name] linked to central research database.")
+ discovered = C.stored_research.discovered_mutations
+ else
+ to_chat(user,"No database to update from.")
+
+/obj/item/sequence_scanner/proc/gene_scan(mob/living/carbon/C, mob/living/user)
+ if(!iscarbon(C) || !C.has_dna())
+ return
+ buffer = C.dna.mutation_index
+ to_chat(user, "Subject [C.name]'s DNA sequence has been saved to buffer.")
+ if(LAZYLEN(buffer))
+ for(var/A in buffer)
+ to_chat(user, "[get_display_name(A)]")
+
+
+/obj/item/sequence_scanner/proc/display_sequence(mob/living/user)
+ if(!LAZYLEN(buffer) || !ready)
+ return
+ var/list/options = list()
+ for(var/A in buffer)
+ options += get_display_name(A)
+
+ var/answer = input(user, "Analyze Potential", "Sequence Analyzer") as null|anything in sortList(options)
+ if(answer && ready && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ var/sequence
+ for(var/A in buffer) //this physically hurts but i dont know what anything else short of an assoc list
+ if(get_display_name(A) == answer)
+ sequence = buffer[A]
+ break
+
+ if(sequence)
+ var/display
+ for(var/i in 0 to length_char(sequence) / DNA_MUTATION_BLOCKS-1)
+ if(i)
+ display += "-"
+ display += copytext_char(sequence, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1)
+
+ to_chat(user, "[display]
")
+
+ ready = FALSE
+ icon_state = "[icon_state]_recharging"
+ addtimer(CALLBACK(src, .proc/recharge), cooldown, TIMER_UNIQUE)
+
+/obj/item/sequence_scanner/proc/recharge()
+ icon_state = initial(icon_state)
+ ready = TRUE
+
+/obj/item/sequence_scanner/proc/get_display_name(mutation)
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation)
+ if(!HM)
+ return "ERROR"
+ if(mutation in discovered)
+ return "[HM.name] ([HM.alias])"
+ else
+ return HM.alias
\ No newline at end of file
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 2c101b0ec5..b929642f33 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -63,10 +63,10 @@
if(attached_device)
attached_device.holder_movement()
-/obj/item/transfer_valve/dropped()
+/obj/item/transfer_valve/dropped(mob/user)
. = ..()
if(attached_device)
- attached_device.dropped()
+ attached_device.dropped(user)
/obj/item/transfer_valve/on_found(mob/finder)
if(attached_device)
diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm
index a03447c0f0..60b1e81c94 100644
--- a/code/game/objects/items/dna_injector.dm
+++ b/code/game/objects/items/dna_injector.dm
@@ -14,33 +14,25 @@
var/list/add_mutations = list()
var/list/remove_mutations = list()
- var/list/add_mutations_static = list()
- var/list/remove_mutations_static = list()
-
var/used = 0
/obj/item/dnainjector/attack_paw(mob/user)
return attack_hand(user)
-/obj/item/dnainjector/proc/prepare()
- for(var/mut_key in add_mutations_static)
- add_mutations.Add(GLOB.mutations_list[mut_key])
- for(var/mut_key in remove_mutations_static)
- remove_mutations.Add(GLOB.mutations_list[mut_key])
-
/obj/item/dnainjector/proc/inject(mob/living/carbon/M, mob/user)
- prepare()
-
if(M.has_dna() && !HAS_TRAIT(M, TRAIT_RADIMMUNE) && !HAS_TRAIT(M, TRAIT_NOCLONE))
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
- for(var/datum/mutation/human/HM in remove_mutations)
- HM.force_lose(M)
- for(var/datum/mutation/human/HM in add_mutations)
- if(HM.name == RACEMUT)
+ for(var/HM in remove_mutations)
+ M.dna.remove_mutation(HM)
+ for(var/HM in add_mutations)
+ if(HM == RACEMUT)
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(M)] with the [name] (MONKEY)")
log_msg += " (MONKEY)"
- HM.force_give(M)
+ if(M.dna.mutation_in_sequence(HM))
+ M.dna.activate_mutation(HM)
+ else
+ M.dna.add_mutation(HM, MUT_EXTRA)
if(fields)
if(fields["name"] && fields["UE"] && fields["blood_type"])
M.real_name = fields["name"]
@@ -90,123 +82,123 @@
/obj/item/dnainjector/antihulk
name = "\improper DNA injector (Anti-Hulk)"
desc = "Cures green skin."
- remove_mutations_static = list(HULK)
+ remove_mutations = list(HULK)
/obj/item/dnainjector/hulkmut
name = "\improper DNA injector (Hulk)"
desc = "This will make you big and strong, but give you a bad skin condition."
- add_mutations_static = list(HULK)
+ add_mutations = list(HULK)
/obj/item/dnainjector/xraymut
name = "\improper DNA injector (X-ray)"
desc = "Finally you can see what the Captain does."
- add_mutations_static = list(XRAY)
+ add_mutations = list(XRAY)
/obj/item/dnainjector/antixray
name = "\improper DNA injector (Anti-X-ray)"
desc = "It will make you see harder."
- remove_mutations_static = list(XRAY)
+ remove_mutations = list(XRAY)
/////////////////////////////////////
/obj/item/dnainjector/antiglasses
name = "\improper DNA injector (Anti-Glasses)"
desc = "Toss away those glasses!"
- remove_mutations_static = list(BADSIGHT)
+ remove_mutations = list(BADSIGHT)
/obj/item/dnainjector/glassesmut
name = "\improper DNA injector (Glasses)"
desc = "Will make you need dorkish glasses."
- add_mutations_static = list(BADSIGHT)
+ add_mutations = list(BADSIGHT)
/obj/item/dnainjector/epimut
name = "\improper DNA injector (Epi.)"
desc = "Shake shake shake the room!"
- add_mutations_static = list(EPILEPSY)
+ add_mutations = list(EPILEPSY)
/obj/item/dnainjector/antiepi
name = "\improper DNA injector (Anti-Epi.)"
desc = "Will fix you up from shaking the room."
- remove_mutations_static = list(EPILEPSY)
+ remove_mutations = list(EPILEPSY)
////////////////////////////////////
/obj/item/dnainjector/anticough
name = "\improper DNA injector (Anti-Cough)"
desc = "Will stop that awful noise."
- remove_mutations_static = list(COUGH)
+ remove_mutations = list(COUGH)
/obj/item/dnainjector/coughmut
name = "\improper DNA injector (Cough)"
desc = "Will bring forth a sound of horror from your throat."
- add_mutations_static = list(COUGH)
+ add_mutations = list(COUGH)
/obj/item/dnainjector/antidwarf
name = "\improper DNA injector (Anti-Dwarfism)"
desc = "Helps you grow big and strong."
- remove_mutations_static = list(DWARFISM)
+ remove_mutations = list(DWARFISM)
/obj/item/dnainjector/dwarf
name = "\improper DNA injector (Dwarfism)"
desc = "It's a small world after all."
- add_mutations_static = list(DWARFISM)
+ add_mutations = list(DWARFISM)
/obj/item/dnainjector/clumsymut
name = "\improper DNA injector (Clumsy)"
desc = "Makes clown minions."
- add_mutations_static = list(CLOWNMUT)
+ add_mutations = list(CLOWNMUT)
/obj/item/dnainjector/anticlumsy
name = "\improper DNA injector (Anti-Clumsy)"
desc = "Apply this for Security Clown."
- remove_mutations_static = list(CLOWNMUT)
+ remove_mutations = list(CLOWNMUT)
/obj/item/dnainjector/antitour
name = "\improper DNA injector (Anti-Tour.)"
desc = "Will cure Tourette's."
- remove_mutations_static = list(TOURETTES)
+ remove_mutations = list(TOURETTES)
/obj/item/dnainjector/tourmut
name = "\improper DNA injector (Tour.)"
desc = "Gives you a nasty case of Tourette's."
- add_mutations_static = list(TOURETTES)
+ add_mutations = list(TOURETTES)
/obj/item/dnainjector/stuttmut
name = "\improper DNA injector (Stutt.)"
desc = "Makes you s-s-stuttterrr."
- add_mutations_static = list(NERVOUS)
+ add_mutations = list(NERVOUS)
/obj/item/dnainjector/antistutt
name = "\improper DNA injector (Anti-Stutt.)"
desc = "Fixes that speaking impairment."
- remove_mutations_static = list(NERVOUS)
+ remove_mutations = list(NERVOUS)
/obj/item/dnainjector/antifire
name = "\improper DNA injector (Anti-Fire)"
desc = "Cures fire."
- remove_mutations_static = list(COLDRES)
+ remove_mutations = list(SPACEMUT)
/obj/item/dnainjector/firemut
name = "\improper DNA injector (Fire)"
desc = "Gives you fire."
- add_mutations_static = list(COLDRES)
+ add_mutations = list(SPACEMUT)
/obj/item/dnainjector/blindmut
name = "\improper DNA injector (Blind)"
desc = "Makes you not see anything."
- add_mutations_static = list(BLINDMUT)
+ add_mutations = list(BLINDMUT)
/obj/item/dnainjector/antiblind
name = "\improper DNA injector (Anti-Blind)"
desc = "IT'S A MIRACLE!!!"
- remove_mutations_static = list(BLINDMUT)
+ remove_mutations = list(BLINDMUT)
/obj/item/dnainjector/antitele
name = "\improper DNA injector (Anti-Tele.)"
desc = "Will make you not able to control your mind."
- remove_mutations_static = list(TK)
+ remove_mutations = list(TK)
/obj/item/dnainjector/telemut
name = "\improper DNA injector (Tele.)"
desc = "Super brain man!"
- add_mutations_static = list(TK)
+ add_mutations = list(TK)
/obj/item/dnainjector/telemut/darkbundle
name = "\improper DNA injector"
@@ -215,100 +207,258 @@
/obj/item/dnainjector/deafmut
name = "\improper DNA injector (Deaf)"
desc = "Sorry, what did you say?"
- add_mutations_static = list(DEAFMUT)
+ add_mutations = list(DEAFMUT)
/obj/item/dnainjector/antideaf
name = "\improper DNA injector (Anti-Deaf)"
desc = "Will make you hear once more."
- remove_mutations_static = list(DEAFMUT)
+ remove_mutations = list(DEAFMUT)
/obj/item/dnainjector/h2m
name = "\improper DNA injector (Human > Monkey)"
desc = "Will make you a flea bag."
- add_mutations_static = list(RACEMUT)
+ add_mutations = list(RACEMUT)
/obj/item/dnainjector/m2h
name = "\improper DNA injector (Monkey > Human)"
desc = "Will make you...less hairy."
- remove_mutations_static = list(RACEMUT)
+ remove_mutations = list(RACEMUT)
/obj/item/dnainjector/antichameleon
name = "\improper DNA injector (Anti-Chameleon)"
- remove_mutations_static = list(CHAMELEON)
+ remove_mutations = list(CHAMELEON)
/obj/item/dnainjector/chameleonmut
name = "\improper DNA injector (Chameleon)"
- add_mutations_static = list(CHAMELEON)
+ add_mutations = list(CHAMELEON)
/obj/item/dnainjector/antiwacky
name = "\improper DNA injector (Anti-Wacky)"
- remove_mutations_static = list(WACKY)
+ remove_mutations = list(WACKY)
/obj/item/dnainjector/wackymut
name = "\improper DNA injector (Wacky)"
- add_mutations_static = list(WACKY)
+ add_mutations = list(WACKY)
/obj/item/dnainjector/antimute
name = "\improper DNA injector (Anti-Mute)"
- remove_mutations_static = list(MUT_MUTE)
+ remove_mutations = list(MUT_MUTE)
/obj/item/dnainjector/mutemut
name = "\improper DNA injector (Mute)"
- add_mutations_static = list(MUT_MUTE)
+ add_mutations = list(MUT_MUTE)
/obj/item/dnainjector/antismile
name = "\improper DNA injector (Anti-Smile)"
- remove_mutations_static = list(SMILE)
+ remove_mutations = list(SMILE)
/obj/item/dnainjector/smilemut
name = "\improper DNA injector (Smile)"
- add_mutations_static = list(SMILE)
+ add_mutations = list(SMILE)
/obj/item/dnainjector/unintelligiblemut
name = "\improper DNA injector (Unintelligible)"
- add_mutations_static = list(UNINTELLIGIBLE)
+ add_mutations = list(UNINTELLIGIBLE)
/obj/item/dnainjector/antiunintelligible
name = "\improper DNA injector (Anti-Unintelligible)"
- remove_mutations_static = list(UNINTELLIGIBLE)
+ remove_mutations = list(UNINTELLIGIBLE)
/obj/item/dnainjector/swedishmut
name = "\improper DNA injector (Swedish)"
- add_mutations_static = list(SWEDISH)
+ add_mutations = list(SWEDISH)
/obj/item/dnainjector/antiswedish
name = "\improper DNA injector (Anti-Swedish)"
- remove_mutations_static = list(SWEDISH)
+ remove_mutations = list(SWEDISH)
/obj/item/dnainjector/chavmut
name = "\improper DNA injector (Chav)"
- add_mutations_static = list(CHAV)
+ add_mutations = list(CHAV)
/obj/item/dnainjector/antichav
name = "\improper DNA injector (Anti-Chav)"
- remove_mutations_static = list(CHAV)
+ remove_mutations = list(CHAV)
/obj/item/dnainjector/elvismut
name = "\improper DNA injector (Elvis)"
- add_mutations_static = list(ELVIS)
+ add_mutations = list(ELVIS)
/obj/item/dnainjector/antielvis
name = "\improper DNA injector (Anti-Elvis)"
- remove_mutations_static = list(ELVIS)
+ remove_mutations = list(ELVIS)
/obj/item/dnainjector/lasereyesmut
name = "\improper DNA injector (Laser Eyes)"
- add_mutations_static = list(LASEREYES)
+ add_mutations = list(LASEREYES)
/obj/item/dnainjector/antilasereyes
name = "\improper DNA injector (Anti-Laser Eyes)"
- remove_mutations_static = list(LASEREYES)
+ remove_mutations = list(LASEREYES)
+
+/obj/item/dnainjector/void
+ name = "\improper DNA injector (Void)"
+ add_mutations = list(VOID)
+
+/obj/item/dnainjector/antivoid
+ name = "\improper DNA injector (Anti-Void)"
+ remove_mutations = list(VOID)
+
+/obj/item/dnainjector/antenna
+ name = "\improper DNA injector (Antenna)"
+ add_mutations = list(ANTENNA)
+
+/obj/item/dnainjector/antiantenna
+ name = "\improper DNA injector (Anti-Antenna)"
+ remove_mutations = list(ANTENNA)
+
+/obj/item/dnainjector/paranoia
+ name = "\improper DNA injector (Paranoia)"
+ add_mutations = list(PARANOIA)
+
+/obj/item/dnainjector/antiparanoia
+ name = "\improper DNA injector (Anti-Paranoia)"
+ remove_mutations = list(PARANOIA)
+
+/obj/item/dnainjector/mindread
+ name = "\improper DNA injector (Mindread)"
+ add_mutations = list(MINDREAD)
+
+/obj/item/dnainjector/antimindread
+ name = "\improper DNA injector (Anti-Mindread)"
+ remove_mutations = list(MINDREAD)
+
+/obj/item/dnainjector/radioactive
+ name = "\improper DNA injector (Radioactive)"
+ add_mutations = list(RADIOACTIVE)
+
+/obj/item/dnainjector/antiradioactive
+ name = "\improper DNA injector (Anti-Radioactive)"
+ remove_mutations = list(RADIOACTIVE)
+/obj/item/dnainjector/olfaction
+ name = "\improper DNA injector (Olfaction)"
+ add_mutations = list(OLFACTION)
+
+/obj/item/dnainjector/antiolfaction
+ name = "\improper DNA injector (Anti-Olfaction)"
+ remove_mutations = list(OLFACTION)
+
+/obj/item/dnainjector/insulated
+ name = "\improper DNA injector (Insulated)"
+ add_mutations = list(INSULATED)
+
+/obj/item/dnainjector/antiinsulated
+ name = "\improper DNA injector (Anti-Insulated)"
+ remove_mutations = list(INSULATED)
+
+/obj/item/dnainjector/shock
+ name = "\improper DNA injector (Shock Touch)"
+ add_mutations = list(SHOCKTOUCH)
+
+/obj/item/dnainjector/antishock
+ name = "\improper DNA injector (Anti-Shock Touch)"
+ remove_mutations = list(SHOCKTOUCH)
+
+/obj/item/dnainjector/spacialinstability
+ name = "\improper DNA injector (Spacial Instability)"
+ add_mutations = list(BADBLINK)
+
+/obj/item/dnainjector/antispacialinstability
+ name = "\improper DNA injector (Anti-Spacial Instability)"
+ remove_mutations = list(BADBLINK)
+
+/obj/item/dnainjector/acidflesh
+ name = "\improper DNA injector (Acid Flesh)"
+ add_mutations = list(ACIDFLESH)
+
+/obj/item/dnainjector/antiacidflesh
+ name = "\improper DNA injector (Acid Flesh)"
+ remove_mutations = list(ACIDFLESH)
+
+/obj/item/dnainjector/gigantism
+ name = "\improper DNA injector (Gigantism)"
+ add_mutations = list(GIGANTISM)
+
+/obj/item/dnainjector/antigigantism
+ name = "\improper DNA injector (Anti-Gigantism)"
+ remove_mutations = list(GIGANTISM)
+
+/obj/item/dnainjector/spastic
+ name = "\improper DNA injector (Spastic)"
+ add_mutations = list(SPASTIC)
+
+/obj/item/dnainjector/antispastic
+ name = "\improper DNA injector (Anti-Spastic)"
+ remove_mutations = list(SPASTIC)
+
+/obj/item/dnainjector/geladikinesis
+ name = "\improper DNA injector (Geladikinesis)"
+ add_mutations = list(GELADIKINESIS)
+
+/obj/item/dnainjector/antigeladikinesis
+ name = "\improper DNA injector (Anti-Geladikinesis)"
+ remove_mutations = list(GELADIKINESIS)
+
+/obj/item/dnainjector/cryokinesis
+ name = "\improper DNA injector (Cryokinesis)"
+ add_mutations = list(CRYOKINESIS)
+
+/obj/item/dnainjector/anticryokinesis
+ name = "\improper DNA injector (Anti-Cryokinesis)"
+ remove_mutations = list(CRYOKINESIS)
+
+/obj/item/dnainjector/thermal
+ name = "\improper DNA injector (Thermal Vision)"
+ add_mutations = list(THERMAL)
+
+/obj/item/dnainjector/antithermal
+ name = "\improper DNA injector (Anti-Thermal Vision)"
+ remove_mutations = list(THERMAL)
+
+/obj/item/dnainjector/glow
+ name = "\improper DNA injector (Glowy)"
+ add_mutations = list(GLOWY)
+
+/obj/item/dnainjector/removeglow
+ name = "\improper DNA injector (Anti-Glowy)"
+ remove_mutations = list(GLOWY)
+
+/obj/item/dnainjector/antiglow
+ name = "\improper DNA injector (Antiglowy)"
+ add_mutations = list(ANTIGLOWY)
+
+/obj/item/dnainjector/removeantiglow
+ name = "\improper DNA injector (Anti-Antiglowy)"
+ remove_mutations = list(ANTIGLOWY)
+
+/obj/item/dnainjector/firebreath
+ name = "\improper DNA injector (Firebreath)"
+ add_mutations = list(FIREBREATH)
+
+/obj/item/dnainjector/antifirebreath
+ name = "\improper DNA injector (Anti-Firebreath)"
+ remove_mutations = list(FIREBREATH)
+
+/obj/item/dnainjector/tonguespike
+ name = "\improper DNA injector (Tongue Spike)"
+ add_mutations = list(TONGUESPIKE)
+
+/obj/item/dnainjector/antitonguespike
+ name = "\improper DNA injector (Anti-Tongue Spike)"
+ remove_mutations = list(TONGUESPIKE)
+
+/obj/item/dnainjector/spiderweb
+ name = "\improper DNA injector (Spider Web)"
+ add_mutations = list(SPIDER_WEB)
+
+/obj/item/dnainjector/antispiderweb
+ name = "\improper DNA injector (Anti-Spider Web)"
+ remove_mutations = list(SPIDER_WEB)
/obj/item/dnainjector/timed
var/duration = 600
/obj/item/dnainjector/timed/inject(mob/living/carbon/M, mob/user)
- prepare()
if(M.stat == DEAD) //prevents dead people from having their DNA changed
to_chat(user, "You can't modify [M]'s DNA while [M.p_theyre()] dead.")
return FALSE
@@ -317,23 +467,22 @@
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
var/endtime = world.time+duration
- for(var/datum/mutation/human/HM in remove_mutations)
- if(HM.name == RACEMUT)
+ for(var/mutation in remove_mutations)
+ if(mutation == RACEMUT)
if(ishuman(M))
continue
- M = HM.force_lose(M)
+ M = M.dna.remove_mutation(mutation)
else
- HM.force_lose(M)
- for(var/datum/mutation/human/HM in add_mutations)
- if((HM in M.dna.mutations) && !(M.dna.temporary_mutations[HM.name]))
+ M.dna.remove_mutation(mutation)
+ for(var/mutation in add_mutations)
+ if(M.dna.get_mutation(mutation))
continue //Skip permanent mutations we already have.
- if(HM.name == RACEMUT && ishuman(M))
+ if(mutation == RACEMUT && ishuman(M))
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(M)] with the [name] (MONKEY)")
log_msg += " (MONKEY)"
- M = HM.force_give(M)
+ M = M.dna.add_mutation(mutation, MUT_OTHER, endtime)
else
- HM.force_give(M)
- M.dna.temporary_mutations[HM.name] = endtime
+ M.dna.add_mutation(mutation, MUT_OTHER, endtime)
if(fields)
if(fields["name"] && fields["UE"] && fields["blood_type"])
if(!M.dna.previous["name"])
@@ -361,9 +510,41 @@
/obj/item/dnainjector/timed/hulk
name = "\improper DNA injector (Hulk)"
desc = "This will make you big and strong, but give you a bad skin condition."
- add_mutations_static = list(HULK)
+ add_mutations = list(HULK)
/obj/item/dnainjector/timed/h2m
name = "\improper DNA injector (Human > Monkey)"
desc = "Will make you a flea bag."
- add_mutations_static = list(RACEMUT)
+ add_mutations = list(RACEMUT)
+
+/obj/item/dnainjector/activator
+ name = "\improper DNA activator"
+ desc = "Activates the current mutation on injection, if the subject has it."
+ var/doitanyway = FALSE
+ var/research = FALSE //Set to true to get expended and filled injectors for chromosomes
+ var/filled = FALSE
+
+/obj/item/dnainjector/activator/inject(mob/living/carbon/M, mob/user)
+ if(M.has_dna() && !HAS_TRAIT(M, TRAIT_RADIMMUNE) && !HAS_TRAIT(M,TRAIT_NOCLONE))
+ M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
+ var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
+ for(var/mutation in add_mutations)
+ var/datum/mutation/human/HM = mutation
+ if(istype(HM, /datum/mutation/human))
+ mutation = HM.type
+ if(!M.dna.activate_mutation(HM))
+ if(!doitanyway)
+ log_msg += "(FAILED)"
+ else
+ M.dna.add_mutation(HM, MUT_EXTRA)
+ name = "expended [name]"
+ else if(research && M.client)
+ filled = TRUE
+ name = "filled [name]"
+ else
+ name = "expended [name]"
+ log_msg += "([mutation])"
+ log_attack("[log_msg] [loc_name(user)]")
+ return TRUE
+ return FALSE
+
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index 0907a18fdd..9749bae313 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -11,7 +11,7 @@
/obj/item/book/granter/proc/turn_page(mob/user)
playsound(user, pick('sound/effects/pageturn1.ogg','sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg'), 30, 1)
- if(do_after(user,50, user))
+ if(do_after(user,50, TRUE, user))
if(remarks.len)
to_chat(user, "[pick(remarks)]")
else
@@ -53,7 +53,7 @@
on_reading_stopped()
reading = FALSE
return
- if(do_after(user,50, user))
+ if(do_after(user,50, TRUE, user))
on_reading_finished(user)
reading = FALSE
return TRUE
@@ -402,10 +402,11 @@
martialname = "sleeping carp"
desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art."
greet = "You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \
- directed toward you. However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab."
+ directed toward you while in Throw Mode. Your body is also honed to protect you from damage and punctures, and even briefly survive space. \
+ However, you are also unable to use any ranged weaponry, and some medical supplies will prove useless to you. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab."
icon = 'icons/obj/wizard.dmi'
icon_state = "scroll2"
- remarks = list("I must prove myself worthy to the masters of the sleeping carp...", "Stance means everything...", "Focus... And you'll be able to incapacitate any foe in seconds...", "I must pierce armor for maximum damage...", "I don't think this would combine with other martial arts...", "Grab them first so they don't retaliate...", "I must prove myself worthy of this power...")
+ remarks = list("Wait, a high protein diet is really all it takes to become bulletproof...?", "Overwhelming force, immovable object...", "Focus... And you'll be able to incapacitate any foe in seconds...", "I must pierce armor for maximum damage...", "I don't think this would combine with other martial arts...", "Become one with the carp...", "Glub...")
/obj/item/book/granter/martial/carp/onlearned(mob/living/carbon/user)
..()
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index ea7d57255f..e1b0cbb661 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -342,15 +342,25 @@
/obj/item/restraints/legcuffs/bola/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(..() || !iscarbon(hit_atom))//if it gets caught or the target can't be cuffed,
return//abort
- var/mob/living/carbon/C = hit_atom
+ ensnare(hit_atom)
+
+/**
+ * Attempts to legcuff someone with the bola
+ *
+ * Arguments:
+ * * C - the carbon that we will try to ensnare
+ */
+/obj/item/restraints/legcuffs/bola/proc/ensnare(mob/living/carbon/C)
if(!C.legcuffed && C.get_num_legs(FALSE) >= 2)
visible_message("
\The [src] ensnares [C]!")
C.legcuffed = src
forceMove(C)
+ C.update_equipment_speed_mods()
C.update_inv_legcuffed()
SSblackbox.record_feedback("tally", "handcuffs", 1, type)
to_chat(C, "
\The [src] ensnares you!")
- C.DefaultCombatKnockdown(knockdown)
+ C.Knockdown(knockdown)
+ playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
/obj/item/restraints/legcuffs/bola/tactical//traitor variant
name = "reinforced bola"
diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm
index d3e966f2fa..fe1f3ebd6c 100644
--- a/code/game/objects/items/robot/robot_items.dm
+++ b/code/game/objects/items/robot/robot_items.dm
@@ -109,7 +109,7 @@
if(scooldown < world.time)
if(M.health >= 0)
if(ishuman(M)||ismonkey(M))
- M.electrocute_act(5, "[user]", safety = 1)
+ M.electrocute_act(5, "[user]", flags = SHOCK_NOGLOVES)
user.visible_message("
[user] electrocutes [M] with [user.p_their()] touch!", \
"
You electrocute [M] with your touch!")
else
@@ -357,7 +357,7 @@
/obj/item/borg/lollipop/equipped()
check_amount()
-/obj/item/borg/lollipop/dropped()
+/obj/item/borg/lollipop/dropped(mob/user)
check_amount()
/obj/item/borg/lollipop/proc/check_amount() //Doesn't even use processing ticks.
@@ -620,7 +620,7 @@
return host.loc
return null
-/obj/item/borg/projectile_dampen/dropped()
+/obj/item/borg/projectile_dampen/dropped(mob/user)
. = ..()
host = loc
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 6cb351cb74..6865660d0b 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -464,6 +464,7 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \
new /datum/stack_recipe("egg box", /obj/item/storage/fancy/egg_box), \
new /datum/stack_recipe("donk-pockets box", /obj/item/storage/box/donkpockets), \
new /datum/stack_recipe("monkey cube box", /obj/item/storage/box/monkeycubes), \
+ new /datum/stack_recipe("nugget box", /obj/item/storage/fancy/nugget_box), \
new /datum/stack_recipe("box (internals)", /obj/item/storage/box/otwo), \
null, \
new /datum/stack_recipe("security-styled box", /obj/item/storage/box/seclooking), \
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index 7a6e1e3db6..103dabdeec 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -132,7 +132,7 @@
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/Pickup_ores)
listeningTo = user
-/obj/item/storage/bag/ore/dropped()
+/obj/item/storage/bag/ore/dropped(mob/user)
. = ..()
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm
index f2c50b359e..3bc90d3615 100644
--- a/code/game/objects/items/storage/fancy.dm
+++ b/code/game/objects/items/storage/fancy.dm
@@ -357,6 +357,20 @@
STR.max_items = 8
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/tinychocolate))
+/obj/item/storage/fancy/nugget_box
+ name = "nugget box"
+ desc = "A cardboard box used for holding chicken nuggies."
+ icon = 'icons/obj/food/containers.dmi'
+ icon_state = "nuggetbox"
+ icon_type = "nugget"
+ spawn_type = /obj/item/reagent_containers/food/snacks/nugget
+
+/obj/item/storage/fancy/nugget_box/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/nugget))
+
/*
* Ring Box
*/
diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm
index 0ff20f16c5..c9c686414d 100644
--- a/code/game/objects/items/storage/firstaid.dm
+++ b/code/game/objects/items/storage/firstaid.dm
@@ -525,14 +525,6 @@
desc = "A kit containing a Deluxe hypospray and Vials."
icon_state = "tactical-mini"
-/obj/item/storage/hypospraykit/cmo/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.can_hold = typecacheof(list(
- /obj/item/hypospray/mkii,
- /obj/item/reagent_containers/glass/bottle/vial))
-
/obj/item/storage/hypospraykit/cmo/PopulateContents()
if(empty)
return
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index 11a06e149d..57fc99c663 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -246,11 +246,11 @@
user.visible_message("
[user] axes [user.p_them()]self from head to toe! It looks like [user.p_theyre()] trying to commit suicide!")
return (BRUTELOSS)
-/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/user, proximity)
+/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/living/user, proximity)
. = ..()
- if(!proximity)
+ if(!proximity || (user.getStaminaLoss() > STAMINA_SOFTCRIT))
return
- if(wielded) //destroys windows and grilles in one hit
+ if(wielded) //destroys windows and grilles in one hit (or more if it has a ton of health like plasmaglass)
if(istype(A, /obj/structure/window))
var/obj/structure/window/W = A
W.take_damage(200, BRUTE, "melee", 0)
@@ -1217,7 +1217,7 @@
target.lastattackerckey = user.ckey
target.visible_message("
[user] has shocked [target] with [src]!", \
"
[user] has shocked you with [src]!")
- log_combat(user, user, "stunned with an electrostaff")
+ log_combat(user, target, "stunned with an electrostaff")
playsound(src, 'sound/weapons/staff.ogg', 50, 1, -1)
target.apply_status_effect(stun_status_effect, stun_status_duration)
if(ishuman(user))
@@ -1242,9 +1242,9 @@
if(user)
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
- target.visible_message("
[user] has seared [user] with [src]!", \
+ target.visible_message("
[user] has seared [target] with [src]!", \
"
[user] has seared you with [src]!")
- log_combat(user, user, "burned with an electrostaff")
+ log_combat(user, target, "burned with an electrostaff")
playsound(src, 'sound/weapons/sear.ogg', 50, 1, -1)
return TRUE
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 2fd6cf9f4c..e582aa7e67 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -244,7 +244,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
if(has_buckled_mobs())
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
- buckled_mob.electrocute_act((CLAMP(round(strength/400), 10, 90) + rand(-5, 5)), src, tesla_shock = 1)
+ buckled_mob.electrocute_act((CLAMP(round(strength/400), 10, 90) + rand(-5, 5)), src, flags = SHOCK_TESLA)
/obj/proc/reset_shocked()
obj_flags &= ~BEING_SHOCKED
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 3fc183edc6..c92bbdc996 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -127,11 +127,10 @@
if ((M.client && M.machine == src))
is_in_use = TRUE
ui_interact(M)
- if(isAI(usr) || iscyborg(usr) || IsAdminGhost(usr) || hasSiliconAccessInArea(usr))
- if (!(usr in nearby))
- if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
- is_in_use = TRUE
- ui_interact(usr)
+ if(usr && hasSiliconAccessInArea(usr) && !(usr in nearby))
+ if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
+ is_in_use = TRUE
+ ui_interact(usr)
// check for TK users
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index fcd1f80a4f..3e35c12314 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -255,7 +255,7 @@
return
return ..()
-/obj/structure/AIcore/update_icon()
+/obj/structure/AIcore/update_icon_state()
switch(state)
if(EMPTY_CORE)
icon_state = "0"
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index 32e40bedec..a793459c5d 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -234,8 +234,7 @@
if(status == BURST)
obj_integrity = integrity_failure * max_integrity
-/obj/structure/alien/egg/update_icon()
- ..()
+/obj/structure/alien/egg/update_icon_state()
switch(status)
if(GROWING)
icon_state = "[base_icon]_growing"
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 8dc7caa5ad..0991351102 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -303,7 +303,7 @@ LINEN BINS
. += "There are [amount] sheets in the bin."
-/obj/structure/bedsheetbin/update_icon()
+/obj/structure/bedsheetbin/update_icon_state()
switch(amount)
if(0)
icon_state = "linenbin-empty"
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index c932dd96e8..e19feea582 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -58,30 +58,40 @@
return ..()
/obj/structure/closet/update_icon()
- cut_overlays()
- if(opened & icon_door_override)
- add_overlay("[icon_door]_open")
+ . = ..()
+ if(!opened)
layer = OBJ_LAYER
- return
- else if(opened)
- add_overlay("[icon_state]_open")
- return
- if(icon_door)
- add_overlay("[icon_door]_door")
else
layer = BELOW_OBJ_LAYER
- add_overlay("[icon_state]_door")
- if(welded)
- add_overlay("welded")
- if(!secure)
- return
- if(broken)
- add_overlay("off")
- add_overlay("sparking")
- else if(locked)
- add_overlay("locked")
+
+/obj/structure/closet/update_overlays()
+ . = ..()
+ closet_update_overlays(.)
+
+/obj/structure/closet/proc/closet_update_overlays(list/new_overlays)
+ . = new_overlays
+ if(!opened)
+ if(icon_door)
+ . += "[icon_door]_door"
+ else
+ . += "[icon_state]_door"
+ if(welded)
+ . += "welded"
+ if(!secure)
+ return
+ if(broken)
+ . += "off"
+ . += "sparking"
+ else if(locked)
+ . += "locked"
+ else
+ . += "unlocked"
else
- add_overlay("unlocked")
+ if(icon_door_override)
+ . += "[icon_door]_open"
+ else
+ . += "[icon_state]_open"
+
/obj/structure/closet/examine(mob/user)
. = ..()
diff --git a/code/game/objects/structures/crates_lockers/closets/bodybag.dm b/code/game/objects/structures/crates_lockers/closets/bodybag.dm
index 1c34850274..5ec3851128 100644
--- a/code/game/objects/structures/crates_lockers/closets/bodybag.dm
+++ b/code/game/objects/structures/crates_lockers/closets/bodybag.dm
@@ -38,10 +38,10 @@
tagged = 0
update_icon()
-/obj/structure/closet/body_bag/update_icon()
- ..()
+/obj/structure/closet/body_bag/update_overlays()
+ . = ..()
if (tagged)
- add_overlay("bodybag_label")
+ . += "bodybag_label"
/obj/structure/closet/body_bag/close()
if(..())
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 4f6d6729fc..b0dfec6318 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -33,12 +33,13 @@
return 1
return !density
-/obj/structure/closet/crate/update_icon()
+/obj/structure/closet/crate/update_icon_state()
icon_state = "[initial(icon_state)][opened ? "open" : ""]"
- cut_overlays()
+/obj/structure/closet/crate/closet_update_overlays(list/new_overlays)
+ . = new_overlays
if(manifest)
- add_overlay("manifest")
+ . += "manifest"
/obj/structure/closet/crate/attack_hand(mob/user)
. = ..()
diff --git a/code/game/objects/structures/crates_lockers/crates/bins.dm b/code/game/objects/structures/crates_lockers/crates/bins.dm
index bc3697a550..b57af43d3e 100644
--- a/code/game/objects/structures/crates_lockers/crates/bins.dm
+++ b/code/game/objects/structures/crates_lockers/crates/bins.dm
@@ -14,15 +14,15 @@
..()
update_icon()
-/obj/structure/closet/crate/bin/update_icon()
- ..()
+/obj/structure/closet/crate/bin/closet_update_overlays(list/new_overlays)
+ . = new_overlays
cut_overlays()
if(contents.len == 0)
- add_overlay("largebing")
+ . += "largebing"
else if(contents.len >= storage_capacity)
- add_overlay("largebinr")
+ . += "largebinr"
else
- add_overlay("largebino")
+ . += "largebino"
/obj/structure/closet/crate/bin/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/storage/bag/trash))
diff --git a/code/game/objects/structures/crates_lockers/crates/critter.dm b/code/game/objects/structures/crates_lockers/crates/critter.dm
index 3c0021b513..3dbbfd5c23 100644
--- a/code/game/objects/structures/crates_lockers/crates/critter.dm
+++ b/code/game/objects/structures/crates_lockers/crates/critter.dm
@@ -22,14 +22,14 @@
return ..()
-/obj/structure/closet/crate/critter/update_icon()
- cut_overlays()
+/obj/structure/closet/crate/critter/closet_update_overlays(list/new_overlays)
+ . = new_overlays
if(opened)
- add_overlay("crittercrate_door_open")
+ . += "crittercrate_door_open"
else
- add_overlay("crittercrate_door")
+ . += "crittercrate_door"
if(manifest)
- add_overlay("manifest")
+ . += "manifest"
/obj/structure/closet/crate/critter/return_air()
if(tank)
diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm
index 602adfb367..4ac69253d3 100644
--- a/code/game/objects/structures/crates_lockers/crates/secure.dm
+++ b/code/game/objects/structures/crates_lockers/crates/secure.dm
@@ -13,14 +13,14 @@
return 0
. = ..()
-/obj/structure/closet/crate/secure/update_icon()
- ..()
+/obj/structure/closet/crate/secure/update_overlays()
+ . += ..()
if(broken)
- add_overlay("securecrateemag")
+ . += "securecrateemag"
else if(locked)
- add_overlay("securecrater")
+ . += "securecrater"
else
- add_overlay("securecrateg")
+ . += "securecrateg"
/obj/structure/closet/crate/secure/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
if(prob(tamperproof) && damage_amount >= DAMAGE_PRECISION)
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 8a5b678f22..c332a07edf 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -84,7 +84,7 @@
alarmed.burglaralert(src)
playsound(src, 'sound/effects/alert.ogg', 50, 1)
-/obj/structure/displaycase/update_icon()
+/obj/structure/displaycase/update_icon_state()
var/icon/I
if(open)
I = icon('icons/obj/stationobjs.dmi',"glassbox_open")
diff --git a/code/game/objects/structures/divine.dm b/code/game/objects/structures/divine.dm
index 1da8a26b52..bca96e67d1 100644
--- a/code/game/objects/structures/divine.dm
+++ b/code/game/objects/structures/divine.dm
@@ -44,7 +44,7 @@
addtimer(CALLBACK(src, /atom/.proc/update_icon), time_between_uses)
-/obj/structure/healingfountain/update_icon()
+/obj/structure/healingfountain/update_icon_state()
if(last_process + time_between_uses > world.time)
icon_state = "fountain"
else
diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm
index f35e9e7d55..79600d5bf2 100644
--- a/code/game/objects/structures/door_assembly.dm
+++ b/code/game/objects/structures/door_assembly.dm
@@ -260,13 +260,13 @@
update_name()
update_icon()
-/obj/structure/door_assembly/update_icon()
- cut_overlays()
+/obj/structure/door_assembly/update_overlays()
+ . = ..()
if(!glass)
- add_overlay(get_airlock_overlay("fill_construction", icon))
+ . += get_airlock_overlay("fill_construction", icon)
else if(glass)
- add_overlay(get_airlock_overlay("glass_construction", overlays_file))
- add_overlay(get_airlock_overlay("panel_c[state+1]", overlays_file))
+ . += get_airlock_overlay("glass_construction", overlays_file)
+ . += get_airlock_overlay("panel_c[state+1]", overlays_file)
/obj/structure/door_assembly/proc/update_name()
name = ""
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index 2124c83d7d..9b736517be 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -116,7 +116,7 @@
opened = !opened
update_icon()
-/obj/structure/extinguisher_cabinet/update_icon()
+/obj/structure/extinguisher_cabinet/update_icon_state()
if(!opened)
icon_state = "extinguisher_closed"
return
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index 52441c2987..e44698f96e 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -64,7 +64,7 @@
update_icon()
air_update_turf(TRUE)
-/obj/structure/falsewall/update_icon()//Calling icon_update will refresh the smoothwalls if it's closed, otherwise it will make sure the icon is correct if it's open
+/obj/structure/falsewall/update_icon_state()//Calling icon_update will refresh the smoothwalls if it's closed, otherwise it will make sure the icon is correct if it's open
if(opening)
if(density)
icon_state = "fwall_opening"
diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm
index 8457a397a9..0f3a23dd0f 100644
--- a/code/game/objects/structures/fireaxe.dm
+++ b/code/game/objects/structures/fireaxe.dm
@@ -140,30 +140,30 @@
update_icon()
return
-/obj/structure/fireaxecabinet/update_icon()
- cut_overlays()
+/obj/structure/fireaxecabinet/update_overlays()
+ . = ..()
if(fireaxe)
- add_overlay("axe")
+ . += "axe"
if(!open)
var/hp_percent = obj_integrity/max_integrity * 100
if(broken)
- add_overlay("glass4")
+ . += "glass4"
else
switch(hp_percent)
if(-INFINITY to 40)
- add_overlay("glass3")
+ . += "glass3"
if(40 to 60)
- add_overlay("glass2")
+ . += "glass2"
if(60 to 80)
- add_overlay("glass1")
+ . += "glass1"
if(80 to INFINITY)
- add_overlay("glass")
+ . += "glass"
if(locked)
- add_overlay("locked")
+ . += "locked"
else
- add_overlay("unlocked")
+ . += "unlocked"
else
- add_overlay("glass_raised")
+ . += "glass_raised"
/obj/structure/fireaxecabinet/proc/toggle_lock(mob/user)
to_chat(user, "
Resetting circuitry...")
diff --git a/code/game/objects/structures/fireplace.dm b/code/game/objects/structures/fireplace.dm
index b670fe5cf0..ee8f285230 100644
--- a/code/game/objects/structures/fireplace.dm
+++ b/code/game/objects/structures/fireplace.dm
@@ -69,21 +69,22 @@
else
. = ..()
-/obj/structure/fireplace/update_icon()
- cut_overlays()
- if(lit)
- switch(burn_time_remaining())
- if(0 to 500)
- add_overlay("fireplace_fire0")
- if(500 to 1000)
- add_overlay("fireplace_fire1")
- if(1000 to 1500)
- add_overlay("fireplace_fire2")
- if(1500 to 2000)
- add_overlay("fireplace_fire3")
- if(2000 to MAXIMUM_BURN_TIMER)
- add_overlay("fireplace_fire4")
- add_overlay("fireplace_glow")
+/obj/structure/fireplace/update_overlays()
+ . = ..()
+ if(!lit)
+ return
+ switch(burn_time_remaining())
+ if(0 to 500)
+ . += "fireplace_fire0"
+ if(500 to 1000)
+ . += "fireplace_fire1"
+ if(1000 to 1500)
+ . += "fireplace_fire2"
+ if(1500 to 2000)
+ . += "fireplace_fire3"
+ if(2000 to MAXIMUM_BURN_TIMER)
+ . += "fireplace_fire4"
+ . += "fireplace_glow"
/obj/structure/fireplace/proc/adjust_light()
if(!lit)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 0f43040c87..61983dcb4f 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -22,7 +22,7 @@
. = ..()
update_icon()
-/obj/structure/grille/update_icon()
+/obj/structure/grille/update_icon_state()
if(QDELETED(src) || broken)
return
diff --git a/code/game/objects/structures/guncase.dm b/code/game/objects/structures/guncase.dm
index 701a4d071c..ede7e31e0d 100644
--- a/code/game/objects/structures/guncase.dm
+++ b/code/game/objects/structures/guncase.dm
@@ -22,17 +22,17 @@
break
update_icon()
-/obj/structure/guncase/update_icon()
- cut_overlays()
+/obj/structure/guncase/update_overlays()
+ . = ..()
if(case_type && LAZYLEN(contents))
var/mutable_appearance/gun_overlay = mutable_appearance(icon, case_type)
for(var/i in 1 to contents.len)
gun_overlay.pixel_x = 3 * (i - 1)
- add_overlay(gun_overlay)
+ . += gun_overlay
if(open)
- add_overlay("[icon_state]_open")
+ . += "[icon_state]_open"
else
- add_overlay("[icon_state]_door")
+ . += "[icon_state]_door"
/obj/structure/guncase/attackby(obj/item/I, mob/user, params)
if(iscyborg(user) || isalien(user))
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index 81433b562d..581ce850de 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -28,14 +28,14 @@
. = ..()
pixel_x = rand(-8, 8)
-/obj/structure/headpike/update_icon()
- ..()
+/obj/structure/headpike/update_overlays()
+ . = ..()
var/obj/item/bodypart/head/H = locate() in contents
- var/mutable_appearance/MA = new()
if(H)
+ var/mutable_appearance/MA = new()
MA.copy_overlays(H)
MA.pixel_y = 12
- add_overlay(H)
+ . += H
/obj/structure/headpike/attack_hand(mob/user)
. = ..()
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index fc1e642468..7ac3aba246 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -189,7 +189,7 @@
if(!shockcd)
if(ismob(user))
var/mob/living/M = user
- M.electrocute_act(15,"Energy Barrier", safety=1)
+ M.electrocute_act(15,"Energy Barrier", flags = SHOCK_NOGLOVES)
shockcd = TRUE
addtimer(CALLBACK(src, .proc/cooldown), 5)
@@ -201,6 +201,6 @@
return
var/mob/living/M = AM
- M.electrocute_act(15,"Energy Barrier", safety=1)
+ M.electrocute_act(15,"Energy Barrier", flags = SHOCK_NOGLOVES)
shockcd = TRUE
addtimer(CALLBACK(src, .proc/cooldown), 5)
diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm
index 58c8a739dc..b10ca8002c 100644
--- a/code/game/objects/structures/janicart.dm
+++ b/code/game/objects/structures/janicart.dm
@@ -161,20 +161,20 @@
updateUsrDialog()
-/obj/structure/janitorialcart/update_icon()
- cut_overlays()
+/obj/structure/janitorialcart/update_overlays()
+ . = ..()
if(mybag)
- add_overlay("cart_garbage")
+ . += "cart_garbage"
if(mymop)
- add_overlay("cart_mop")
+ . += "cart_mop"
if(mybroom)
- add_overlay("cart_broom")
+ . += "cart_broom"
if(myspray)
- add_overlay("cart_spray")
+ . += "cart_spray"
if(myreplacer)
- add_overlay("cart_replacer")
+ . += "cart_replacer"
if(signs)
- add_overlay("cart_sign[signs]")
+ . += "cart_sign[signs]"
if(reagents.total_volume > 0)
- add_overlay("cart_water")
+ . += "cart_water"
diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm
index b3f415bafe..53a1f609d1 100644
--- a/code/game/objects/structures/ladders.dm
+++ b/code/game/objects/structures/ladders.dm
@@ -55,7 +55,7 @@
down.update_icon()
up = down = null
-/obj/structure/ladder/update_icon()
+/obj/structure/ladder/update_icon_state()
if(up && down)
icon_state = "ladder11"
diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm
index 0ae0e29459..a64dc6c6a9 100644
--- a/code/game/objects/structures/life_candle.dm
+++ b/code/game/objects/structures/life_candle.dm
@@ -46,7 +46,7 @@
STOP_PROCESSING(SSobj, src)
set_light(0)
-/obj/structure/life_candle/update_icon()
+/obj/structure/life_candle/update_icon_state()
if(linked_minds.len)
icon_state = icon_state_active
else
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index caad9e7bd4..f52fa0576a 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -118,7 +118,7 @@
update_icon()
isSwitchingStates = 0
-/obj/structure/mineral_door/update_icon()
+/obj/structure/mineral_door/update_icon_state()
if(state)
icon_state = "[initial_state]open"
else
diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm
index a9e12c1404..e1bb143fef 100644
--- a/code/game/objects/structures/mop_bucket.dm
+++ b/code/game/objects/structures/mop_bucket.dm
@@ -23,7 +23,7 @@
else
return ..()
-/obj/structure/mopbucket/update_icon()
- cut_overlays()
+/obj/structure/mopbucket/update_overlays()
+ . = ..()
if(reagents.total_volume > 0)
- add_overlay("mopbucket_water")
\ No newline at end of file
+ . += "mopbucket_water"
\ No newline at end of file
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index bf891ef223..8cf5b1ee66 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -45,9 +45,6 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
..()
update_icon()
-/obj/structure/bodycontainer/update_icon()
- return
-
/obj/structure/bodycontainer/relaymove(mob/user)
if(user.stat || !isturf(loc))
return
@@ -174,7 +171,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
to_chat(user, "
You turn the speaker function [beeper ? "on" : "off"].")
return TRUE
-/obj/structure/bodycontainer/morgue/update_icon()
+/obj/structure/bodycontainer/morgue/update_icon_state()
if (!connected || connected.loc != src) // Open or tray is gone.
icon_state = "morgue0"
else
@@ -228,20 +225,17 @@ GLOBAL_LIST_EMPTY(crematoriums)
GLOB.crematoriums.Add(src)
..()
-/obj/structure/bodycontainer/crematorium/update_icon()
+/obj/structure/bodycontainer/crematorium/update_icon_state()
if(!connected || connected.loc != src)
icon_state = "crema0"
else
-
- if(src.contents.len > 1)
- src.icon_state = "crema2"
+ if(contents.len > 1)
+ icon_state = "crema2"
else
- src.icon_state = "crema1"
+ icon_state = "crema1"
if(locked)
- src.icon_state = "crema_active"
-
- return
+ icon_state = "crema_active"
/obj/structure/bodycontainer/crematorium/proc/cremate(mob/user)
if(locked)
diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm
index bdc7825feb..bba51035fe 100644
--- a/code/game/objects/structures/musician.dm
+++ b/code/game/objects/structures/musician.dm
@@ -203,7 +203,8 @@
if(lines.len)
var/bpm_string = "BPM: "
if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1))
- tempo = sanitize_tempo(600 / text2num(copytext(lines[1], length(bpm_string) + 1)))
+ var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default
+ tempo = sanitize_tempo(600 / round(divisor, 1))
lines.Cut(1, 2)
else
tempo = sanitize_tempo(5) // default 120 BPM
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 92c4d0a066..2a7fc9aeb6 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -13,7 +13,7 @@ FLOOR SAFES
anchored = TRUE
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
var/open = FALSE //is the safe open?
var/tumbler_1_pos //the tumbler position- from 0 to 72
var/tumbler_1_open //the tumbler position to open at- 0 to 72
@@ -73,7 +73,7 @@ FLOOR SAFES
num = 0
return num
-/obj/structure/safe/update_icon()
+/obj/structure/safe/update_icon_state()
if(open)
icon_state = "[initial(icon_state)]-open"
else
diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm
index f2c4e628e0..d1a2a6f9f6 100644
--- a/code/game/objects/structures/stairs.dm
+++ b/code/game/objects/structures/stairs.dm
@@ -54,7 +54,7 @@
return FALSE
return ..()
-/obj/structure/stairs/update_icon()
+/obj/structure/stairs/update_icon_state()
if(isTerminator())
icon_state = "stairs_t"
else
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index 5fabcafd75..6a8175b921 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -25,18 +25,18 @@
new /obj/item/tank/internals/plasma(src)
update_icon()
-/obj/structure/tank_dispenser/update_icon()
- cut_overlays()
+/obj/structure/tank_dispenser/update_overlays()
+ . = ..()
switch(oxygentanks)
if(1 to 3)
- add_overlay("oxygen-[oxygentanks]")
+ . += "oxygen-[oxygentanks]"
if(4 to TANK_DISPENSER_CAPACITY)
- add_overlay("oxygen-4")
+ . += "oxygen-4"
switch(plasmatanks)
if(1 to 4)
- add_overlay("plasma-[plasmatanks]")
+ . += "plasma-[plasmatanks]"
if(5 to TANK_DISPENSER_CAPACITY)
- add_overlay("plasma-5")
+ . += "plasma-5"
/obj/structure/tank_dispenser/attackby(obj/item/I, mob/user, params)
var/full
diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
index 392c802ed8..04f113a0c8 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
@@ -19,7 +19,7 @@
empty_pod()
return ..()
-/obj/structure/transit_tube_pod/update_icon()
+/obj/structure/transit_tube_pod/update_icon_state()
if(contents.len)
icon_state = "pod_occupied"
else
diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm
index fa9c052aa3..8044bb9bb7 100644
--- a/code/game/objects/structures/traps.dm
+++ b/code/game/objects/structures/traps.dm
@@ -82,7 +82,7 @@
icon_state = "trap-shock"
/obj/structure/trap/stun/trap_effect(mob/living/L)
- L.electrocute_act(30, src, safety=1) // electrocute act does a message.
+ L.electrocute_act(30, src, flags = SHOCK_NOGLOVES) // electrocute act does a message.
L.DefaultCombatKnockdown(100)
/obj/structure/trap/fire
@@ -146,7 +146,7 @@
/obj/structure/trap/cult/trap_effect(mob/living/L)
to_chat(L, "
With a crack, the hostile constructs come out of hiding, stunning you!")
- L.electrocute_act(10, src, safety = TRUE) // electrocute act does a message.
+ L.electrocute_act(10, src, flags = SHOCK_NOGLOVES) // electrocute act does a message.
L.DefaultCombatKnockdown(20)
new /mob/living/simple_animal/hostile/construct/proteon/hostile(loc)
new /mob/living/simple_animal/hostile/construct/proteon/hostile(loc)
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 78df9b5d57..5f806e7fc4 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -72,7 +72,7 @@
update_icon()
-/obj/structure/toilet/update_icon()
+/obj/structure/toilet/update_icon_state()
icon_state = "toilet[open][cistern]"
@@ -216,8 +216,6 @@
density = FALSE
use_power = NO_POWER_USE
var/on = FALSE
- var/obj/effect/mist/mymist = null
- var/ismist = 0 //needs a var so we can make it linger~
var/watertemp = "normal" //freezing, normal, or boiling
var/datum/looping_sound/showering/soundloop
@@ -240,6 +238,7 @@
/obj/machinery/shower/interact(mob/M)
on = !on
update_icon()
+ handle_mist()
add_fingerprint(M)
if(on)
START_PROCESSING(SSmachines, src)
@@ -280,31 +279,30 @@
return TRUE
-/obj/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up
- cut_overlays() //once it's been on for a while, in addition to handling the water overlay.
- if(mymist)
- qdel(mymist)
-
+/obj/machinery/shower/update_overlays()
+ . = ..()
if(on)
- add_overlay(mutable_appearance('icons/obj/watercloset.dmi', "water", ABOVE_MOB_LAYER))
- if(watertemp == "freezing")
- return
- if(!ismist)
- spawn(50)
- if(src && on)
- ismist = 1
- mymist = new /obj/effect/mist(loc)
- else
- ismist = 1
- mymist = new /obj/effect/mist(loc)
- else if(ismist)
- ismist = 1
- mymist = new /obj/effect/mist(loc)
- spawn(250)
- if(!on && mymist)
- qdel(mymist)
- ismist = 0
+ . += mutable_appearance('icons/obj/watercloset.dmi', "water", ABOVE_MOB_LAYER)
+/obj/machinery/shower/proc/handle_mist()
+ // If there is no mist, and the shower was turned on (on a non-freezing temp): make mist in 5 seconds
+ // If there was already mist, and the shower was turned off (or made cold): remove the existing mist in 25 sec
+ var/obj/effect/mist/mist = locate() in loc
+ if(!mist && on && watertemp != "freezing")
+ addtimer(CALLBACK(src, .proc/make_mist), 5 SECONDS)
+
+ if(mist && (!on || watertemp == "freezing"))
+ addtimer(CALLBACK(src, .proc/clear_mist), 25 SECONDS)
+
+/obj/machinery/shower/proc/make_mist()
+ var/obj/effect/mist/mist = locate() in loc
+ if(!mist && on && watertemp != "freezing")
+ new /obj/effect/mist(loc)
+
+/obj/machinery/shower/proc/clear_mist()
+ var/obj/effect/mist/mist = locate() in loc
+ if(mist && (!on || watertemp == "freezing"))
+ qdel(mist)
/obj/machinery/shower/Crossed(atom/movable/AM)
..()
@@ -692,7 +690,7 @@
open = !open
update_icon()
-/obj/structure/curtain/update_icon()
+/obj/structure/curtain/update_icon_state()
if(!open)
icon_state = "closed"
layer = WALL_OBJ_LAYER
diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm
index e824567b50..04c3909696 100644
--- a/code/game/objects/structures/windoor_assembly.dm
+++ b/code/game/objects/structures/windoor_assembly.dm
@@ -47,7 +47,7 @@
setDir(ini_dir)
move_update_air(T)
-/obj/structure/windoor_assembly/update_icon()
+/obj/structure/windoor_assembly/update_icon_state()
icon_state = "[facing]_[secure ? "secure_" : ""]windoor_assembly[state]"
/obj/structure/windoor_assembly/CanPass(atom/movable/mover, turf/target)
@@ -315,13 +315,8 @@
/obj/structure/windoor_assembly/ComponentInitialize()
. = ..()
- AddComponent(
- /datum/component/simple_rotation,
- ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS,
- null,
- CALLBACK(src, .proc/can_be_rotated),
- CALLBACK(src,.proc/after_rotation)
- )
+ var/static/rotation_flags = ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS
+ AddComponent(/datum/component/simple_rotation, rotation_flags, can_be_rotated=CALLBACK(src, .proc/can_be_rotated), after_rotation=CALLBACK(src,.proc/after_rotation))
/obj/structure/windoor_assembly/proc/can_be_rotated(mob/user,rotation_type)
if(anchored)
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index d4925c8604..5dd4e7d987 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -18,7 +18,6 @@
var/obj/item/stack/sheet/glass_type = /obj/item/stack/sheet/glass
var/cleanable_type = /obj/effect/decal/cleanable/glass
var/glass_amount = 1
- var/mutable_appearance/crack_overlay
can_be_unanchored = TRUE
resistance_flags = ACID_PROOF
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
@@ -337,22 +336,19 @@
queue_smooth_neighbors(src)
//merges adjacent full-tile windows into one
-/obj/structure/window/update_icon()
- if(!QDELETED(src))
- if(!fulltile)
- return
+/obj/structure/window/update_overlays()
+ . = ..()
+ if(QDELETED(src) || !fulltile)
+ return
+ var/ratio = obj_integrity / max_integrity
+ ratio = CEILING(ratio*4, 1) * 25
- var/ratio = obj_integrity / max_integrity
- ratio = CEILING(ratio*4, 1) * 25
+ if(smooth)
+ queue_smooth(src)
- if(smooth)
- queue_smooth(src)
-
- cut_overlay(crack_overlay)
- if(ratio > 75)
- return
- crack_overlay = mutable_appearance('icons/obj/structures.dmi', "damage[ratio]", -(layer+0.1))
- add_overlay(crack_overlay)
+ if(ratio > 75)
+ return
+ . += mutable_appearance('icons/obj/structures.dmi', "damage[ratio]", -(layer+0.1))
/obj/structure/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 29d7224950..e79784290b 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -63,7 +63,7 @@
addtimer(CALLBACK(src, .proc/emagNotify), 150)
return TRUE
-/obj/machinery/vr_sleeper/update_icon()
+/obj/machinery/vr_sleeper/update_icon_state()
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
/obj/machinery/vr_sleeper/open_machine()
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index bbb12b6692..4ddb92ab19 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -9,9 +9,9 @@
/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE)
var/static/key_cache = list()
if(!real_bans_only)
- if(key_cache[key])
+ if(key_cache[key] >= REALTIMEOFDAY)
return list("reason"="concurrent connection attempts", "desc"="You are attempting to connect too fast. Try again.")
- key_cache[key] = 1
+ key_cache[key] = REALTIMEOFDAY+10 //this shouldn't runtime, but if it does, expiry attempts will cover it to ensure genuine connection attemps wont get trapped in limbo
if (!key || !address || !computer_id)
if(real_bans_only)
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index 9784aa7cd1..4d1cb1a7de 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -40,6 +40,9 @@
H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list)
H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list)
H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list)
+ H.dna.features["flavor_text"] = "" //Oh no.
+
+ SEND_SIGNAL(H, COMSIG_HUMAN_ON_RANDOMIZE)
H.update_body()
H.update_hair()
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index c2a3f953f5..39cdf8a5fa 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -419,8 +419,9 @@
Congratulations! You are now trained for invasive xenobiology research!"}
-/obj/item/paper/guides/antag/abductor/update_icon()
- return
+/obj/item/paper/guides/antag/abductor/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/item/paper/guides/antag/abductor/AltClick()
return //otherwise it would fold into a paperplane.
@@ -442,6 +443,10 @@
w_class = WEIGHT_CLASS_NORMAL
actions_types = list(/datum/action/item_action/toggle_mode)
+/obj/item/abductor/baton/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
/obj/item/abductor/baton/proc/toggle(mob/living/user=usr)
mode = (mode+1)%BATON_MODES
var/txt
@@ -458,7 +463,7 @@
to_chat(usr, "
You switch the baton to [txt] mode.")
update_icon()
-/obj/item/abductor/baton/update_icon()
+/obj/item/abductor/baton/update_icon_state()
switch(mode)
if(BATON_STUN)
icon_state = "wonderprodStun"
diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm
index b5233606cf..96bcd27a3b 100644
--- a/code/modules/antagonists/abductor/machinery/experiment.dm
+++ b/code/modules/antagonists/abductor/machinery/experiment.dm
@@ -223,7 +223,7 @@
return
-/obj/machinery/abductor/experiment/update_icon()
+/obj/machinery/abductor/experiment/update_icon_state()
if(state_open)
icon_state = "experiment-open"
else
diff --git a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
index 261677a74b..b720d3590e 100644
--- a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
+++ b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
@@ -20,7 +20,7 @@
/mob/living/simple_animal/hostile/blob/update_icons()
if(overmind)
- add_atom_colour(overmind.blob_reagent_datum.color, FIXED_COLOUR_PRIORITY)
+ add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
else
remove_atom_colour(FIXED_COLOUR_PRIORITY)
@@ -34,7 +34,7 @@
for(var/i in 1 to 2)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
if(overmind)
- H.color = overmind.blob_reagent_datum.complementary_color
+ H.color = overmind.blobstrain.complementary_color
else
H.color = "#000000"
adjustHealth(-maxHealth*0.0125)
@@ -142,8 +142,10 @@
// Create the reagents to put into the air
create_reagents(10)
- if(overmind && overmind.blob_reagent_datum)
- reagents.add_reagent(overmind.blob_reagent_datum.type, 10)
+
+
+ if(overmind && overmind.blobstrain)
+ overmind.blobstrain.on_sporedeath(src)
else
reagents.add_reagent(/datum/reagent/toxin/spore, 10)
@@ -167,14 +169,14 @@
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
if(overmind)
- add_atom_colour(overmind.blob_reagent_datum.complementary_color, FIXED_COLOUR_PRIORITY)
+ add_atom_colour(overmind.blobstrain.complementary_color, FIXED_COLOUR_PRIORITY)
else
remove_atom_colour(FIXED_COLOUR_PRIORITY)
if(is_zombie)
copy_overlays(oldguy, TRUE)
var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head")
if(overmind)
- blob_head_overlay.color = overmind.blob_reagent_datum.complementary_color
+ blob_head_overlay.color = overmind.blobstrain.complementary_color
color = initial(color)//looks better.
add_overlay(blob_head_overlay)
@@ -243,14 +245,14 @@
adjustHealth(-maxHealth*0.1)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
if(overmind)
- H.color = overmind.blob_reagent_datum.complementary_color
+ H.color = overmind.blobstrain.complementary_color
else
H.color = "#000000"
if(locate(/obj/structure/blob/node) in blobs_in_area)
adjustHealth(-maxHealth*0.05)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src))
if(overmind)
- H.color = overmind.blob_reagent_datum.complementary_color
+ H.color = overmind.blobstrain.complementary_color
else
H.color = "#000000"
if(damagesources)
@@ -259,7 +261,7 @@
var/image/I = new('icons/mob/blob.dmi', src, "nautdamage", MOB_LAYER+0.01)
I.appearance_flags = RESET_COLOR
if(overmind)
- I.color = overmind.blob_reagent_datum.complementary_color
+ I.color = overmind.blobstrain.complementary_color
flick_overlay_view(I, src, 8)
/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
@@ -274,16 +276,14 @@
/mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget()
. = ..()
if(. && isliving(target) && overmind)
- var/mob/living/L = target
- var/mob_protection = L.get_permeability_protection()
- overmind.blob_reagent_datum.reaction_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
+ overmind.blobstrain.blobbernaut_attack(target)
/mob/living/simple_animal/hostile/blob/blobbernaut/update_icons()
..()
if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage
melee_damage_lower = 4
melee_damage_upper = 4
- attacktext = overmind.blob_reagent_datum.blobbernaut_message
+ attacktext = overmind.blobstrain.blobbernaut_message
else
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
diff --git a/code/modules/antagonists/blob/blob/blobs/core.dm b/code/modules/antagonists/blob/blob/blobs/core.dm
index 58f79e1c73..38484c70b0 100644
--- a/code/modules/antagonists/blob/blob/blobs/core.dm
+++ b/code/modules/antagonists/blob/blob/blobs/core.dm
@@ -8,12 +8,8 @@
explosion_block = 6
point_return = -1
health_regen = 0 //we regen in Life() instead of when pulsed
- var/core_regen = 2
- var/resource_delay = 0
- var/point_rate = 2
-
-/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, new_rate = 2, placed = 0)
+/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, placed = 0)
GLOB.blob_cores += src
START_PROCESSING(SSobj, src)
GLOB.poi_list |= src
@@ -22,7 +18,6 @@
return INITIALIZE_HINT_QDEL
if(overmind)
update_icon()
- point_rate = new_rate
addtimer(CALLBACK(src, .proc/generate_announcement), 1800)
. = ..()
@@ -37,7 +32,7 @@
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
- blob_overlay.color = overmind.blob_reagent_datum.color
+ blob_overlay.color = overmind.blobstrain.color
add_overlay(blob_overlay)
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay"))
@@ -60,17 +55,13 @@
if(overmind) //we should have an overmind, but...
overmind.update_health_hud()
-/obj/structure/blob/core/Life()
+/obj/structure/blob/core/process()
if(QDELETED(src))
return
if(!overmind)
qdel(src)
- else
- if(resource_delay <= world.time)
- resource_delay = world.time + 10 // 1 second
- overmind.add_points(point_rate)
- obj_integrity = min(max_integrity, obj_integrity+core_regen)
if(overmind)
+ overmind.blobstrain.core_process()
overmind.update_health_hud()
Pulse_Area(overmind, 12, 4, 3)
for(var/obj/structure/blob/normal/B in range(1, src))
diff --git a/code/modules/antagonists/blob/blob/blobs/node.dm b/code/modules/antagonists/blob/blob/blobs/node.dm
index 23f37bca29..14fbc741c0 100644
--- a/code/modules/antagonists/blob/blob/blobs/node.dm
+++ b/code/modules/antagonists/blob/blob/blobs/node.dm
@@ -22,7 +22,7 @@
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
- blob_overlay.color = overmind.blob_reagent_datum.color
+ blob_overlay.color = overmind.blobstrain.color
add_overlay(blob_overlay)
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay"))
@@ -31,6 +31,6 @@
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/structure/blob/node/Life()
+/obj/structure/blob/node/process()
if(overmind)
Pulse_Area(overmind, 10, 3, 2)
diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm
index 481c5e6c8d..bc4e517ced 100644
--- a/code/modules/antagonists/blob/blob/blobs/shield.dm
+++ b/code/modules/antagonists/blob/blob/blobs/shield.dm
@@ -47,7 +47,7 @@
icon_state = "blob_glow"
flags_1 = CHECK_RICOCHET_1
point_return = 8
- max_integrity = 50
+ max_integrity = 100
brute_resist = 1
explosion_block = 2
diff --git a/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm b/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm
new file mode 100644
index 0000000000..8b6f4bd687
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm
@@ -0,0 +1,74 @@
+GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/blobstrain/reagent, /datum/blobstrain/multiplex))
+
+/datum/blobstrain
+ var/name
+ var/description
+ var/color = "#000000"
+ var/complementary_color = "#000000" //a color that's complementary to the normal blob color
+ var/shortdesc = null //just damage and on_mob effects, doesn't include special, blob-tile only effects
+ var/effectdesc = null //any long, blob-tile specific effects
+ var/analyzerdescdamage = "Unknown. Report this bug to a coder, or just adminhelp."
+ var/analyzerdesceffect = "N/A"
+ var/blobbernaut_message = "slams" //blobbernaut attack verb
+ var/message = "The blob strikes you" //message sent to any mob hit by the blob
+ var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt
+ var/core_regen = 2
+ var/resource_delay = 0
+ var/point_rate = 2
+ var/mob/camera/blob/overmind
+
+/datum/blobstrain/New(mob/camera/blob/new_overmind)
+ if (!istype(new_overmind))
+ stack_trace("blobstrain created without overmind")
+ overmind = new_overmind
+
+/datum/blobstrain/proc/on_gain()
+ overmind.color = complementary_color
+ for(var/BL in GLOB.blobs)
+ var/obj/structure/blob/B = BL
+ B.update_icon()
+ for(var/BLO in overmind.blob_mobs)
+ var/mob/living/simple_animal/hostile/blob/BM = BLO
+ BM.update_icons() //If it's getting a new strain, tell it what it does!
+ to_chat(BM, "Your overmind's blob strain is now:
[name]!")
+ to_chat(BM, "The
[name] strain [shortdesc ? "[shortdesc]" : "[description]"]")
+
+/datum/blobstrain/proc/on_lose()
+
+/datum/blobstrain/proc/on_sporedeath(mob/living/spore)
+
+/datum/blobstrain/proc/send_message(mob/living/M)
+ var/totalmessage = message
+ if(message_living && !issilicon(M))
+ totalmessage += message_living
+ totalmessage += "!"
+ to_chat(M, "
[totalmessage]")
+
+/datum/blobstrain/proc/core_process()
+ if(resource_delay <= world.time)
+ resource_delay = world.time + 10 // 1 second
+ overmind.add_points(point_rate)
+ overmind.blob_core.obj_integrity = min(overmind.blob_core.max_integrity, overmind.blob_core.obj_integrity+core_regen)
+
+/datum/blobstrain/proc/attack_living(var/mob/living/L) // When the blob attacks people
+ send_message(L)
+
+/datum/blobstrain/proc/blobbernaut_attack(mob/living/L) // When this blob's blobbernaut attacks people
+
+/datum/blobstrain/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
+ return coefficient*damage
+
+/datum/blobstrain/proc/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this
+ return
+
+/datum/blobstrain/proc/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
+ return
+
+/datum/blobstrain/proc/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this
+ return 1 //return 0 to ignore damage
+
+/datum/blobstrain/proc/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this
+ return
+
+/datum/blobstrain/proc/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this
+ return
diff --git a/code/modules/antagonists/blob/blob/blobstrains/_reagent.dm b/code/modules/antagonists/blob/blob/blobstrains/_reagent.dm
new file mode 100644
index 0000000000..f47c3b3378
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/_reagent.dm
@@ -0,0 +1,33 @@
+/datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones
+ var/datum/reagent/reagent
+
+/datum/blobstrain/reagent/New(mob/camera/blob/new_overmind)
+ . = ..()
+ reagent = new reagent()
+
+
+/datum/blobstrain/reagent/attack_living(var/mob/living/L)
+ var/mob_protection = L.get_permeability_protection()
+ reagent.reaction_mob(L, VAPOR, 25, 1, mob_protection, overmind)
+ send_message(L)
+
+/datum/blobstrain/reagent/blobbernaut_attack(mob/living/L)
+ var/mob_protection = L.get_permeability_protection()
+ reagent.reaction_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
+
+/datum/blobstrain/reagent/on_sporedeath(mob/living/spore)
+ spore.reagents.add_reagent(reagent.type, 10)
+
+// These can only be applied by blobs. They are what (reagent) blobs are made out of.
+/datum/reagent/blob
+ name = "Unknown"
+ description = "shouldn't exist and you should adminhelp immediately."
+ color = "#FFFFFF"
+ taste_description = "bad code and slime"
+ can_synth = FALSE
+
+
+/datum/reagent/blob/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
+ return 0 //the dead, and blob mobs, don't cause reactions
+ return round(reac_volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume.
diff --git a/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm
new file mode 100644
index 0000000000..97b974e28f
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm
@@ -0,0 +1,41 @@
+
+//sets you on fire, does burn damage, explodes into flame when burnt, weak to water
+/datum/blobstrain/reagent/blazing_oil
+ name = "Blazing Oil"
+ description = "will do medium burn damage and set targets on fire."
+ effectdesc = "will also release bursts of flame when burnt, but takes damage from water."
+ analyzerdescdamage = "Does medium burn damage and sets targets on fire."
+ analyzerdesceffect = "Releases fire when burnt, but takes damage from water and other extinguishing liquids."
+ color = "#B68D00"
+ complementary_color = "#BE5532"
+ blobbernaut_message = "splashes"
+ message = "The blob splashes you with burning oil"
+ message_living = ", and you feel your skin char and melt"
+ reagent = /datum/reagent/blob/blazing_oil
+
+/datum/blobstrain/reagent/blazing_oil/extinguish_reaction(obj/structure/blob/B)
+ B.take_damage(1.5, BURN, "energy")
+
+/datum/blobstrain/reagent/blazing_oil/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if(damage_type == BURN && damage_flag != "energy")
+ for(var/turf/open/T in range(1, B))
+ var/obj/structure/blob/C = locate() in T
+ if(!(C && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) && prob(80))
+ new /obj/effect/hotspot(T)
+ if(damage_flag == "fire")
+ return 0
+ return ..()
+
+/datum/reagent/blob/blazing_oil
+ name = "Blazing Oil"
+ taste_description = "burning oil"
+ color = "#B68D00"
+
+/datum/reagent/blob/blazing_oil/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.adjust_fire_stacks(round(reac_volume/10))
+ M.IgniteMob()
+ if(M)
+ M.apply_damage(0.8*reac_volume, BURN)
+ if(iscarbon(M))
+ M.emote("scream")
diff --git a/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm b/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm
new file mode 100644
index 0000000000..9b8edcd0e5
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm
@@ -0,0 +1,32 @@
+//does brute, burn, and toxin damage, and cools targets down
+/datum/blobstrain/reagent/cryogenic_poison
+ name = "Cryogenic Poison"
+ description = "will inject targets with a freezing poison that does high damage over time."
+ analyzerdescdamage = "Injects targets with a freezing poison that will gradually solidify the target's internal organs."
+ color = "#8BA6E9"
+ complementary_color = "#7D6EB4"
+ blobbernaut_message = "injects"
+ message = "The blob stabs you"
+ message_living = ", and you feel like your insides are solidifying"
+ reagent = /datum/reagent/blob/cryogenic_poison
+
+/datum/reagent/blob/cryogenic_poison
+ name = "Cryogenic Poison"
+ description = "will inject targets with a freezing poison that does high damage over time."
+ color = "#8BA6E9"
+ taste_description = "brain freeze"
+
+/datum/reagent/blob/cryogenic_poison/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ if(M.reagents)
+ M.reagents.add_reagent("frostoil", 0.3*reac_volume)
+ M.reagents.add_reagent("ice", 0.3*reac_volume)
+ M.reagents.add_reagent("cryogenic_poison", 0.3*reac_volume)
+ M.apply_damage(0.2*reac_volume, BRUTE)
+
+/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/M)
+ M.adjustBruteLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
+ M.adjustFireLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
+ M.adjustToxLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
+ . = 1
+ ..()
diff --git a/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm b/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm
new file mode 100644
index 0000000000..0e665603c9
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm
@@ -0,0 +1,33 @@
+//does burn damage and EMPs, slightly fragile
+/datum/blobstrain/reagent/electromagnetic_web
+ name = "Electromagnetic Web"
+ color = "#83ECEC"
+ complementary_color = "#EC8383"
+ reagent = /datum/reagent/blob/electromagnetic_web
+
+/datum/blobstrain/reagent/electromagnetic_web/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if(damage_type == BRUTE) //take full brute
+ switch(B.brute_resist)
+ if(0.5)
+ return damage * 2
+ if(0.25)
+ return damage * 4
+ if(0.1)
+ return damage * 10
+ return damage * 1.25 //a laser will do 25 damage, which will kill any normal blob
+
+/datum/blobstrain/reagent/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag)
+ if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
+ empulse(B.loc, 1, 3) //less than screen range, so you can stand out of range to avoid it
+
+/datum/reagent/blob/electromagnetic_web
+ name = "Electromagnetic Web"
+ taste_description = "pop rocks"
+ color = "#83ECEC"
+
+/datum/reagent/blob/electromagnetic_web/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ if(prob(reac_volume*2))
+ M.emp_act(EMP_LIGHT)
+ if(M)
+ M.apply_damage(reac_volume, BURN)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/energized_jelly.dm b/code/modules/antagonists/blob/blob/blobstrains/energized_jelly.dm
new file mode 100644
index 0000000000..66ce3c303d
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/energized_jelly.dm
@@ -0,0 +1,34 @@
+//does tons of oxygen damage and a little stamina, immune to tesla bolts, weak to EMP
+/datum/blobstrain/reagent/energized_jelly
+ name = "Energized Jelly"
+ description = "will cause low stamina and high oxygen damage, and cause targets to be unable to breathe."
+ effectdesc = "will also conduct electricity, but takes damage from EMPs."
+ analyzerdescdamage = "Does low stamina damage, high oxygen damage, and prevents targets from breathing."
+ analyzerdesceffect = "Is immune to electricity and will easily conduct it, but is weak to EMPs."
+ color = "#EFD65A"
+ complementary_color = "#00E5B1"
+ reagent = /datum/reagent/blob/energized_jelly
+
+/datum/blobstrain/reagent/energized_jelly/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && B.obj_integrity - damage <= 0 && prob(10))
+ do_sparks(rand(2, 4), FALSE, B)
+ return ..()
+
+/datum/blobstrain/reagent/energized_jelly/tesla_reaction(obj/structure/blob/B, power)
+ return 0
+
+/datum/blobstrain/reagent/energized_jelly/emp_reaction(obj/structure/blob/B, severity)
+ var/damage = rand(30, 50) - severity * rand(10, 15)
+ B.take_damage(damage, BURN, "energy")
+
+/datum/reagent/blob/energized_jelly
+ name = "Energized Jelly"
+ taste_description = "gelatin"
+ color = "#EFD65A"
+
+/datum/reagent/blob/energized_jelly/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.losebreath += round(0.2*reac_volume)
+ M.adjustStaminaLoss(0.4*reac_volume)
+ if(M)
+ M.apply_damage(0.6*reac_volume, OXY)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm
new file mode 100644
index 0000000000..f8fd2e2f0d
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm
@@ -0,0 +1,40 @@
+//does aoe brute damage when hitting targets, is immune to explosions
+/datum/blobstrain/reagent/explosive_lattice
+ name = "Explosive Lattice"
+ description = "will do brute damage in an area around targets."
+ effectdesc = "will also resist explosions, but takes increased damage from fire and other energy sources."
+ analyzerdescdamage = "Does medium brute damage and causes damage to everyone near its targets."
+ analyzerdesceffect = "Is highly resistant to explosions, but takes increased damage from fire and other energy sources."
+ color = "#8B2500"
+ complementary_color = "#00668B"
+ blobbernaut_message = "blasts"
+ message = "The blob blasts you"
+ reagent = /datum/reagent/blob/explosive_lattice
+
+/datum/blobstrain/reagent/explosive_lattice/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if(damage_flag == "bomb")
+ return 0
+ else if(damage_flag != "melee" && damage_flag != "bullet" && damage_flag != "laser")
+ return damage * 1.5
+ return ..()
+
+/datum/reagent/blob/explosive_lattice
+ name = "Explosive Lattice"
+ taste_description = "the bomb"
+ color = "#8B2500"
+
+/datum/reagent/blob/explosive_lattice/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ var/initial_volume = reac_volume
+ reac_volume = ..()
+ if(reac_volume >= 10) //if it's not a spore cloud, bad time incoming
+ var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M))
+ E.alpha = 150
+ for(var/mob/living/L in orange(get_turf(M), 1))
+ if(ROLE_BLOB in L.faction) //no friendly fire
+ continue
+ var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
+ L.apply_damage(0.4*aoe_volume, BRUTE)
+ if(M)
+ M.apply_damage(0.6*reac_volume, BRUTE)
+ else
+ M.apply_damage(0.6*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/multiplex.dm b/code/modules/antagonists/blob/blob/blobstrains/multiplex.dm
new file mode 100644
index 0000000000..191da6c51f
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/multiplex.dm
@@ -0,0 +1,40 @@
+/datum/blobstrain/multiplex
+ var/list/blobstrains
+ var/typeshare
+
+/datum/blobstrain/multiplex/New(mob/camera/blob/new_overmind, list/blobstrains)
+ . = ..()
+ for (var/bt in blobstrains)
+ if (ispath(bt, /datum/blobstrain))
+ src.blobstrains += new bt(overmind)
+ else if (istype(bt, /datum/blobstrain))
+ var/datum/blobstrain/bts = bt
+ bts.overmind = overmind
+ src.blobstrains += bt
+ typeshare = (0.8 * length(src.blobstrains)) - (length(src.blobstrains)-1) // 1 is 80%, 2 are 60% etc
+
+/datum/blobstrain/multiplex/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
+ for (var/datum/blobstrain/bt in blobstrains)
+ . += bt.damage_reaction(B, damage, damage_type, damage_flag, coefficient*typeshare)
+
+/datum/blobstrain/multiplex/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this
+ for (var/datum/blobstrain/bt in blobstrains)
+ . += bt.death_reaction(B, damage_flag, coefficient*typeshare)
+
+/datum/blobstrain/multiplex/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
+ for (var/datum/blobstrain/bt in blobstrains)
+ . += bt.expand_reaction(B, newB, T, O, coefficient*typeshare)
+
+/datum/blobstrain/multiplex/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this
+ for (var/datum/blobstrain/bt in blobstrains)
+ . += bt.tesla_reaction(B, power, coefficient*typeshare)
+ if (prob(. / length(blobstrains) * 100))
+ return 1
+
+/datum/blobstrain/multiplex/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this
+ for (var/datum/blobstrain/bt in blobstrains)
+ . += bt.extinguish_reaction(B, coefficient*typeshare)
+
+/datum/blobstrain/multiplex/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this
+ for (var/datum/blobstrain/bt in blobstrains)
+ . += bt.emp_reaction(B, severity, coefficient*typeshare)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm b/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm
new file mode 100644
index 0000000000..fac3470c7a
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm
@@ -0,0 +1,38 @@
+//does massive brute and burn damage, but can only expand manually
+/datum/blobstrain/reagent/networked_fibers
+ name = "Networked Fibers"
+ description = "will do high brute and burn damage and will generate resources quicker, but can only expand manually."
+ shortdesc = "will do high brute and burn damage."
+ effectdesc = "will move your core when manually expanding near it."
+ analyzerdescdamage = "Does high brute and burn damage."
+ analyzerdesceffect = "Is highly mobile and generates resources rapidly."
+ color = "#CDC0B0"
+ complementary_color = "#FFF68F"
+ reagent = /datum/reagent/blob/networked_fibers
+
+/datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
+ if(!O && newB.overmind)
+ if(!istype(B, /obj/structure/blob/node))
+ newB.overmind.add_points(1)
+ qdel(newB)
+ else
+ var/area/A = get_area(T)
+ if(!isspaceturf(T) && !istype(A, /area/shuttle))
+ for(var/obj/structure/blob/core/C in range(1, newB))
+ if(C.overmind == O)
+ newB.forceMove(get_turf(C))
+ C.forceMove(T)
+ C.setDir(get_dir(newB, C))
+ O.add_points(1)
+
+//does massive brute and burn damage, but can only expand manually
+/datum/reagent/blob/networked_fibers
+ name = "Networked Fibers"
+ taste_description = "efficiency"
+ color = "#CDC0B0"
+
+/datum/reagent/blob/networked_fibers/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.apply_damage(0.6*reac_volume, BRUTE)
+ if(M)
+ M.apply_damage(0.6*reac_volume, BURN)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm b/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm
new file mode 100644
index 0000000000..6a984e66a2
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm
@@ -0,0 +1,51 @@
+//does low brute damage, oxygen damage, and stamina damage and wets tiles when damaged
+/datum/blobstrain/reagent/pressurized_slime
+ name = "Pressurized Slime"
+ description = "will do low brute, oxygen, and stamina damage, and wet tiles under targets."
+ effectdesc = "will also wet tiles near blobs that are attacked or killed."
+ analyzerdescdamage = "Does low brute damage, low oxygen damage, drains stamina, and wets tiles under targets, extinguishing them."
+ analyzerdesceffect = "When attacked or killed, lubricates nearby tiles, extinguishing anything on them."
+ color = "#AAAABB"
+ complementary_color = "#BBBBAA"
+ blobbernaut_message = "emits slime at"
+ message = "The blob splashes into you"
+ message_living = ", and you gasp for breath"
+ reagent = /datum/reagent/blob/pressurized_slime
+
+/datum/blobstrain/reagent/pressurized_slime/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") || damage_type != BURN)
+ extinguisharea(B, damage)
+ return ..()
+
+/datum/blobstrain/reagent/pressurized_slime/death_reaction(obj/structure/blob/B, damage_flag)
+ if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
+ B.visible_message("
The blob ruptures, spraying the area with liquid!")
+ extinguisharea(B, 50)
+
+/datum/blobstrain/reagent/pressurized_slime/proc/extinguisharea(obj/structure/blob/B, probchance)
+ for(var/turf/open/T in range(1, B))
+ if(prob(probchance))
+ T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
+ for(var/obj/O in T)
+ O.extinguish()
+ for(var/mob/living/L in T)
+ L.adjust_fire_stacks(-2.5)
+ L.ExtinguishMob()
+
+/datum/reagent/blob/pressurized_slime
+ name = "Pressurized Slime"
+ taste_description = "a sponge"
+ color = "#AAAABB"
+
+/datum/reagent/blob/pressurized_slime/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ var/turf/open/T = get_turf(M)
+ if(istype(T) && prob(reac_volume))
+ T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
+ M.adjust_fire_stacks(-(reac_volume / 10))
+ M.ExtinguishMob()
+ M.apply_damage(0.4*reac_volume, BRUTE)
+ if(M)
+ M.apply_damage(0.4*reac_volume, OXY)
+ if(M)
+ M.adjustStaminaLoss(0.2*reac_volume)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/reactive_spines.dm b/code/modules/antagonists/blob/blob/blobstrains/reactive_spines.dm
new file mode 100644
index 0000000000..fca56d1402
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/reactive_spines.dm
@@ -0,0 +1,30 @@
+//does brute damage through armor and bio resistance
+/datum/blobstrain/reagent/reactive_spines
+ name = "Reactive Spines"
+ description = "will do medium brute damage through armor and bio resistance."
+ effectdesc = "will also react when attacked with brute damage, attacking all near the attacked blob."
+ analyzerdescdamage = "Does medium brute damage, ignoring armor and bio resistance."
+ analyzerdesceffect = "When attacked with brute damage, will lash out, attacking everything near it."
+ color = "#9ACD32"
+ complementary_color = "#FFA500"
+ blobbernaut_message = "stabs"
+ message = "The blob stabs you"
+ reagent = /datum/reagent/blob/reactive_spines
+
+/datum/blobstrain/reagent/reactive_spines/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if(damage && damage_type == BRUTE && B.obj_integrity - damage > 0) //is there any damage, is it brute, and will we be alive
+ if(damage_flag == "melee")
+ B.visible_message("
The blob retaliates, lashing out!")
+ for(var/atom/A in range(1, B))
+ A.blob_act(B)
+ return ..()
+
+/datum/reagent/blob/reactive_spines
+ name = "Reactive Spines"
+ taste_description = "rock"
+ color = "#9ACD32"
+
+/datum/reagent/blob/reactive_spines/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
+ return 0 //the dead, and blob mobs, don't cause reactions
+ M.adjustBruteLoss(0.8*reac_volume)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/regenerative_materia.dm b/code/modules/antagonists/blob/blob/blobstrains/regenerative_materia.dm
new file mode 100644
index 0000000000..25511e01ff
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/regenerative_materia.dm
@@ -0,0 +1,33 @@
+//does toxin damage, hallucination, targets think they're not hurt at all
+/datum/blobstrain/reagent/regenerative_materia
+ name = "Regenerative Materia"
+ description = "will do toxin damage and cause targets to believe they are fully healed."
+ analyzerdescdamage = "Does toxin damage and injects a toxin that causes the target to believe they are fully healed."
+ color = "#C8A5DC"
+ complementary_color = "#CD7794"
+ message_living = ", and you feel
alive"
+ reagent = /datum/reagent/blob/regenerative_materia
+
+/datum/reagent/blob/regenerative_materia
+ name = "Regenerative Materia"
+ taste_description = "heaven"
+ color = "#C8A5DC"
+
+/datum/reagent/blob/regenerative_materia/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.adjust_drugginess(reac_volume)
+ if(M.reagents)
+ M.reagents.add_reagent("regenerative_materia", 0.2*reac_volume)
+ M.reagents.add_reagent("spore", 0.2*reac_volume)
+ M.apply_damage(0.7*reac_volume, TOX)
+
+/datum/reagent/blob/regenerative_materia/on_mob_life(mob/living/carbon/C)
+ C.adjustToxLoss(1*REAGENTS_EFFECT_MULTIPLIER)
+ C.hal_screwyhud = SCREWYHUD_HEALTHY //fully healed, honest
+ ..()
+
+/datum/reagent/blob/regenerative_materia/on_mob_delete(mob/living/M)
+ if(iscarbon(M))
+ var/mob/living/carbon/N = M
+ N.hal_screwyhud = 0
+ ..()
diff --git a/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm b/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm
new file mode 100644
index 0000000000..00743c671e
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm
@@ -0,0 +1,34 @@
+/datum/blobstrain/reagent/replicating_foam
+ description = "will do medium brute damage and occasionally expand again when expanding."
+ shortdesc = "will do medium brute damage."
+ effectdesc = "will also expand when attacked with burn damage, but takes more brute damage."
+ color = "#7B5A57"
+ complementary_color = "#57787B"
+ analyzerdescdamage = "Does medium brute damage."
+ analyzerdesceffect = "Expands when attacked with burn damage, will occasionally expand again when expanding, and is fragile to brute damage."
+ reagent = /datum/reagent/blob/replicating_foam
+
+
+/datum/blobstrain/reagent/replicating_foam/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if(damage_type == BRUTE)
+ damage = damage * 2
+ else if(damage_type == BURN && damage > 0 && B.obj_integrity - damage > 0 && prob(60))
+ var/obj/structure/blob/newB = B.expand(null, null, 0)
+ if(newB)
+ newB.obj_integrity = B.obj_integrity - damage
+ newB.update_icon()
+ return ..()
+
+
+/datum/blobstrain/reagent/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
+ if(prob(30))
+ newB.expand(null, null, 0) //do it again!
+
+/datum/reagent/blob/replicating_foam
+ name = "Replicating Foam"
+ taste_description = "duplication"
+ color = "#7B5A57"
+
+/datum/reagent/blob/replicating_foam/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.apply_damage(0.7*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm b/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm
new file mode 100644
index 0000000000..dbb3d6fb9b
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm
@@ -0,0 +1,35 @@
+//does brute damage, shifts away when damaged
+/datum/blobstrain/reagent/shifting_fragments
+ description = "will do medium brute damage."
+ effectdesc = "will also cause blob parts to shift away when attacked."
+ analyzerdescdamage = "Does medium brute damage."
+ analyzerdesceffect = "When attacked, may shift away from the attacker."
+ color = "#C8963C"
+ complementary_color = "#3C6EC8"
+ reagent = /datum/reagent/blob/shifting_fragments
+
+/datum/blobstrain/reagent/shifting_fragments/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
+ if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25)))
+ newB.forceMove(get_turf(B))
+ B.forceMove(T)
+
+/datum/blobstrain/reagent/shifting_fragments/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage > 0 && B.obj_integrity - damage > 0 && prob(60-damage))
+ var/list/blobstopick = list()
+ for(var/obj/structure/blob/OB in orange(1, B))
+ if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind.blobstrain.type == B.overmind.blobstrain.type)
+ blobstopick += OB //as long as the blob picked is valid; ie, a normal or shield blob that has the same chemical as we do, we can swap with it
+ if(blobstopick.len)
+ var/obj/structure/blob/targeted = pick(blobstopick) //randomize the blob chosen, because otherwise it'd tend to the lower left
+ var/turf/T = get_turf(targeted)
+ targeted.forceMove(get_turf(B))
+ B.forceMove(T) //swap the blobs
+ return ..()
+
+/datum/reagent/blob/shifting_fragments
+ name = "Shifting Fragments"
+ color = "#C8963C"
+
+/datum/reagent/blob/shifting_fragments/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.apply_damage(0.7*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm
new file mode 100644
index 0000000000..d58fb5b37d
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm
@@ -0,0 +1,38 @@
+//does brute damage, bonus damage for each nearby blob, and spreads damage out
+/datum/blobstrain/reagent/synchronous_mesh
+ name = "Synchronous Mesh"
+ description = "will do massively increased brute damage for each blob near the target."
+ effectdesc = "will also spread damage between each blob near the attacked blob."
+ analyzerdescdamage = "Does brute damage, increasing for each blob near the target."
+ analyzerdesceffect = "When attacked, spreads damage between all blobs near the attacked blob."
+ color = "#65ADA2"
+ complementary_color = "#AD6570"
+ blobbernaut_message = "synchronously strikes"
+ message = "The blobs strike you"
+
+/datum/blobstrain/reagent/synchronous_mesh/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") //the cause isn't fire or bombs, so split the damage
+ var/damagesplit = 1 //maximum split is 9, reducing the damage each blob takes to 11% but doing that damage to 9 blobs
+ for(var/obj/structure/blob/C in orange(1, B))
+ if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it
+ damagesplit += 1
+ for(var/obj/structure/blob/C in orange(1, B))
+ if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes
+ C.take_damage(damage/damagesplit, CLONE, 0, 0)
+ return damage / damagesplit
+ else
+ return damage * 1.25
+
+/datum/reagent/blob/synchronous_mesh
+ name = "Synchronous Mesh"
+ taste_description = "toxic mold"
+ color = "#65ADA2"
+
+/datum/reagent/blob/synchronous_mesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.apply_damage(0.2*reac_volume, BRUTE)
+ if(M && reac_volume)
+ for(var/obj/structure/blob/B in range(1, M)) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume
+ if(M)
+ B.blob_attack_animation(M) //show them they're getting a bad time
+ M.apply_damage(0.3*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/zombifying_pods.dm b/code/modules/antagonists/blob/blob/blobstrains/zombifying_pods.dm
new file mode 100644
index 0000000000..b2bb9d5115
--- /dev/null
+++ b/code/modules/antagonists/blob/blob/blobstrains/zombifying_pods.dm
@@ -0,0 +1,46 @@
+//kills sleeping targets and turns them into blob zombies, produces fragile spores when killed or on expanding
+/datum/blobstrain/reagent/zombifying_pods
+ name = "Zombifying Pods"
+ description = "will do very low toxin damage and harvest sleeping targets for additional resources and a blob zombie."
+ effectdesc = "will also produce fragile spores when killed and on expanding."
+ shortdesc = "will do very low toxin damage and harvest sleeping targets for additional resources(for your overmind) and a blob zombie."
+ analyzerdescdamage = "Does very low toxin damage and kills unconscious humans, turning them into blob zombies."
+ analyzerdesceffect = "Produces spores when expanding and when killed."
+ color = "#E88D5D"
+ complementary_color = "#823ABB"
+ message_living = ", and you feel tired"
+ reagent = /datum/reagent/blob/zombifying_pods
+
+/datum/blobstrain/reagent/zombifying_pods/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
+ if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage <= 20 && B.obj_integrity - damage <= 0 && prob(30)) //if the cause isn't fire or a bomb, the damage is less than 21, we're going to die from that damage, 20% chance of a shitty spore.
+ B.visible_message("
A spore floats free of the blob!")
+ var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(B.loc)
+ BS.overmind = B.overmind
+ BS.update_icons()
+ B.overmind.blob_mobs.Add(BS)
+ return ..()
+
+/datum/blobstrain/reagent/zombifying_pods/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
+ if(prob(10))
+ var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(T)
+ BS.overmind = B.overmind
+ BS.update_icons()
+ newB.overmind.blob_mobs.Add(BS)
+
+/datum/reagent/blob/zombifying_pods
+ name = "Zombifying Pods"
+ color = "#E88D5D"
+
+/datum/reagent/blob/zombifying_pods/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
+ reac_volume = ..()
+ M.apply_damage(0.6*reac_volume, TOX)
+ if(O && ishuman(M) && M.stat == UNCONSCIOUS)
+ M.death() //sleeping in a fight? bad plan.
+ var/points = rand(5, 10)
+ var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(get_turf(M))
+ BS.overmind = O
+ BS.update_icons()
+ O.blob_mobs.Add(BS)
+ BS.Zombify(M)
+ O.add_points(points)
+ to_chat(O, "
Gained [points] resources from the zombification of [M].")
diff --git a/code/modules/antagonists/blob/blob/overmind.dm b/code/modules/antagonists/blob/blob/overmind.dm
index 18e13118fe..aed64e4039 100644
--- a/code/modules/antagonists/blob/blob/overmind.dm
+++ b/code/modules/antagonists/blob/blob/overmind.dm
@@ -20,25 +20,25 @@ GLOBAL_LIST_EMPTY(blob_nodes)
pass_flags = PASSBLOB
faction = list(ROLE_BLOB)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- call_life = TRUE
hud_type = /datum/hud/blob_overmind
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 250
var/last_attack = 0
- var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
+ var/datum/blobstrain/blobstrain
var/list/blob_mobs = list()
var/list/resource_blobs = list()
- var/free_chem_rerolls = 1 //one free chemical reroll
+ var/free_strain_rerolls = 1 //one free strain reroll
+ var/last_reroll_time = 0 //time since we last rerolled, used to give free rerolls
var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
var/placed = 0
- var/base_point_rate = 2 //for blob core placement
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
var/list/blobs_legit = list()
var/max_count = 0 //The biggest it got before death
var/blobwincount = 400
var/victory_in_progress = FALSE
+ var/rerolling = FALSE
/mob/camera/blob/Initialize(mapload, starting_points = 60)
validate_location()
@@ -50,13 +50,14 @@ GLOBAL_LIST_EMPTY(blob_nodes)
name = new_name
real_name = new_name
last_attack = world.time
- var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob)))
- blob_reagent_datum = new BC
- color = blob_reagent_datum.complementary_color
+ var/datum/blobstrain/BS = pick(GLOB.valid_blobstrains)
+ set_strain(BS)
+ color = blobstrain.complementary_color
if(blob_core)
blob_core.update_icon()
SSshuttle.registerHostileEnvironment(src)
- .= ..()
+ . = ..()
+ START_PROCESSING(SSobj, src)
/mob/camera/blob/proc/validate_location()
var/turf/T = get_turf(src)
@@ -70,13 +71,28 @@ GLOBAL_LIST_EMPTY(blob_nodes)
CRASH("No blobspawnpoints and blob spawned in nullspace.")
forceMove(T)
+/mob/camera/blob/proc/set_strain(datum/blobstrain/new_strain)
+ if (ispath(new_strain))
+ var/hadstrain = FALSE
+ if (istype(blobstrain))
+ blobstrain.on_lose()
+ qdel(blobstrain)
+ hadstrain = TRUE
+ blobstrain = new new_strain(src)
+ blobstrain.on_gain()
+ if (hadstrain)
+ to_chat(src, "Your strain is now:
[blobstrain.name]!")
+ to_chat(src, "The
[blobstrain.name] strain [blobstrain.description]")
+ if(blobstrain.effectdesc)
+ to_chat(src, "The
[blobstrain.name] strain [blobstrain.effectdesc]")
+
/mob/camera/blob/proc/is_valid_turf(turf/T)
var/area/A = get_area(T)
if((A && !A.blob_allowed) || !T || !is_station_level(T.z) || isspaceturf(T))
return FALSE
return TRUE
-/mob/camera/blob/Life()
+/mob/camera/blob/process()
if(!blob_core)
if(!placed)
if(manualplace_min_time && world.time >= manualplace_min_time)
@@ -84,7 +100,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
to_chat(src, "
You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].")
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
- place_blob_core(base_point_rate, 1)
+ place_blob_core(1)
else
qdel(src)
else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
@@ -94,11 +110,12 @@ GLOBAL_LIST_EMPTY(blob_nodes)
max_blob_points = INFINITY
blob_points = INFINITY
addtimer(CALLBACK(src, .proc/victory), 450)
+ else if(!free_strain_rerolls && (last_reroll_time + BLOB_REROLL_TIME
You have gained another free strain re-roll.")
+ free_strain_rerolls = 1
if(!victory_in_progress && max_count < blobs_legit.len)
max_count = blobs_legit.len
- ..()
-
/mob/camera/blob/proc/victory()
sound_to_playing_players('sound/machines/alarm.ogg')
@@ -129,7 +146,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
continue
if(!A.blob_allowed)
continue
- A.color = blob_reagent_datum.color
+ A.color = blobstrain.color
A.name = "blob"
A.icon = 'icons/mob/blob.dmi'
A.icon_state = "blob_shield"
@@ -162,6 +179,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
blobs_legit = null
SSshuttle.clearHostileEnvironment(src)
+ STOP_PROCESSING(SSobj, src)
return ..()
@@ -173,9 +191,9 @@ GLOBAL_LIST_EMPTY(blob_nodes)
add_points(0)
/mob/camera/blob/examine(mob/user)
- . = ..()
- if(blob_reagent_datum)
- . += "Its chemical is [blob_reagent_datum.name]."
+ ..()
+ if(blobstrain)
+ to_chat(user, "Its strain is [blobstrain.name].")
/mob/camera/blob/update_health_hud()
if(blob_core)
@@ -214,7 +232,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
src.log_talk(message, LOG_SAY)
var/message_a = say_quote(message)
- var/rendered = "\[Blob Telepathy\] [name]([blob_reagent_datum.name]) [message_a]"
+ var/rendered = "\[Blob Telepathy\] [name]([blobstrain.name]) [message_a]"
for(var/mob/M in GLOB.mob_list)
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
@@ -233,8 +251,8 @@ GLOBAL_LIST_EMPTY(blob_nodes)
stat(null, "Core Health: [blob_core.obj_integrity]")
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
stat(null, "Blobs to Win: [blobs_legit.len]/[blobwincount]")
- if(free_chem_rerolls)
- stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
+ if(free_strain_rerolls)
+ stat(null, "You have [free_strain_rerolls] Free Strain Reroll\s Remaining")
if(!placed)
if(manualplace_min_time)
stat(null, "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]")
diff --git a/code/modules/antagonists/blob/blob/powers.dm b/code/modules/antagonists/blob/blob/powers.dm
index fe492831b8..73d0c97476 100644
--- a/code/modules/antagonists/blob/blob/powers.dm
+++ b/code/modules/antagonists/blob/blob/powers.dm
@@ -7,7 +7,7 @@
// Power verbs
-/mob/camera/blob/proc/place_blob_core(point_rate, placement_override , pop_override = FALSE)
+/mob/camera/blob/proc/place_blob_core(placement_override, pop_override = FALSE)
if(placed && placement_override != -1)
return 1
if(!placement_override)
@@ -47,7 +47,7 @@
if(placed && blob_core)
blob_core.forceMove(loc)
else
- var/obj/structure/blob/core/core = new(get_turf(src), src, point_rate, 1)
+ var/obj/structure/blob/core/core = new(get_turf(src), src, 1)
core.overmind = src
blobs_legit += src
blob_core = core
@@ -71,13 +71,13 @@
var/list/nodes = list()
for(var/i in 1 to GLOB.blob_nodes.len)
var/obj/structure/blob/node/B = GLOB.blob_nodes[i]
- nodes["Blob Node #[i] ([B.overmind ? "[B.overmind.blob_reagent_datum.name]":"No Chemical"])"] = B
+ nodes["Blob Node #[i] ([get_area_name(B)])"] = B
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
var/obj/structure/blob/node/chosen_node = nodes[node_name]
if(chosen_node)
forceMove(chosen_node.loc)
-/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
+/mob/camera/blob/proc/createSpecial(price, blobstrain, nearEquals, needsNode, turf/T)
if(!T)
T = get_turf(src)
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
@@ -93,12 +93,12 @@
return //handholdotron 2000
if(nearEquals)
for(var/obj/structure/blob/L in orange(nearEquals, T))
- if(L.type == blobType)
+ if(L.type == blobstrain)
to_chat(src, "There is a similar blob nearby, move more than [nearEquals] tiles away from it!")
return
if(!can_buy(price))
return
- var/obj/structure/blob/N = B.change_to(blobType, src)
+ var/obj/structure/blob/N = B.change_to(blobstrain, src)
return N
/mob/camera/blob/verb/toggle_node_req()
@@ -120,12 +120,13 @@
/mob/camera/blob/proc/create_shield(turf/T)
var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T
if(S)
- if(!can_buy(15))
+ if(!can_buy(BLOB_REFLECTOR_COST))
return
if(S.obj_integrity < S.max_integrity * 0.5)
+ add_points(BLOB_REFLECTOR_COST)
to_chat(src, "This shield blob is too damaged to be modified properly!")
return
- to_chat(src, "You secrete a reflective ooze over the shield blob, allowing it to reflect projectiles at the cost of reduced intregrity.")
+ to_chat(src, "You secrete a reflective ooze over the shield blob, allowing it to reflect projectiles at the cost of reduced integrity.")
S.change_to(/obj/structure/blob/shield/reflective, src)
else
createSpecial(15, /obj/structure/blob/shield, 0, 0, T)
@@ -166,7 +167,9 @@
if(!can_buy(40))
return
- var/list/mob/candidates = pollGhostCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
+ B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory.
+ to_chat(src, "You attempt to produce a blobbernaut.")
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now.
B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health
B.obj_integrity = min(B.obj_integrity, B.max_integrity)
@@ -188,8 +191,8 @@
to_chat(blobber, "You are a blobbernaut!")
to_chat(blobber, "You are powerful, hard to kill, and slowly regenerate near nodes and cores, but will slowly die if not near the blob or if the factory that made you is killed.")
to_chat(blobber, "You can communicate with other blobbernauts and overminds via :b")
- to_chat(blobber, "Your overmind's blob reagent is: [blob_reagent_datum.name]!")
- to_chat(blobber, "The [blob_reagent_datum.name] reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]")
+ to_chat(blobber, "Your overmind's blob reagent is: [blobstrain.name]!")
+ to_chat(blobber, "The [blobstrain.name] reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")
else
to_chat(src, "You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.")
add_points(40)
@@ -244,8 +247,8 @@
/mob/camera/blob/verb/expand_blob_power()
set category = "Blob"
- set name = "Expand/Attack Blob (4)"
- set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects."
+ set name = "Expand/Attack Blob ([BLOB_SPREAD_COST])"
+ set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects and refunding [BLOB_ATTACK_REFUND] points."
var/turf/T = get_turf(src)
expand_blob(T)
@@ -258,23 +261,21 @@
if(!possibleblobs.len)
to_chat(src, "There is no blob adjacent to the target tile!")
return
- if(can_buy(4))
+ if(can_buy(BLOB_SPREAD_COST))
var/attacksuccess = FALSE
for(var/mob/living/L in T)
if(ROLE_BLOB in L.faction) //no friendly/dead fire
continue
if(L.stat != DEAD)
attacksuccess = TRUE
- var/mob_protection = L.get_permeability_protection()
- blob_reagent_datum.reaction_mob(L, VAPOR, 25, 1, mob_protection, src)
- blob_reagent_datum.send_message(L)
+ blobstrain.attack_living(L)
var/obj/structure/blob/B = locate() in T
if(B)
- if(attacksuccess) //if we successfully attacked a turf with a blob on it, don't refund shit
+ if(attacksuccess) //if we successfully attacked a turf with a blob on it, only give an attack refund
B.blob_attack_animation(T, src)
else
to_chat(src, "There is a blob there!")
- add_points(4) //otherwise, refund all of the cost
+ add_points(BLOB_SPREAD_COST) //otherwise, refund all of the cost
else
var/list/cardinalblobs = list()
var/list/diagonalblobs = list()
@@ -287,14 +288,15 @@
var/obj/structure/blob/OB
if(cardinalblobs.len)
OB = pick(cardinalblobs)
- OB.expand(T, src)
+ if(!OB.expand(T, src))
+ add_points(BLOB_ATTACK_REFUND) //assume it's attacked SOMETHING, possibly a structure
else
OB = pick(diagonalblobs)
if(attacksuccess)
OB.blob_attack_animation(T, src)
playsound(OB, 'sound/effects/splat.ogg', 50, 1)
else
- add_points(4) //if we're attacking diagonally and didn't hit anything, refund
+ add_points(BLOB_SPREAD_COST) //if we're attacking diagonally and didn't hit anything, refund
if(attacksuccess)
last_attack = world.time + CLICK_CD_MELEE
else
@@ -331,41 +333,38 @@
if(BM.stat == CONSCIOUS)
BM.say(speak_text)
-/mob/camera/blob/verb/chemical_reroll()
+/mob/camera/blob/verb/strain_reroll()
set category = "Blob"
- set name = "Reactive Chemical Adaptation (40)"
- set desc = "Replaces your chemical with a random, different one."
- if(free_chem_rerolls || can_buy(40))
- set_chemical()
- if(free_chem_rerolls)
- free_chem_rerolls--
+ set name = "Reactive Strain Adaptation (40)"
+ set desc = "Replaces your strain with a random, different one."
+ if(!rerolling && (free_strain_rerolls || can_buy(40)))
+ rerolling = TRUE
+ reroll_strain()
+ rerolling = FALSE
+ if(free_strain_rerolls)
+ free_strain_rerolls--
+ last_reroll_time = world.time
-/mob/camera/blob/proc/set_chemical()
- var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
- blob_reagent_datum = new BC
- color = blob_reagent_datum.complementary_color
- for(var/BL in GLOB.blobs)
- var/obj/structure/blob/B = BL
- B.update_icon()
- for(var/BLO in blob_mobs)
- var/mob/living/simple_animal/hostile/blob/BM = BLO
- BM.update_icons() //If it's getting a new chemical, tell it what it does!
- to_chat(BM, "Your overmind's blob reagent is now: [blob_reagent_datum.name]!")
- to_chat(BM, "The [blob_reagent_datum.name] reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]")
- to_chat(src, "Your reagent is now: [blob_reagent_datum.name]!")
- to_chat(src, "The [blob_reagent_datum.name] reagent [blob_reagent_datum.description]")
- if(blob_reagent_datum.effectdesc)
- to_chat(src, "The [blob_reagent_datum.name] reagent [blob_reagent_datum.effectdesc]")
+/mob/camera/blob/proc/reroll_strain()
+ var/list/choices = list()
+ while (length(choices) < 4)
+ var/datum/blobstrain/bs = pick((GLOB.valid_blobstrains))
+ choices[initial(bs.name)] = bs
+
+ var/choice = input(usr, "Please choose a new strain","Strain") as anything in choices
+ if (choice && choices[choice] && !QDELETED(src))
+ var/datum/blobstrain/bs = choices[choice]
+ set_strain(bs)
/mob/camera/blob/verb/blob_help()
set category = "Blob"
set name = "*Blob Help*"
set desc = "Help on how to blob."
to_chat(src, "As the overmind, you can control the blob!")
- to_chat(src, "Your blob reagent is: [blob_reagent_datum.name]!")
- to_chat(src, "The [blob_reagent_datum.name] reagent [blob_reagent_datum.description]")
- if(blob_reagent_datum.effectdesc)
- to_chat(src, "The [blob_reagent_datum.name] reagent [blob_reagent_datum.effectdesc]")
+ to_chat(src, "Your blob reagent is: [blobstrain.name]!")
+ to_chat(src, "The [blobstrain.name] reagent [blobstrain.description]")
+ if(blobstrain.effectdesc)
+ to_chat(src, "The [blobstrain.name] reagent [blobstrain.effectdesc]")
to_chat(src, "You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.")
to_chat(src, "Normal Blobs will expand your reach and can be upgraded into special blobs that perform certain functions.")
to_chat(src, "You can upgrade normal blobs into the following types of blob:")
diff --git a/code/modules/antagonists/blob/blob/theblob.dm b/code/modules/antagonists/blob/blob/theblob.dm
index fc78f859f2..444b10684b 100644
--- a/code/modules/antagonists/blob/blob/theblob.dm
+++ b/code/modules/antagonists/blob/blob/theblob.dm
@@ -83,16 +83,10 @@
/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind.
if(overmind)
- add_atom_colour(overmind.blob_reagent_datum.color, FIXED_COLOUR_PRIORITY)
+ add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
else
remove_atom_colour(FIXED_COLOUR_PRIORITY)
-/obj/structure/blob/process()
- Life()
-
-/obj/structure/blob/proc/Life()
- return
-
/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
if(QDELETED(pulsing_overmind))
pulsing_overmind = overmind
@@ -148,10 +142,10 @@
O.setDir(dir)
if(controller)
var/mob/camera/blob/BO = controller
- O.color = BO.blob_reagent_datum.color
+ O.color = BO.blobstrain.color
O.alpha = 200
else if(overmind)
- O.color = overmind.blob_reagent_datum.color
+ O.color = overmind.blobstrain.color
if(A)
O.do_attack_animation(A) //visually attack the whatever
return O //just in case you want to do something to the animation.
@@ -192,7 +186,7 @@
B.forceMove(T)
B.update_icon()
if(B.overmind && expand_reaction)
- B.overmind.blob_reagent_datum.expand_reaction(src, B, T, controller)
+ B.overmind.blobstrain.expand_reaction(src, B, T, controller)
return B
else
blob_attack_animation(T, controller)
@@ -209,14 +203,14 @@
return
if(severity > 0)
if(overmind)
- overmind.blob_reagent_datum.emp_reaction(src, severity)
+ overmind.blobstrain.emp_reaction(src, severity)
if(prob(100 - severity * 30))
new /obj/effect/temp_visual/emp(get_turf(src))
/obj/structure/blob/tesla_act(power)
..()
if(overmind)
- if(overmind.blob_reagent_datum.tesla_reaction(src, power))
+ if(overmind.blobstrain.tesla_reaction(src, power))
take_damage(power/400, BURN, "energy")
else
take_damage(power/400, BURN, "energy")
@@ -224,7 +218,7 @@
/obj/structure/blob/extinguish()
..()
if(overmind)
- overmind.blob_reagent_datum.extinguish_reaction(src)
+ overmind.blobstrain.extinguish_reaction(src)
/obj/structure/blob/hulk_damage()
return 15
@@ -243,13 +237,13 @@
else
return ..()
-/obj/structure/blob/proc/chemeffectreport()
+/obj/structure/blob/proc/chemeffectreport(mob/user)
RETURN_TYPE(/list)
. = list()
if(overmind)
- . += "Material: [overmind.blob_reagent_datum.name]."
- . += "Material Effects: [overmind.blob_reagent_datum.analyzerdescdamage]"
- . += "Material Properties: [overmind.blob_reagent_datum.analyzerdesceffect]
"
+ to_chat(user, "Material: [overmind.blobstrain.name].")
+ to_chat(user, "Material Effects: [overmind.blobstrain.analyzerdescdamage]")
+ to_chat(user, "Material Properties: [overmind.blobstrain.analyzerdesceffect]
")
else
. += "No Material Detected!
"
@@ -288,7 +282,7 @@
armor_protection = armor.getRating(damage_flag)
damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1)
if(overmind && damage_flag)
- damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag)
+ damage_amount = overmind.blobstrain.damage_reaction(src, damage_amount, damage_type, damage_flag)
return damage_amount
/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
@@ -298,7 +292,7 @@
/obj/structure/blob/obj_destruction(damage_flag)
if(overmind)
- overmind.blob_reagent_datum.death_reaction(src, damage_flag)
+ overmind.blobstrain.death_reaction(src, damage_flag)
..()
/obj/structure/blob/proc/change_to(type, controller)
@@ -332,8 +326,8 @@
/obj/structure/blob/proc/get_chem_name()
if(overmind)
- return overmind.blob_reagent_datum.name
- return "an unknown variant"
+ return overmind.blobstrain.name
+ return "some kind of organic tissue"
/obj/structure/blob/normal
name = "normal blob"
diff --git a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm
index b69b851990..4c377f4b82 100644
--- a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm
+++ b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm
@@ -90,6 +90,7 @@
user.dropItemToGround(src, TRUE) //user.drop_item() // "drop item" doesn't seem to exist anymore. New proc is user.dropItemToGround() but it doesn't seem like it's needed now?
var/obj/item/bodypart/B = C.get_bodypart("chest") // This was all taken from hitby() in human_defense.dm
B.embedded_objects |= src
+ embedded()
add_mob_blood(target)//Place blood on the stake
loc = C // Put INSIDE the character
B.receive_damage(w_class * embedding.embedded_impact_pain_multiplier)
diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
index db286dd6fe..a8925dfb7b 100644
--- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
+++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
@@ -258,7 +258,7 @@
/obj/structure/bloodsucker/vassalrack/proc/torture_victim(mob/living/user, mob/living/target)
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// Check Bloodmob/living/M, force = FALSE, check_loc = TRUE
- var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
+ var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
if(user.blood_volume < convert_cost + 5)
to_chat(user, "You don't have enough blood to initiate the Dark Communion with [target].")
return
@@ -449,7 +449,7 @@
/obj/structure/bloodsucker/candelabrum/Destroy()
STOP_PROCESSING(SSobj, src)
-/obj/structure/bloodsucker/candelabrum/update_icon()
+/obj/structure/bloodsucker/candelabrum/update_icon_state()
icon_state = "candelabrum[lit ? "_lit" : ""]"
/obj/structure/bloodsucker/candelabrum/examine(mob/user)
diff --git a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
index d5354c1af9..3be5eb02e9 100644
--- a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
+++ b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
@@ -126,15 +126,12 @@
to_chat(user, "[target] is fixed in place by your hypnotic gaze.")
target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm
target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze.
-
- UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
-
- spawn(power_time)
- if(istype(target) && success)
- target.notransform = FALSE
- // They Woke Up! (Notice if within view)
- if(istype(user) && target.stat == CONSCIOUS && (target in view(10, get_turf(user))) )
- to_chat(user, "[target] has snapped out of their trance.")
+ spawn(power_time)
+ if(istype(target) && success)
+ target.notransform = FALSE
+ // They Woke Up! (Notice if within view)
+ if(istype(user) && target.stat == CONSCIOUS && (target in view(10, get_turf(user))) )
+ to_chat(user, "[target] has snapped out of their trance.")
/datum/action/bloodsucker/targeted/mesmerize/ContinueActive(mob/living/user, mob/living/target)
return ..() && CheckCanUse() && CheckCanTarget(target)
diff --git a/code/modules/antagonists/changeling/powers/chameleon_skin.dm b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
index b7545353b2..03f3aab473 100644
--- a/code/modules/antagonists/changeling/powers/chameleon_skin.dm
+++ b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
@@ -13,17 +13,14 @@
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
if(!istype(H)) // req_human could be done in can_sting stuff.
return
- var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
- if(HM in H.dna.mutations)
- HM.force_lose(H)
+ if(H.dna.get_mutation(CHAMELEON))
+ H.dna.remove_mutation(CHAMELEON)
else
- HM.force_give(H)
+ H.dna.add_mutation(CHAMELEON)
return TRUE
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
action.Remove(user)
if(user.has_dna())
var/mob/living/carbon/C = user
- var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
- if(HM in C.dna.mutations)
- HM.force_lose(C)
\ No newline at end of file
+ C.dna.remove_mutation(CHAMELEON)
\ No newline at end of file
diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
index 1b4d26ac86..62382966f0 100644
--- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
+++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
@@ -251,6 +251,7 @@
return TRUE
/obj/effect/clockwork/sigil/transmission/update_icon()
+ . = ..()
var/power_charge = get_clockwork_power()
if(GLOB.ratvar_awakens)
alpha = 255
diff --git a/code/modules/antagonists/clockcult/clock_mobs.dm b/code/modules/antagonists/clockcult/clock_mobs.dm
index 9bdf03cbc7..2f00fd4e4a 100644
--- a/code/modules/antagonists/clockcult/clock_mobs.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs.dm
@@ -37,7 +37,7 @@
/mob/living/simple_animal/hostile/clockwork/ratvar_act()
fully_heal(TRUE)
-/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return 0 //ouch, my metal-unlikely-to-be-damaged-by-electricity-body
/mob/living/simple_animal/hostile/clockwork/examine(mob/user)
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 3dc199b56d..edeb74eeaf 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -264,7 +264,7 @@
/obj/item/restraints/legcuffs/bola/cult/pickup(mob/living/user)
if(!iscultist(user))
to_chat(user, "The bola seems to take on a life of its own!")
- throw_impact(user)
+ ensnare(user)
/obj/item/restraints/legcuffs/bola/cult/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(iscultist(hit_atom))
@@ -372,7 +372,7 @@
max = 40
prefix = "darkened"
-/obj/item/sharpener/cult/update_icon()
+/obj/item/sharpener/cult/update_icon_state()
var/old_state = icon_state
icon_state = "cult_sharpener[used ? "_used" : ""]"
if(old_state != icon_state)
@@ -688,7 +688,7 @@
qdel(spear_act)
..()
-/obj/item/twohanded/cult_spear/update_icon()
+/obj/item/twohanded/cult_spear/update_icon_state()
icon_state = "bloodspear[wielded]"
/obj/item/twohanded/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index c6c29a385b..1963bf16e3 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -31,6 +31,7 @@
var/interior = ""
var/proper_bomb = TRUE //Please
var/obj/effect/countdown/nuclearbomb/countdown
+ var/nuclear_cooldown //used to stop global spam.
/obj/machinery/nuclearbomb/Initialize()
. = ..()
@@ -416,12 +417,16 @@
return
timing = !timing
if(timing)
+ if(nuclear_cooldown > world.time)
+ to_chat(usr, "[src]'s timer protocols are currently on cooldown, please stand by.")
+ return
previous_level = get_security_level()
detonation_timer = world.time + (timer_set * 10)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
set_security_level("delta")
+ nuclear_cooldown = world.time + 15 SECONDS
if(GLOB.war_declared)
var/area/A = get_area(src)
diff --git a/code/modules/antagonists/overthrow/overthrow_converter.dm b/code/modules/antagonists/overthrow/overthrow_converter.dm
index 99d1a52de8..eec6163107 100644
--- a/code/modules/antagonists/overthrow/overthrow_converter.dm
+++ b/code/modules/antagonists/overthrow/overthrow_converter.dm
@@ -49,7 +49,7 @@
else
to_chat(user, "[user] fails to implant [M].")
-/obj/item/overthrow_converter/update_icon()
+/obj/item/overthrow_converter/update_icon_state()
if(uses)
icon_state = "implanter1"
else
diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm
index d2e71615e3..a99e3900b0 100644
--- a/code/modules/antagonists/revenant/revenant_abilities.dm
+++ b/code/modules/antagonists/revenant/revenant_abilities.dm
@@ -213,7 +213,7 @@
continue
L.Beam(M,icon_state="purple_lightning",time=5)
if(!M.anti_magic_check(FALSE, TRUE))
- M.electrocute_act(shock_damage, L, safety=TRUE)
+ M.electrocute_act(shock_damage, L, flags = SHOCK_NOGLOVES)
do_sparks(4, FALSE, M)
playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm
index 8a01622c87..475438ba52 100644
--- a/code/modules/antagonists/slaughter/slaughter.dm
+++ b/code/modules/antagonists/slaughter/slaughter.dm
@@ -76,8 +76,9 @@
icon_state = "demon_heart-on"
decay_factor = 0
-/obj/item/organ/heart/demon/update_icon()
- return //always beating visually
+/obj/item/organ/heart/demon/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/item/organ/heart/demon/attack(mob/M, mob/living/carbon/user, obj/target)
if(M != user)
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index 92852c6c7f..292fb10202 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -492,8 +492,8 @@
playsound(src,'sound/effects/sparks4.ogg',50,1)
do_teleport(target, F, 0, channel = TELEPORT_CHANNEL_BLUESPACE)
-/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
- if(!tesla_shock)
+/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ if(!(flags & SHOCK_TESLA))
return FALSE
return ..()
@@ -584,7 +584,7 @@
var/mob/living/L = AM
if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
playsound(loc,'sound/effects/snap.ogg',50, 1, -1)
- L.electrocute_act(0, src, 1, 1, 1)
+ L.electrocute_act(0, src, 1, flags = SHOCK_NOGLOVES|SHOCK_ILLUSION)
if(iscyborg(L))
L.DefaultCombatKnockdown(100)
qdel(src)
diff --git a/code/modules/antagonists/swarmer/swarmer_event.dm b/code/modules/antagonists/swarmer/swarmer_event.dm
index 8e385f6674..6ef35fdbaa 100644
--- a/code/modules/antagonists/swarmer/swarmer_event.dm
+++ b/code/modules/antagonists/swarmer/swarmer_event.dm
@@ -5,6 +5,7 @@
max_occurrences = 0
earliest_start = 30 MINUTES
min_players = 15
+ gamemode_blacklist = list("dynamic")
/datum/round_event/spawn_swarmer
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index d22d1b5e39..15ec78692f 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -24,5 +24,5 @@
return
H.dna.add_mutation(HULK)
H.dna.add_mutation(XRAY)
- H.dna.add_mutation(COLDRES)
+ H.dna.add_mutation(SPACEMUT)
H.dna.add_mutation(TK)
\ No newline at end of file
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index 70cb29e7a3..5ef3f24bd6 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -375,6 +375,8 @@
desc = "A collection of wands that allow for a wide variety of utility. Wands have a limited number of charges, so be conservative in use. Comes in a handy belt."
item_path = /obj/item/storage/belt/wands/full
category = "Defensive"
+ dynamic_requirement = 60
+ dynamic_cost = 10
/datum/spellbook_entry/item/armor
name = "Mastercrafted Armor Set"
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index 28bb543828..a40a4c1a42 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -19,15 +19,17 @@
/obj/item/onetankbomb/examine(mob/user)
bombtank.examine(user)
-/obj/item/onetankbomb/update_icon()
- cut_overlays()
+/obj/item/onetankbomb/update_icon_state()
if(bombtank)
icon = bombtank.icon
icon_state = bombtank.icon_state
+
+/obj/item/onetankbomb/update_overlays()
+ . = ..()
if(bombassembly)
- add_overlay(bombassembly.icon_state)
- copy_overlays(bombassembly)
- add_overlay("bomb_assembly")
+ . += bombassembly.icon_state
+ . += bombassembly.overlays
+ . += "bomb_assembly"
/obj/item/onetankbomb/wrench_act(mob/living/user, obj/item/I)
to_chat(user, "You disassemble [src]!")
@@ -101,10 +103,10 @@
bombassembly.setDir(dir)
bombassembly.Move()
-/obj/item/onetankbomb/dropped()
+/obj/item/onetankbomb/dropped(mob/user)
. = ..()
if(bombassembly)
- bombassembly.dropped()
+ bombassembly.dropped(user)
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index d7c184de92..c960a7f039 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -83,9 +83,9 @@
/obj/item/assembly_holder/dropped(mob/user)
. = ..()
if(a_left)
- a_left.dropped()
+ a_left.dropped(user)
if(a_right)
- a_right.dropped()
+ a_right.dropped(user)
/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
. = ..()
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index d19666077d..33c6d46045 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -20,13 +20,8 @@
/obj/item/assembly/infra/ComponentInitialize()
. = ..()
- AddComponent(
- /datum/component/simple_rotation,
- ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS,
- null,
- null,
- CALLBACK(src,.proc/after_rotation)
- )
+ var/static/rotation_flags = ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS
+ AddComponent(/datum/component/simple_rotation, rotation_flags, after_rotation=CALLBACK(src,.proc/after_rotation))
/obj/item/assembly/infra/proc/after_rotation()
refreshBeam()
@@ -74,7 +69,7 @@
holder.update_icon()
return
-/obj/item/assembly/infra/dropped()
+/obj/item/assembly/infra/dropped(mob/user)
. = ..()
if(holder)
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 4d2b8de586..07ddd4c042 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -624,10 +624,7 @@
"set_internal_pressure" = 0
))
-/obj/machinery/airalarm/update_icon()
- set_light(0)
- cut_overlays()
- SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
+/obj/machinery/airalarm/update_icon_state()
if(stat & NOPOWER)
icon_state = "alarm0"
return
@@ -636,35 +633,39 @@
icon_state = "alarmx"
return
- if(panel_open)
- switch(buildstage)
- if(2)
- icon_state = "alarmx"
- if(1)
- icon_state = "alarm_b2"
- if(0)
- icon_state = "alarm_b1"
+ if(!panel_open)
+ icon_state = "alarm1"
return
- icon_state = "alarm1"
+ switch(buildstage)
+ if(2)
+ icon_state = "alarmx"
+ if(1)
+ icon_state = "alarm_b2"
+ if(0)
+ icon_state = "alarm_b1"
+
+/obj/machinery/airalarm/update_overlays()
+ . = ..()
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
var/overlay_state = AALARM_OVERLAY_OFF
var/area/A = get_base_area(src)
switch(max(danger_level, A.atmosalm))
if(0)
- add_overlay(AALARM_OVERLAY_GREEN)
overlay_state = AALARM_OVERLAY_GREEN
light_color = LIGHT_COLOR_GREEN
- set_light(brightness_on)
if(1)
- add_overlay(AALARM_OVERLAY_WARN)
overlay_state = AALARM_OVERLAY_WARN
light_color = LIGHT_COLOR_LAVA
- set_light(brightness_on)
if(2)
- add_overlay(AALARM_OVERLAY_DANGER)
overlay_state = AALARM_OVERLAY_DANGER
light_color = LIGHT_COLOR_RED
- set_light(brightness_on)
+
+ if(overlay_state != AALARM_OVERLAY_OFF)
+ . += overlay_state
+ set_light(brightness_on)
+ else
+ set_light(0)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
update_light()
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index 0d335da482..0b26cfc2f0 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -34,7 +34,6 @@
var/restricted = FALSE
req_access = list()
- var/update = 0
var/static/list/label2types = list(
"n2" = /obj/machinery/portable_atmospherics/canister/nitrogen,
"o2" = /obj/machinery/portable_atmospherics/canister/oxygen,
@@ -213,61 +212,26 @@
air_contents.gases[/datum/gas/oxygen] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
air_contents.gases[/datum/gas/nitrogen] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
-#define HOLDING (1<<0)
-#define CONNECTED (1<<1)
-#define EMPTY (1<<2)
-#define LOW (1<<3)
-#define MEDIUM (1<<4)
-#define FULL (1<<5)
-#define DANGER (1<<6)
-/obj/machinery/portable_atmospherics/canister/update_icon()
+/obj/machinery/portable_atmospherics/canister/update_icon_state()
if(stat & BROKEN)
- cut_overlays()
icon_state = "[icon_state]-1"
- return
- var/last_update = update
- update = 0
+/obj/machinery/portable_atmospherics/canister/update_overlays()
+ . = ..()
if(holding)
- update |= HOLDING
+ . += "can-open"
if(connected_port)
- update |= CONNECTED
+ . += "can-connector"
var/pressure = air_contents.return_pressure()
- if(pressure < 10)
- update |= EMPTY
- else if(pressure < 5 * ONE_ATMOSPHERE)
- update |= LOW
- else if(pressure < 10 * ONE_ATMOSPHERE)
- update |= MEDIUM
- else if(pressure < 40 * ONE_ATMOSPHERE)
- update |= FULL
- else
- update |= DANGER
-
- if(update == last_update)
- return
-
- cut_overlays()
- if(update & HOLDING)
- add_overlay("can-open")
- if(update & CONNECTED)
- add_overlay("can-connector")
- if(update & LOW)
- add_overlay("can-o0")
- else if(update & MEDIUM)
- add_overlay("can-o1")
- else if(update & FULL)
- add_overlay("can-o2")
- else if(update & DANGER)
- add_overlay("can-o3")
-#undef HOLDING
-#undef CONNECTED
-#undef EMPTY
-#undef LOW
-#undef MEDIUM
-#undef FULL
-#undef DANGER
+ if(pressure >= 40 * ONE_ATMOSPHERE)
+ . += "can-o3"
+ else if(pressure >= 10 * ONE_ATMOSPHERE)
+ . += "can-o2"
+ else if(pressure >= 5 * ONE_ATMOSPHERE)
+ . += "can-o1"
+ else if(pressure >= 10)
+ . += "can-o0"
/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > temperature_resistance)
diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm
index ddb907a2ad..3603e46490 100644
--- a/code/modules/atmospherics/machinery/portable/pump.dm
+++ b/code/modules/atmospherics/machinery/portable/pump.dm
@@ -29,14 +29,16 @@
QDEL_NULL(pump)
return ..()
-/obj/machinery/portable_atmospherics/pump/update_icon()
+/obj/machinery/portable_atmospherics/pump/update_icon_state()
icon_state = "psiphon:[on]"
- cut_overlays()
+
+/obj/machinery/portable_atmospherics/pump/update_overlays()
+ . = ..()
if(holding)
- add_overlay("siphon-open")
+ . += "siphon-open"
if(connected_port)
- add_overlay("siphon-connector")
+ . += "siphon-connector"
/obj/machinery/portable_atmospherics/pump/process_atmos()
..()
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index 0c902e0426..3dfce7c1bf 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -5,6 +5,7 @@
var/on = FALSE
var/volume_rate = 1000
+ var/use_overlays = TRUE
volume = 1000
var/list/scrubbing = list(/datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz, /datum/gas/nitryl, /datum/gas/tritium, /datum/gas/hypernoblium, /datum/gas/water_vapor)
@@ -15,14 +16,17 @@
air_update_turf()
return ..()
-/obj/machinery/portable_atmospherics/scrubber/update_icon()
+/obj/machinery/portable_atmospherics/scrubber/update_icon_state()
icon_state = "pscrubber:[on]"
- cut_overlays()
+/obj/machinery/portable_atmospherics/scrubber/update_overlays()
+ . = ..()
+ if(!use_overlays)
+ return
if(holding)
- add_overlay("scrubber-open")
+ . += "scrubber-open"
if(connected_port)
- add_overlay("scrubber-connector")
+ . += "scrubber-connector"
/obj/machinery/portable_atmospherics/scrubber/process_atmos()
..()
@@ -117,11 +121,12 @@
volume = 50000
var/movable = FALSE
+ use_overlays = FALSE
/obj/machinery/portable_atmospherics/scrubber/huge/movable
movable = TRUE
-/obj/machinery/portable_atmospherics/scrubber/huge/update_icon()
+/obj/machinery/portable_atmospherics/scrubber/huge/update_icon_state()
icon_state = "scrubber:[on]"
/obj/machinery/portable_atmospherics/scrubber/huge/process_atmos()
diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm
index c4bb6e8cdd..594eae5d46 100644
--- a/code/modules/awaymissions/capture_the_flag.dm
+++ b/code/modules/awaymissions/capture_the_flag.dm
@@ -379,7 +379,7 @@
force = 75
mag_type = /obj/item/ammo_box/magazine/m50/ctf
-/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped()
+/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped(mob/user)
. = ..()
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
@@ -402,14 +402,14 @@
desc = "This looks like it could really hurt in melee."
force = 50
-/obj/item/gun/ballistic/automatic/laser/ctf/dropped()
+/obj/item/gun/ballistic/automatic/laser/ctf/dropped(mob/user)
. = ..()
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
/obj/item/ammo_box/magazine/recharge/ctf
ammo_type = /obj/item/ammo_casing/caseless/laser/ctf
-/obj/item/ammo_box/magazine/recharge/ctf/dropped()
+/obj/item/ammo_box/magazine/recharge/ctf/dropped(mob/user)
. = ..()
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
@@ -475,7 +475,7 @@
return TRUE
return ..()
-/obj/item/claymore/ctf/dropped()
+/obj/item/claymore/ctf/dropped(mob/user)
. = ..()
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index e693abc8e4..4f155d4174 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -53,11 +53,8 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
ready = TRUE
return ready
-/obj/machinery/gateway/update_icon()
- if(active)
- icon_state = "on"
- return
- icon_state = "off"
+/obj/machinery/gateway/update_icon_state()
+ icon_state = active ? "on" : "off"
/obj/machinery/gateway/attack_hand(mob/user)
. = ..()
@@ -100,11 +97,8 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
var/obj/machinery/gateway/centeraway/awaygate = null
can_link = TRUE
-/obj/machinery/gateway/centerstation/update_icon()
- if(active)
- icon_state = "oncenter"
- return
- icon_state = "offcenter"
+/obj/machinery/gateway/centerstation/update_icon_state()
+ icon_state = active ? "oncenter" : "offcenter"
/obj/machinery/gateway/centerstation/process()
if((stat & (NOPOWER)) && use_power)
@@ -185,11 +179,8 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
stationgate = locate(/obj/machinery/gateway/centerstation)
-/obj/machinery/gateway/centeraway/update_icon()
- if(active)
- icon_state = "oncenter"
- return
- icon_state = "offcenter"
+/obj/machinery/gateway/centeraway/update_icon_state()
+ icon_state = active ? "oncenter" : "offcenter"
/obj/machinery/gateway/centeraway/toggleon(mob/user)
if(!detect())
diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm
index f24cb065dd..a1453f6aa1 100644
--- a/code/modules/awaymissions/mission_code/Academy.dm
+++ b/code/modules/awaymissions/mission_code/Academy.dm
@@ -337,8 +337,9 @@
icon_state = "1"
color = rgb(0,0,255)
-/obj/structure/ladder/unbreakable/rune/update_icon()
- return
+/obj/structure/ladder/unbreakable/rune/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/structure/ladder/unbreakable/rune/show_fluff_message(up,mob/user)
user.visible_message("[user] activates \the [src].","You activate \the [src].")
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index ac1a4077ec..505b9a1edc 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -94,7 +94,7 @@
to_chat(user, "Your wish is granted, but at a terrible cost...")
to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart.")
user.dna.add_mutation(LASEREYES)
- user.dna.add_mutation(COLDRES)
+ user.dna.add_mutation(SPACEMUT)
user.dna.add_mutation(XRAY)
user.set_species(/datum/species/shadow)
if("Wealth")
diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm
index 7cbbde096d..74bcb4b302 100644
--- a/code/modules/awaymissions/pamphlet.dm
+++ b/code/modules/awaymissions/pamphlet.dm
@@ -35,5 +35,6 @@
environs."
//we don't want the silly text overlay!
-/obj/item/paper/pamphlet/update_icon()
- return
+/obj/item/paper/pamphlet/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
diff --git a/code/modules/buildmode/buttons.dm b/code/modules/buildmode/buttons.dm
index e72dbde064..c219f18cec 100644
--- a/code/modules/buildmode/buttons.dm
+++ b/code/modules/buildmode/buttons.dm
@@ -27,7 +27,7 @@
update_icon()
return 1
-/obj/screen/buildmode/mode/update_icon()
+/obj/screen/buildmode/mode/update_icon_state()
icon_state = bd.mode.get_button_iconstate()
/obj/screen/buildmode/help
@@ -44,9 +44,8 @@
screen_loc = "NORTH,WEST+2"
name = "Change Dir"
-/obj/screen/buildmode/bdir/update_icon()
+/obj/screen/buildmode/bdir/update_icon_state()
dir = bd.build_dir
- return
/obj/screen/buildmode/bdir/Click()
bd.toggle_dirswitch()
diff --git a/code/modules/cargo/packs/organic.dm b/code/modules/cargo/packs/organic.dm
index cdac6f49f2..ffc0d757b5 100644
--- a/code/modules/cargo/packs/organic.dm
+++ b/code/modules/cargo/packs/organic.dm
@@ -193,8 +193,8 @@
/obj/item/reagent_containers/food/snacks/meat/slab/bear,
/obj/item/reagent_containers/food/snacks/meat/slab/xeno,
/obj/item/reagent_containers/food/snacks/meat/slab/spider,
+ /obj/item/reagent_containers/food/snacks/spidereggs,
/obj/item/reagent_containers/food/snacks/meat/rawcrab,
- /obj/item/reagent_containers/food/snacks/meat/rawbacon,
/obj/item/reagent_containers/food/snacks/spiderleg,
/obj/item/reagent_containers/food/snacks/carpmeat,
/obj/item/reagent_containers/food/snacks/meat/slab/human)
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index e82a2141eb..aaa1afb004 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -78,15 +78,14 @@
. = ..()
setStyle(style, TRUE) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
-/obj/structure/closet/supplypod/update_icon()
- cut_overlays()
+/obj/structure/closet/supplypod/closet_update_overlays(list/new_overlays)
+ . = new_overlays
if (style == STYLE_SEETHROUGH || style == STYLE_INVISIBLE) //If we're invisible, we dont bother adding any overlays
return
+ if (opened)
+ . += "[icon_state]_open"
else
- if (opened)
- add_overlay("[icon_state]_open")
- else
- add_overlay("[icon_state]_door")
+ . += "[icon_state]_door"
/obj/structure/closet/supplypod/proc/setStyle(chosenStyle, var/duringInit = FALSE) //Used to give the sprite an icon state, name, and description
if (!duringInit && style == chosenStyle) //Check if the input style is already the same as the pod's style. This happens in centcom_podlauncher, and as such we set the style to STYLE_CENTCOM.
diff --git a/code/modules/cargo/supplypod_beacon.dm b/code/modules/cargo/supplypod_beacon.dm
index 668698d2e9..b749d4def6 100644
--- a/code/modules/cargo/supplypod_beacon.dm
+++ b/code/modules/cargo/supplypod_beacon.dm
@@ -31,14 +31,14 @@
ready = FALSE
update_icon()
-/obj/item/supplypod_beacon/update_icon()
- cut_overlays()
+/obj/item/supplypod_beacon/update_overlays()
+ . = ..()
if (launched)
- add_overlay("sp_green")
+ . += "sp_green"
else if (ready)
- add_overlay("sp_yellow")
+ . += "sp_yellow"
else if (linked)
- add_overlay("sp_orange")
+ . += "sp_orange"
/obj/item/supplypod_beacon/proc/endLaunch()
launched = FALSE
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index 902b442403..7bafefcf53 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -703,3 +703,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
Insert(initial(D.id), I)
return ..()
+
+/datum/asset/simple/genetics
+ assets = list(
+ "dna_discovered.gif" = 'html/dna_discovered.gif',
+ "dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
+ "dna_extra.gif" = 'html/dna_extra.gif'
+)
\ No newline at end of file
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index a9cf45744e..a9deba070a 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -2429,6 +2429,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
else
character.Digitigrade_Leg_Swap(TRUE)
+ SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
+
//let's be sure the character updates
if(icon_updates)
character.update_body()
diff --git a/code/modules/client/verbs/minimap.dm b/code/modules/client/verbs/minimap.dm
new file mode 100644
index 0000000000..3d213dc210
--- /dev/null
+++ b/code/modules/client/verbs/minimap.dm
@@ -0,0 +1,10 @@
+/client/verb/show_station_minimap()
+ set category = "OOC"
+ set name = "Show Station Minimap"
+ set desc = "Shows a minimap of the currently loaded station map."
+
+ if(!CONFIG_GET(flag/minimaps_enabled))
+ to_chat(usr, "Minimap generation is not enabled in the server's configuration.")
+ return
+
+ SSminimaps.station_minimap.show(src)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index ef2ee331a9..7e2d73190a 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -441,17 +441,14 @@ BLIND // can't see anything
return TRUE
-/obj/item/clothing/update_icon() // Polychrome stuff
- ..()
+/obj/item/clothing/update_overlays() // Polychrome stuff
+ . = ..()
if(hasprimary) //Checks if the overlay is enabled
- var/mutable_appearance/primary_overlay = mutable_appearance(icon, "[item_color]-primary") //Automagically picks overlays
- primary_overlay.color = primary_color //Colors the greyscaled overlay
- add_overlay(primary_overlay) //Applies the coloured overlay onto the item sprite. but NOT the mob sprite.
+ var/mutable_appearance/primary_overlay = mutable_appearance(icon, "[item_color]-primary", color = primary_color) //Automagically picks overlays
+ . += primary_overlay //Applies the coloured overlay onto the item sprite. but NOT the mob sprite.
if(hassecondary)
- var/mutable_appearance/secondary_overlay = mutable_appearance(icon, "[item_color]-secondary")
- secondary_overlay.color = secondary_color
- add_overlay(secondary_overlay)
+ var/mutable_appearance/secondary_overlay = mutable_appearance(icon, "[item_color]-secondary", color = secondary_color)
+ . += secondary_overlay
if(hastertiary)
- var/mutable_appearance/tertiary_overlay = mutable_appearance(icon, "[item_color]-tertiary")
- tertiary_overlay.color = tertiary_color
- add_overlay(tertiary_overlay)
\ No newline at end of file
+ var/mutable_appearance/tertiary_overlay = mutable_appearance(icon, "[item_color]-tertiary", color = tertiary_color)
+ . += tertiary_overlay
\ No newline at end of file
diff --git a/code/modules/clothing/ears/_ears.dm b/code/modules/clothing/ears/_ears.dm
index 6775279ef6..1c31e19202 100644
--- a/code/modules/clothing/ears/_ears.dm
+++ b/code/modules/clothing/ears/_ears.dm
@@ -35,16 +35,15 @@
. = ..()
update_icon()
-/obj/item/clothing/ears/headphones/update_icon()
+/obj/item/clothing/ears/headphones/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/clothing/ears/headphones/update_icon_state()
icon_state = "[initial(icon_state)]_[headphones_on? "on" : "off"]"
item_state = "[initial(item_state)]_[headphones_on? "on" : "off"]"
/obj/item/clothing/ears/headphones/proc/toggle(owner)
headphones_on = !headphones_on
update_icon()
- var/mob/living/carbon/human/H = owner
- if(istype(H))
- H.update_inv_ears()
- H.update_inv_neck()
- H.update_inv_head()
to_chat(owner, "You turn the music [headphones_on? "on. Untz Untz Untz!" : "off."]")
diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm
index 0132fa8596..2a64445776 100644
--- a/code/modules/clothing/glasses/engine_goggles.dm
+++ b/code/modules/clothing/glasses/engine_goggles.dm
@@ -31,6 +31,10 @@
START_PROCESSING(SSobj, src)
update_icon()
+/obj/item/clothing/glasses/meson/engine/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
/obj/item/clothing/glasses/meson/engine/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
@@ -120,18 +124,8 @@
pic = new('icons/turf/overlays.dmi', place, "redOverlay", AREA_LAYER)
flick_overlay(pic, list(user.client), 8)
-/obj/item/clothing/glasses/meson/engine/update_icon()
+/obj/item/clothing/glasses/meson/engine/update_icon_state()
icon_state = "trayson-[mode]"
- update_mob()
-
-/obj/item/clothing/glasses/meson/engine/proc/update_mob()
- item_state = icon_state
- if(isliving(loc))
- var/mob/living/user = loc
- if(user.get_item_by_slot(SLOT_GLASSES) == src)
- user.update_inv_glasses()
- else
- user.update_inv_hands()
/obj/item/clothing/glasses/meson/engine/tray //atmos techs have lived far too long without tray goggles while those damned engineers get their dual-purpose gogles all to themselves
name = "optical t-ray scanner"
diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm
index e7a8243889..29b0aa9552 100644
--- a/code/modules/clothing/head/hardhat.dm
+++ b/code/modules/clothing/head/hardhat.dm
@@ -16,6 +16,11 @@
dog_fashion = /datum/dog_fashion/head
+
+/obj/item/clothing/head/hardhat/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
/obj/item/clothing/head/hardhat/attack_self(mob/living/user)
toggle_helmet_light(user)
@@ -27,15 +32,9 @@
turn_off(user)
update_icon()
-/obj/item/clothing/head/hardhat/update_icon()
+/obj/item/clothing/head/hardhat/update_icon_state()
icon_state = "hardhat[on]_[item_color]"
item_state = "hardhat[on]_[item_color]"
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- H.update_inv_head()
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon(force = TRUE)
/obj/item/clothing/head/hardhat/proc/turn_on(mob/user)
set_light(brightness_on, power_on)
@@ -132,11 +131,10 @@
if(!up)
. += mutable_appearance('icons/mob/head.dmi', "weldvisor")
-/obj/item/clothing/head/hardhat/weldhat/update_icon()
- cut_overlays()
+/obj/item/clothing/head/hardhat/weldhat/update_overlays()
+ . = ..()
if(!up)
- add_overlay("weldvisor")
- ..()
+ . += "weldvisor"
/obj/item/clothing/head/hardhat/weldhat/orange
icon_state = "hardhat0_orange"
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index e176943aa9..3b1b92ea78 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -258,7 +258,11 @@
//LightToggle
-/obj/item/clothing/head/helmet/update_icon()
+/obj/item/clothing/head/helment/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/clothing/head/helmet/update_icon_state()
var/state = "[initial(icon_state)]"
if(F)
if(F.on)
@@ -268,10 +272,6 @@
icon_state = state
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- H.update_inv_head()
-
/obj/item/clothing/head/helmet/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_helmet_flashlight))
toggle_helmlight()
diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm
index 4be949a65e..0b58ea234c 100644
--- a/code/modules/clothing/head/misc_special.dm
+++ b/code/modules/clothing/head/misc_special.dm
@@ -197,16 +197,19 @@
. = ..()
update_icon()
-/obj/item/clothing/head/wig/update_icon()
- cut_overlays()
+/obj/item/clothing/head/wig/update_icon_state()
var/datum/sprite_accessory/S = GLOB.hair_styles_list[hair_style]
if(!S)
+ icon = 'icons/obj/clothing/hats.dmi'
icon_state = "pwig"
- else
- var/mutable_appearance/M = mutable_appearance(S.icon,S.icon_state)
+
+/obj/item/clothing/head/wig/update_overlays()
+ . = ..()
+ var/datum/sprite_accessory/S = GLOB.hair_styles_list[hair_style]
+ if(S)
+ var/mutable_appearance/M = mutable_appearance(S.icon, S.icon_state, color = hair_color)
M.appearance_flags |= RESET_COLOR
- M.color = hair_color
- add_overlay(M)
+ . += M
/obj/item/clothing/head/wig/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = list()
diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm
index cb169f4f62..19ff6bc8b2 100644
--- a/code/modules/clothing/head/soft_caps.dm
+++ b/code/modules/clothing/head/soft_caps.dm
@@ -9,10 +9,10 @@
var/flipped = 0
-/obj/item/clothing/head/soft/dropped()
- src.icon_state = "[item_color]soft"
- src.flipped=0
- ..()
+/obj/item/clothing/head/soft/dropped(mob/user)
+ icon_state = "[item_color]soft"
+ flipped = FALSE
+ return ..()
/obj/item/clothing/head/soft/verb/flipcap()
set category = "Object"
diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm
index 84990f9660..c743f26363 100644
--- a/code/modules/clothing/masks/miscellaneous.dm
+++ b/code/modules/clothing/masks/miscellaneous.dm
@@ -247,6 +247,30 @@
/obj/item/clothing/mask/bandana/attack_self(mob/user)
adjustmask(user)
+/obj/item/clothing/mask/bandana/AltClick(mob/user)
+ . = ..()
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ if((C.get_item_by_slot(SLOT_HEAD == src)) || (C.get_item_by_slot(SLOT_WEAR_MASK) == src))
+ to_chat(user, "You can't tie [src] while wearing it!")
+ return
+ if(slot_flags & ITEM_SLOT_HEAD)
+ to_chat(user, "You must undo [src] before you can tie it into a neckerchief!")
+ else
+ if(user.is_holding(src))
+ var/obj/item/clothing/neck/neckerchief/nk = new(src)
+ nk.name = "[name] neckerchief"
+ nk.desc = "[desc] It's tied up like a neckerchief."
+ nk.icon_state = icon_state
+ nk.sourceBandanaType = src.type
+ var/currentHandIndex = user.get_held_index_of_item(src)
+ user.transferItemToLoc(src, null)
+ user.put_in_hand(nk, currentHandIndex)
+ user.visible_message("You tie [src] up like a neckerchief.", "[user] ties [src] up like a neckerchief.")
+ qdel(src)
+ else
+ to_chat(user, "You must be holding [src] in order to tie it!")
+
/obj/item/clothing/mask/bandana/red
name = "red bandana"
desc = "A fine red bandana with nanotech lining."
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 2b43460257..530af7777d 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -291,3 +291,36 @@
icon = 'icons/obj/clothing/neck.dmi'
icon_state = "bling"
item_color = "bling"
+
+//////////////////////////////////
+//VERY SUPER BADASS NECKERCHIEFS//
+//////////////////////////////////
+
+obj/item/clothing/neck/neckerchief
+ icon = 'icons/obj/clothing/masks.dmi' //In order to reuse the bandana sprite
+ w_class = WEIGHT_CLASS_TINY
+ var/sourceBandanaType
+
+/obj/item/clothing/neck/neckerchief/worn_overlays(isinhands)
+ . = ..()
+ if(!isinhands)
+ var/mutable_appearance/realOverlay = mutable_appearance('icons/mob/mask.dmi', icon_state)
+ realOverlay.pixel_y = -3
+ . += realOverlay
+
+/obj/item/clothing/neck/neckerchief/AltClick(mob/user)
+ . = ..()
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ if(C.get_item_by_slot(SLOT_NECK) == src)
+ to_chat(user, "You can't untie [src] while wearing it!")
+ return
+ if(user.is_holding(src))
+ var/obj/item/clothing/mask/bandana/newBand = new sourceBandanaType(user)
+ var/currentHandIndex = user.get_held_index_of_item(src)
+ var/oldName = src.name
+ qdel(src)
+ user.put_in_hand(newBand, currentHandIndex)
+ user.visible_message("You untie [oldName] back into a [newBand.name]", "[user] unties [oldName] back into a [newBand.name]")
+ else
+ to_chat(user, "You must be holding [src] in order to untie it!")
diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm
index ffb7771d2f..08f12deb1f 100644
--- a/code/modules/clothing/shoes/bananashoes.dm
+++ b/code/modules/clothing/shoes/bananashoes.dm
@@ -10,11 +10,15 @@
/obj/item/clothing/shoes/clown_shoes/banana_shoes/Initialize()
. = ..()
- AddComponent(/datum/component/material_container, list(/datum/material/bananium), 200000, TRUE, /obj/item/stack)
- AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75)
if(always_noslip)
clothing_flags |= NOSLIP
+/obj/item/clothing/shoes/clown_shoes/banana_shoes/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+ AddComponent(/datum/component/material_container, list(/datum/material/bananium), 200000, TRUE, /obj/item/stack)
+ AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75)
+
/obj/item/clothing/shoes/clown_shoes/banana_shoes/step_action()
. = ..()
var/datum/component/material_container/bananium = GetComponent(/datum/component/material_container)
@@ -55,12 +59,5 @@
else
to_chat(user, "You need bananium to turn the prototype shoes on!")
-/obj/item/clothing/shoes/clown_shoes/banana_shoes/update_icon()
- if(on)
- icon_state = "clown_prototype_on"
- else
- icon_state = "clown_prototype_off"
- usr.update_inv_shoes()
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
+/obj/item/clothing/shoes/clown_shoes/banana_shoes/update_icon_state()
+ icon_state = "clown_prototype_[on? "on" : "off"]"
\ No newline at end of file
diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm
index 3d06fba285..4311aa5166 100644
--- a/code/modules/clothing/spacesuits/chronosuit.dm
+++ b/code/modules/clothing/spacesuits/chronosuit.dm
@@ -8,16 +8,11 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
var/obj/item/clothing/suit/space/chronos/suit = null
-/obj/item/clothing/head/helmet/space/chronos/dropped()
+/obj/item/clothing/head/helmet/space/chronos/dropped(mob/user)
if(suit)
suit.deactivate(1, 1)
..()
-/obj/item/clothing/head/helmet/space/chronos/Destroy()
- dropped()
- return ..()
-
-
/obj/item/clothing/suit/space/chronos
name = "Chronosuit"
desc = "An advanced spacesuit equipped with time-bluespace teleportation and anti-compression technology."
@@ -57,15 +52,11 @@
else
deactivate()
-/obj/item/clothing/suit/space/chronos/dropped()
+/obj/item/clothing/suit/space/chronos/dropped(mob/user)
if(activated)
deactivate()
..()
-/obj/item/clothing/suit/space/chronos/Destroy()
- dropped()
- return ..()
-
/obj/item/clothing/suit/space/chronos/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
@@ -220,7 +211,7 @@
teleport_now.Remove(user)
if(user.wear_suit == src)
if(hard_landing)
- user.electrocute_act(35, src, safety = 1)
+ user.electrocute_act(35, src, flags = SHOCK_NOGLOVES)
user.DefaultCombatKnockdown(200)
if(!silent)
to_chat(user, "\nroot@ChronosuitMK4# chronowalk4 --stop\n")
diff --git a/code/modules/clothing/spacesuits/flightsuit.dm b/code/modules/clothing/spacesuits/flightsuit.dm
index 2f9ded7848..89644667d0 100644
--- a/code/modules/clothing/spacesuits/flightsuit.dm
+++ b/code/modules/clothing/spacesuits/flightsuit.dm
@@ -805,7 +805,8 @@
. += "Its maintainence panel is [maint_panel ? "OPEN" : "CLOSED"]"
/obj/item/clothing/suit/space/hardsuit/flightsuit/Destroy()
- dropped()
+ if(ismob(loc))
+ dropped(loc)
QDEL_NULL(pack)
QDEL_NULL(shoes)
return ..()
@@ -834,7 +835,7 @@
return FALSE
return ..()
-/obj/item/clothing/suit/space/hardsuit/flightsuit/dropped()
+/obj/item/clothing/suit/space/hardsuit/flightsuit/dropped(mob/the_user)
if(deployedpack)
retract_flightpack(TRUE)
if(deployedshoes)
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index d9e4f17649..1bad198c73 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -275,7 +275,7 @@
visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDEFACIALHAIR
visor_flags = STOPSPRESSUREDAMAGE
-/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon()
+/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon_state()
icon_state = "hardsuit[on]-[item_color]"
/obj/item/clothing/head/helmet/space/hardsuit/syndi/Initialize()
@@ -336,7 +336,7 @@
linkedsuit.update_icon()
user.update_inv_wear_suit()
user.update_inv_w_uniform()
-
+ user.update_equipment_speed_mods()
/obj/item/clothing/suit/space/hardsuit/syndi
name = "blood-red hardsuit"
@@ -362,9 +362,6 @@
armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100)
heat_protection = HEAD
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
- visor_flags_inv = 0
- visor_flags = 0
- on = FALSE
resistance_flags = FIRE_PROOF | ACID_PROOF
/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/debug
@@ -705,7 +702,7 @@
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/on_mob_move)
listeningTo = user
-/obj/item/clothing/suit/space/hardsuit/ancient/dropped()
+/obj/item/clothing/suit/space/hardsuit/ancient/dropped(mob/user)
. = ..()
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
@@ -946,15 +943,9 @@
var/datum/action/A = X
A.UpdateButtonIcon()
-/obj/item/clothing/head/helmet/space/hardsuit/lavaknight/update_icon()
- var/mutable_appearance/helm_overlay = mutable_appearance(icon, "knight_cydonia_overlay")
-
- if(energy_color)
- helm_overlay.color = energy_color
-
- cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other
-
- add_overlay(helm_overlay)
+/obj/item/clothing/head/helmet/space/hardsuit/lavaknight/update_overlays()
+ . = ..()
+ . += mutable_appearance(icon, "knight_cydonia_overlay", color = energy_color)
/obj/item/clothing/head/helmet/space/hardsuit/lavaknight/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = ..()
@@ -983,15 +974,9 @@
set_light(1)
update_icon()
-/obj/item/clothing/suit/space/hardsuit/lavaknight/update_icon()
- var/mutable_appearance/suit_overlay = mutable_appearance(icon, "knight_cydonia_overlay")
-
- if(energy_color)
- suit_overlay.color = energy_color
-
- cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other
-
- add_overlay(suit_overlay)
+/obj/item/clothing/suit/space/hardsuit/lavaknight/update_overlays()
+ . = ..()
+ . += mutable_appearance(icon, "knight_cydonia_overlay", color = energy_color)
/obj/item/clothing/suit/space/hardsuit/lavaknight/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE)
. = ..()
diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm
index aca98df232..662e333f59 100644
--- a/code/modules/clothing/spacesuits/syndi.dm
+++ b/code/modules/clothing/spacesuits/syndi.dm
@@ -79,7 +79,6 @@
/obj/item/clothing/head/helmet/space/syndicate/contract
name = "contractor helmet"
desc = "A specialised black and gold helmet that's more compact than its standard Syndicate counterpart. Can be ultra-compressed into even the tightest of spaces."
- slowdown = 0.55
w_class = WEIGHT_CLASS_SMALL
icon_state = "syndicate-contract-helm"
item_state = "syndicate-contract-helm"
@@ -91,6 +90,7 @@
w_class = WEIGHT_CLASS_SMALL
icon_state = "syndicate-contract"
item_state = "syndicate-contract"
+ mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC|STYLE_PAW_TAURIC
//Black-green syndicate space suit
/obj/item/clothing/head/helmet/space/syndicate/black/green
diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm
index 79b221a807..cbbd64ebcc 100644
--- a/code/modules/clothing/suits/toggles.dm
+++ b/code/modules/clothing/suits/toggles.dm
@@ -45,7 +45,7 @@
var/datum/action/A = X
A.UpdateButtonIcon()
-/obj/item/clothing/suit/hooded/dropped()
+/obj/item/clothing/suit/hooded/dropped(mob/user)
..()
RemoveHood()
@@ -76,7 +76,7 @@
suit = null
return ..()
-/obj/item/clothing/head/hooded/dropped()
+/obj/item/clothing/head/hooded/dropped(mob/user)
..()
if(suit)
suit.RemoveHood()
@@ -174,7 +174,7 @@
else
helmet.forceMove(src)
-/obj/item/clothing/suit/space/hardsuit/dropped()
+/obj/item/clothing/suit/space/hardsuit/dropped(mob/user)
..()
RemoveHelmet()
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 68c5d8c5c8..490cd0ff21 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -59,7 +59,7 @@
var/datum/disease/dnaspread/DS = D
DS.strain_data["name"] = H.real_name
DS.strain_data["UI"] = H.dna.uni_identity
- DS.strain_data["SE"] = H.dna.struc_enzymes
+ DS.strain_data["SE"] = H.dna.mutation_index
else
D = new virus_type()
else
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index ab9ab96678..47cde0899b 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -157,7 +157,7 @@
active = FALSE
STOP_PROCESSING(SSobj,src)
-/obj/machinery/shuttle_scrambler/update_icon()
+/obj/machinery/shuttle_scrambler/update_icon_state()
if(active)
icon_state = "dominator-blue"
else
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 5ef30d0030..457b5bd4ec 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -4,6 +4,7 @@
weight = 2
min_players = 15
earliest_start = 30 MINUTES
+ gamemode_blacklist = list("dynamic")
/datum/round_event/portal_storm/syndicate_shocktroop
boss_types = list(/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper = 2)
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index 9dbe1443d1..b7f8b8f911 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -4,6 +4,7 @@
max_occurrences = 3
weight = 2
min_players = 2
+ gamemode_blacklist = list("dynamic")
/datum/round_event/wormholes
diff --git a/code/modules/fields/fields.dm b/code/modules/fields/fields.dm
index 5b0b4bc1f8..8710282021 100644
--- a/code/modules/fields/fields.dm
+++ b/code/modules/fields/fields.dm
@@ -312,7 +312,7 @@
else if(!operating)
QDEL_NULL(current)
-/obj/item/multitool/field_debug/dropped()
+/obj/item/multitool/field_debug/dropped(mob/user)
. = ..()
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
diff --git a/code/modules/food_and_drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm
index 2a313361c4..bd20ad8d69 100644
--- a/code/modules/food_and_drinks/food/customizables.dm
+++ b/code/modules/food_and_drinks/food/customizables.dm
@@ -318,14 +318,17 @@
..()
update_icon()
-/obj/item/reagent_containers/glass/bowl/update_icon()
- cut_overlays()
+
+/obj/item/reagent_containers/glass/bowl/update_icon_state()
+ if(!reagents || !reagents.total_volume)
+ icon_state = "bowl"
+
+/obj/item/reagent_containers/glass/bowl/update_overlays()
+ . = ..()
if(reagents && reagents.total_volume)
var/mutable_appearance/filling = mutable_appearance('icons/obj/food/soupsalad.dmi', "fullbowl")
filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
- else
- icon_state = "bowl"
+ . += filling
#undef INGREDIENTS_FILL
#undef INGREDIENTS_SCATTER
diff --git a/code/modules/food_and_drinks/food/snacks_burgers.dm b/code/modules/food_and_drinks/food/snacks_burgers.dm
index 35b17e67f0..9a4ef2ce89 100644
--- a/code/modules/food_and_drinks/food/snacks_burgers.dm
+++ b/code/modules/food_and_drinks/food/snacks_burgers.dm
@@ -323,6 +323,14 @@
tastes = list("bun" = 4, "bacon" = 2)
foodtype = GRAIN | MEAT
+/obj/item/reagent_containers/food/snacks/burger/empoweredburger
+ name = "empowered burger"
+ desc = "It's shockingly good, if you live off of electricity that is."
+ icon_state = "empoweredburger"
+ list_reagents = list(/datum/reagent/consumable/nutriment = 8, /datum/reagent/consumable/liquidelectricity = 5)
+ tastes = list("bun" = 2, "pure electricity" = 4)
+ foodtype = GRAIN | TOXIC
+
/obj/item/reagent_containers/food/snacks/burger/soylent
name = "soylent burger"
desc = "A eco-friendly burger made using upcycled low value biomass."
@@ -331,6 +339,14 @@
tastes = list("bun" = 2, "assistant" = 4)
foodtype = GRAIN | MEAT | DAIRY
+/obj/item/reagent_containers/food/snacks/burger/crab
+ name = "crab burger"
+ desc = "A delicious patty of the crabby kind, slapped in between a bun."
+ icon_state = "crabburger"
+ bonus_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/consumable/nutriment/vitamin = 3)
+ tastes = list("bun" = 2, "crab meat" = 4)
+ foodtype = GRAIN | MEAT
+
/obj/item/reagent_containers/food/snacks/burger/rib
name = "mcrib"
desc = "An elusive rib shaped burger with limited availablity across the galaxy. Not as good as you remember it."
diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm
index 7dc2188100..6c71318807 100644
--- a/code/modules/food_and_drinks/food/snacks_other.dm
+++ b/code/modules/food_and_drinks/food/snacks_other.dm
@@ -579,6 +579,16 @@
icon_state = "peachcanmaint"
tastes = list("peaches" = 1, "tin" = 7)
+/obj/item/reagent_containers/food/snacks/crab_rangoon
+ name = "Crab Rangoon"
+ desc = "Has many names, like crab puffs, cheese wontons, crab dumplings? Whatever you call them, they're a fabulous blast of cream cheesy crab."
+ icon_state = "crabrangoon"
+ list_reagents = list(/datum/reagent/consumable/nutriment = 10, /datum/reagent/consumable/nutriment/vitamin = 5)
+ filling_color = "#f2efdc"
+ w_class = WEIGHT_CLASS_SMALL
+ tastes = list("cream cheese" = 4, "crab" = 3, "crispiness" = 2)
+ foodtype = MEAT | DAIRY | GRAIN
+
/obj/item/reagent_containers/food/snacks/chocolatestrawberry
name = "Chocolate dipped strawberries"
desc = "A strawberry dipped in a bit of chocolate."
diff --git a/code/modules/food_and_drinks/food/snacks_soup.dm b/code/modules/food_and_drinks/food/snacks_soup.dm
index a0e1ebcb39..3d70b32f2e 100644
--- a/code/modules/food_and_drinks/food/snacks_soup.dm
+++ b/code/modules/food_and_drinks/food/snacks_soup.dm
@@ -228,6 +228,22 @@
tastes = list("beet" = 1)
foodtype = VEGETABLES
+/obj/item/reagent_containers/food/snacks/soup/onion
+ name = "french onion soup"
+ desc = "Good enough to make a grown mime cry."
+ icon_state = "onionsoup"
+ bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 5)
+ tastes = list("caramelized onions" = 1)
+ foodtype = VEGETABLES
+
+/obj/item/reagent_containers/food/snacks/soup/bisque
+ name = "bisque"
+ desc = "A classic entree from Space-France."
+ icon_state = "bisque"
+ bonus_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 6)
+ tastes = list("creamy texture" = 1, "crab" = 4)
+ foodtype = MEAT
+
/obj/item/reagent_containers/food/snacks/soup/electron
name = "electron soup"
desc = "A gastronomic curiosity of ethereal origin. It is famed for the minature weather system formed over a properly prepared soup."
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index 24f60300fa..5117439049 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -21,12 +21,12 @@
add_overlay("grjam")
/obj/machinery/gibber/RefreshParts()
- var/gib_time = 40
+ gibtime = 40
+ meat_produced = 0
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
meat_produced += B.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
- gib_time -= 5 * M.rating
- gibtime = gib_time
+ gibtime -= 5 * M.rating
if(M.rating >= 2)
ignore_clothing = TRUE
@@ -38,18 +38,18 @@
if(M.rating >= 2)
. += "Gibber has been upgraded to process inorganic materials."
-/obj/machinery/gibber/update_icon()
- cut_overlays()
+/obj/machinery/gibber/update_overlays()
+ . = ..()
if (dirty)
- add_overlay("grbloody")
+ . += "grbloody"
if(stat & (NOPOWER|BROKEN))
return
if (!occupant)
- add_overlay("grjam")
+ . += "grjam"
else if (operating)
- add_overlay("gruse")
+ . += "gruse"
else
- add_overlay("gridle")
+ . += "gridle"
/obj/machinery/gibber/attack_paw(mob/user)
return attack_hand(user)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/grill.dm b/code/modules/food_and_drinks/kitchen_machinery/grill.dm
index c52389cd56..547ed244c0 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/grill.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/grill.dm
@@ -15,7 +15,7 @@
. = ..()
grill_loop = new(list(src), FALSE)
-/obj/machinery/grill/update_icon()
+/obj/machinery/grill/update_icon_state()
if(grilled_item)
icon_state = "grill"
else if(grill_fuel)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
index 24dd99e5e0..2adff414e8 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
@@ -85,7 +85,7 @@
. += "- Capacity: [max_n_of_items] items."
. += "- Cook time reduced by [(efficiency - 1) * 25]%."
-/obj/machinery/microwave/update_icon()
+/obj/machinery/microwave/update_icon_state()
if(broken)
icon_state = "mwb"
else if(dirty_anim_playing)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index 7619f68448..67a636eb9c 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -42,7 +42,7 @@
..()
update_icon()
-/obj/machinery/smartfridge/update_icon()
+/obj/machinery/smartfridge/update_icon_state()
if(!stat)
if(visible_contents)
switch(contents.len)
@@ -289,13 +289,12 @@
..()
update_icon()
-/obj/machinery/smartfridge/drying_rack/update_icon()
- ..()
- cut_overlays()
+/obj/machinery/smartfridge/drying_rack/update_overlays()
+ . = ..()
if(drying)
- add_overlay("drying_rack_drying")
+ . += "drying_rack_drying"
if(contents.len)
- add_overlay("drying_rack_filled")
+ . += "drying_rack_filled"
/obj/machinery/smartfridge/drying_rack/process()
..()
diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm
index 0b297648c5..c15c1cd2f5 100644
--- a/code/modules/food_and_drinks/recipes/food_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm
@@ -1,6 +1,7 @@
/datum/crafting_recipe/food
var/real_parts
category = CAT_FOOD
+ time = 5
/datum/crafting_recipe/food/New()
real_parts = parts.Copy()
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm
index cf96c01e7b..5789b8daeb 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm
@@ -150,6 +150,24 @@
result = /obj/item/reagent_containers/food/snacks/burger/chicken
subcategory = CAT_BURGER
+/datum/crafting_recipe/food/crabburger
+ name = "Crab Burger"
+ reqs = list(
+ /obj/item/reagent_containers/food/snacks/meat/crab = 2,
+ /obj/item/reagent_containers/food/snacks/bun = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/burger/crab
+ subcategory = CAT_BURGER
+
+/datum/crafting_recipe/food/empoweredburger
+ name = "Empowered Burger"
+ reqs = list(
+ /obj/item/stack/sheet/mineral/plasma = 2,
+ /obj/item/reagent_containers/food/snacks/bun = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/burger/empoweredburger
+ subcategory = CAT_BURGER
+
/datum/crafting_recipe/food/fishburger
name = "Fish burger"
reqs = list(
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm
index 6a622d6719..479e116801 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm
@@ -171,3 +171,16 @@
)
result = /obj/item/reagent_containers/food/snacks/salad/ricepudding
subcategory = CAT_MISCFOOD
+
+/datum/crafting_recipe/food/butterbear //ITS ALIVEEEEEE!
+ name = "Living bear/butter hybrid"
+ reqs = list(
+ /obj/item/organ/brain = 1,
+ /obj/item/organ/heart = 1,
+ /obj/item/reagent_containers/food/snacks/butter = 10,
+ /obj/item/reagent_containers/food/snacks/meat/slab = 5,
+ /datum/reagent/blood = 50,
+ /datum/reagent/teslium = 1 //To shock the whole thing into life
+ )
+ result = /mob/living/simple_animal/hostile/bear/butter
+ subcategory = CAT_MISCFOOD
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm
index 4fc12f0777..45083e0374 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm
@@ -97,6 +97,17 @@
result = /obj/item/reagent_containers/food/snacks/tuna
subcategory = CAT_SEAFOOD
+/datum/crafting_recipe/food/crab_rangoon
+ name = "Crab Rangoon"
+ reqs = list(
+ /obj/item/reagent_containers/food/snacks/doughslice = 1,
+ /datum/reagent/consumable/cream = 5,
+ /obj/item/reagent_containers/food/snacks/cheesewedge = 1,
+ /obj/item/reagent_containers/food/snacks/meat/crab = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/crab_rangoon
+ subcategory = CAT_SEAFOOD
+
/datum/crafting_recipe/food/cubancarp
name = "Cuban carp"
reqs = list(
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm
index 0c966faf33..ac49537adf 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm
@@ -24,6 +24,16 @@
result = /obj/item/reagent_containers/food/snacks/soup/beet
subcategory = CAT_SOUP
+/datum/crafting_recipe/food/bisque
+ name = "Bisque"
+ reqs = list(
+ /datum/reagent/water = 10,
+ /obj/item/reagent_containers/food/snacks/meat/crab = 1,
+ /obj/item/reagent_containers/food/snacks/salad/boiledrice = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/soup/bisque
+ subcategory = CAT_SOUP
+
/datum/crafting_recipe/food/bloodsoup
name = "Blood soup"
reqs = list(
@@ -67,6 +77,17 @@
result = /obj/item/reagent_containers/food/snacks/soup/tomato/eyeball
subcategory = CAT_SOUP
+/datum/crafting_recipe/food/onionsoup
+ name = "French onion soup"
+ reqs = list(
+ /datum/reagent/water = 10,
+ /obj/item/reagent_containers/glass/bowl = 1,
+ /obj/item/reagent_containers/food/snacks/grown/onion = 1,
+ /obj/item/reagent_containers/food/snacks/cheesewedge = 1,
+ )
+ result = /obj/item/reagent_containers/food/snacks/soup/onion
+ subcategory = CAT_SOUP
+
/datum/crafting_recipe/food/hotchili
name = "Hot chili"
reqs = list(
diff --git a/code/modules/games/cas.dm b/code/modules/games/cas.dm
index 4fbb931b32..77db8dbe3f 100644
--- a/code/modules/games/cas.dm
+++ b/code/modules/games/cas.dm
@@ -93,7 +93,7 @@
qdel(SC)
update_icon()
-/obj/item/toy/cards/deck/cas/update_icon()
+/obj/item/toy/cards/deck/cas/update_icon_state()
if(cards.len < 26)
icon_state = "deck_[deckstyle]_low"
@@ -136,7 +136,7 @@
Flip()
return TRUE
-/obj/item/toy/cards/singlecard/cas/update_icon()
+/obj/item/toy/cards/singlecard/cas/update_icon_state()
if(flipped)
icon_state = "[card_face]_flipped"
else
diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm
index e6aed7a8ca..884769ebd4 100644
--- a/code/modules/holodeck/items.dm
+++ b/code/modules/holodeck/items.dm
@@ -196,7 +196,7 @@
if(numbuttons == numready)
begin_event()
-/obj/machinery/readybutton/update_icon()
+/obj/machinery/readybutton/update_icon_state()
if(ready)
icon_state = "auth_on"
else
diff --git a/code/modules/hydroponics/beekeeping/honeycomb.dm b/code/modules/hydroponics/beekeeping/honeycomb.dm
index 5bb2936c1e..1a3e1bc1be 100644
--- a/code/modules/hydroponics/beekeeping/honeycomb.dm
+++ b/code/modules/hydroponics/beekeeping/honeycomb.dm
@@ -20,13 +20,13 @@
update_icon()
-/obj/item/reagent_containers/honeycomb/update_icon()
- cut_overlays()
+/obj/item/reagent_containers/honeycomb/update_overlays()
+ . = ..()
var/mutable_appearance/honey_overlay = mutable_appearance(icon, "honey")
if(honey_color)
honey_overlay.icon_state = "greyscale_honey"
honey_overlay.color = honey_color
- add_overlay(honey_overlay)
+ . += honey_overlay
/obj/item/reagent_containers/honeycomb/proc/set_reagent(reagent)
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index f7715e7320..6a3fd56208 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -61,7 +61,7 @@
/obj/machinery/biogenerator/on_reagent_change(changetype) //When the reagents change, change the icon as well.
update_icon()
-/obj/machinery/biogenerator/update_icon()
+/obj/machinery/biogenerator/update_icon_state()
if(panel_open)
icon_state = "biogen-empty-o"
else if(!src.beaker)
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
index ab6625b46d..1c31113332 100644
--- a/code/modules/hydroponics/fermenting_barrel.dm
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -64,7 +64,7 @@
to_chat(user, "You close [src], letting you draw from its tap.")
update_icon()
-/obj/structure/fermenting_barrel/update_icon()
+/obj/structure/fermenting_barrel/update_icon_state()
if(open)
icon_state = "barrel_open"
else
diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm
index fe6d13f69e..4e545c13ee 100644
--- a/code/modules/hydroponics/gene_modder.dm
+++ b/code/modules/hydroponics/gene_modder.dm
@@ -53,17 +53,18 @@
min_wchance = 0
min_wrate = 0
-/obj/machinery/plantgenes/update_icon()
- ..()
- cut_overlays()
+/obj/machinery/plantgenes/update_icon_state()
if((stat & (BROKEN|NOPOWER)))
icon_state = "dnamod-off"
else
icon_state = "dnamod"
+
+/obj/machinery/plantgenes/update_overlays()
+ . = ..()
if(seed)
- add_overlay("dnamod-dna")
+ . += "dnamod-dna"
if(panel_open)
- add_overlay("dnamod-open")
+ . += "dnamod-open"
/obj/machinery/plantgenes/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, "dnamod", "dnamod", I))
diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm
index 7668d17ac1..0411a80443 100644
--- a/code/modules/hydroponics/grown/banana.dm
+++ b/code/modules/hydroponics/grown/banana.dm
@@ -151,7 +151,7 @@
spawn(30)
if(!QDELETED(src))
var/mob/living/simple_animal/banana_spider/S = new /mob/living/simple_animal/banana_spider(get_turf(src.loc))
- S.speed += round(10 / seed.potency)
+ S.speed += round(10 / max(seed.potency, 1), 1)
S.visible_message("The banana spider chitters as it stretches its legs.")
qdel(src)
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 272f44267e..b3ec36e0bb 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -250,14 +250,14 @@
/datum/plant_gene/trait/cell_charge/on_slip(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/C)
var/power = G.seed.potency*rate
if(prob(power))
- C.electrocute_act(round(power), G, 1, 1)
+ C.electrocute_act(round(power), G, 1, SHOCK_NOGLOVES)
/datum/plant_gene/trait/cell_charge/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target)
if(iscarbon(target))
var/mob/living/carbon/C = target
var/power = G.seed.potency*rate
if(prob(power))
- C.electrocute_act(round(power), G, 1, 1)
+ C.electrocute_act(round(power), G, 1, SHOCK_NOGLOVES)
/datum/plant_gene/trait/cell_charge/on_consume(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/target)
if(!G.reagents.total_volume)
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
index 2a0cf8c334..9026c45f82 100644
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -305,17 +305,17 @@
/obj/item/electronic_assembly/proc/can_move()
return FALSE
-/obj/item/electronic_assembly/update_icon()
+/obj/item/electronic_assembly/update_icon_state()
if(opened)
icon_state = initial(icon_state) + "-open"
else
icon_state = initial(icon_state)
- cut_overlays()
+
+/obj/item/electronic_assembly/update_overlays()
+ . = ..()
if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off.
return
- var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color")
- detail_overlay.color = detail_color
- add_overlay(detail_overlay)
+ . += mutable_appearance('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color", color = detail_color)
/obj/item/electronic_assembly/proc/return_total_complexity()
. = 0
diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm
index 52739c7940..9720bccfe5 100644
--- a/code/modules/integrated_electronics/core/detailer.dm
+++ b/code/modules/integrated_electronics/core/detailer.dm
@@ -34,18 +34,17 @@
.=..()
update_icon()
-/obj/item/integrated_electronics/detailer/update_icon()
- cut_overlays()
- var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/assemblies/electronic_tools.dmi', "detailer-color")
- detail_overlay.color = detail_color
- add_overlay(detail_overlay)
+/obj/item/integrated_electronics/detailer/update_overlays()
+ . = ..()
+ . += mutable_appearance('icons/obj/assemblies/electronic_tools.dmi', "detailer-color", color = detail_color)
+
/obj/item/integrated_electronics/detailer/attack_self(mob/user)
var/color_choice = input(user, "Select color.", "Assembly Detailer") as null|anything in color_list
if(!color_list[color_choice])
return
if(!in_range(src, user))
- return
+ return
if(color_choice == "custom")
detail_color = input(user,"","Choose Color",detail_color) as color|null
else
diff --git a/code/modules/integrated_electronics/core/wirer.dm b/code/modules/integrated_electronics/core/wirer.dm
index 9550f9b4ae..95f46564cb 100644
--- a/code/modules/integrated_electronics/core/wirer.dm
+++ b/code/modules/integrated_electronics/core/wirer.dm
@@ -15,7 +15,7 @@
var/datum/integrated_io/selected_io = null
var/mode = WIRE
-/obj/item/integrated_electronics/wirer/update_icon()
+/obj/item/integrated_electronics/wirer/update_icon_state()
icon_state = "wirer-[mode]"
/obj/item/integrated_electronics/wirer/proc/wire(var/datum/integrated_io/io, mob/user)
@@ -27,7 +27,7 @@
selected_io = io
to_chat(user, "You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.")
mode = WIRING
- update_icon()
+ update_icon()
if(WIRING)
if(io == selected_io)
to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.")
diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
index 3183a6d0e6..d1cd852651 100644
--- a/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -402,11 +402,22 @@
power_draw_per_use = 40
ext_cooldown = 1
cooldown_per_use = 10
- var/list/mtypes = list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/uranium, /datum/material/plasma, /datum/material/bluespace, /datum/material/bananium, /datum/material/titanium, /datum/material/plastic)
+ var/static/list/mtypes = list(
+ /datum/material/iron,
+ /datum/material/glass,
+ /datum/material/silver,
+ /datum/material/gold,
+ /datum/material/diamond,
+ /datum/material/uranium,
+ /datum/material/plasma,
+ /datum/material/bluespace,
+ /datum/material/bananium,
+ /datum/material/titanium,
+ /datum/material/plastic
+ )
-/obj/item/integrated_circuit/manipulation/matman/Initialize()
- var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
- mtypes, 100000, FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
+/obj/item/integrated_circuit/manipulation/matman/ComponentInitialize()
+ var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, mtypes, 100000, FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
materials.precise_insertion = TRUE
.=..()
diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm
index d7f59ff883..6efa95cd91 100644
--- a/code/modules/jobs/job_types/geneticist.dm
+++ b/code/modules/jobs/job_types/geneticist.dm
@@ -32,4 +32,5 @@
backpack = /obj/item/storage/backpack/genetics
satchel = /obj/item/storage/backpack/satchel/gen
duffelbag = /obj/item/storage/backpack/duffelbag/med
+ l_pocket = /obj/item/sequence_scanner
diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm
index 964ee65047..36b4775c34 100644
--- a/code/modules/keybindings/bindings_mob.dm
+++ b/code/modules/keybindings/bindings_mob.dm
@@ -61,16 +61,28 @@
if(client.keys_held["Ctrl"])
switch(SSinput.movement_keys[_key])
if(NORTH)
- northface()
+ if(client.keys_held["Shift"])
+ northshift()
+ else
+ northface()
return
if(SOUTH)
- southface()
+ if(client.keys_held["Shift"])
+ southshift()
+ else
+ southface()
return
if(WEST)
- westface()
+ if(client.keys_held["Shift"])
+ westshift()
+ else
+ westface()
return
if(EAST)
- eastface()
+ if(client.keys_held["Shift"])
+ eastshift()
+ else
+ eastface()
return
return ..()
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index bfd0ae03cb..89fa3ac6cf 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -147,11 +147,8 @@
qdel(src)
-/obj/structure/bookcase/update_icon()
- if(contents.len < 5)
- icon_state = "book-[contents.len]"
- else
- icon_state = "book-5"
+/obj/structure/bookcase/update_icon_state()
+ icon_state = "book-[min(length(contents), 5)]"
/obj/structure/bookcase/manuals/medical
diff --git a/code/modules/mapping/mapping_helpers/_mapping_helpers.dm b/code/modules/mapping/mapping_helpers/_mapping_helpers.dm
index 99184b1c3e..4de16ba350 100644
--- a/code/modules/mapping/mapping_helpers/_mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers/_mapping_helpers.dm
@@ -97,7 +97,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
if(target_type && !istype(A,target_type))
continue
var/cargs = build_args()
- A.AddComponent(arglist(cargs))
+ A._AddComponent(cargs)
qdel(src)
return
diff --git a/code/modules/mapping/minimaps.dm b/code/modules/mapping/minimaps.dm
new file mode 100644
index 0000000000..c347a8d7a6
--- /dev/null
+++ b/code/modules/mapping/minimaps.dm
@@ -0,0 +1,155 @@
+/datum/minimap
+ var/name
+ var/icon/map_icon
+ var/icon/meta_icon
+ var/icon/overlay_icon
+ var/list/color_area_names = list()
+ var/minx
+ var/maxx
+ var/miny
+ var/maxy
+ var/z_level
+ var/id = 0
+ var/static/next_id = 0
+
+/datum/minimap/New(z, x1 = 1, y1 = 1, x2 = world.maxx, y2 = world.maxy, name)
+ src.name = name
+ id = ++next_id
+ z_level = z
+
+ var/crop_x1 = x2
+ var/crop_x2 = x1
+ var/crop_y1 = y2
+ var/crop_y2 = y1
+
+ // do the generating
+ map_icon = new('html/blank.png')
+ meta_icon = new('html/blank.png')
+ map_icon.Scale(x2-x1+1, y2-y1+1) // arrays start at 1
+ meta_icon.Scale(x2-x1+1, y2-y1+1)
+ var/list/area_to_color = list()
+ for(var/turf/T in block(locate(x1,y1,z),locate(x2,y2,z)))
+ var/area/A = T.loc
+ var/img_x = T.x - x1 + 1 // arrays start at 1
+ var/img_y = T.y - y1 + 1
+ if(!istype(A, /area/space) || istype(T, /turf/closed/wall))
+ crop_x1 = min(crop_x1, T.x)
+ crop_x2 = max(crop_x2, T.x)
+ crop_y1 = min(crop_y1, T.y)
+ crop_y2 = max(crop_y2, T.y)
+ var/meta_color = area_to_color[A]
+ if(!meta_color)
+ meta_color = rgb(rand(0,255),rand(0,255),rand(0,255)) // technically conflicts could happen but it's like very unlikely and it's not that big of a deal if one happens
+ area_to_color[A] = meta_color
+ color_area_names[meta_color] = A.name
+ meta_icon.DrawBox(meta_color, img_x, img_y)
+ if(istype(T, /turf/closed/wall))
+ map_icon.DrawBox("#000000", img_x, img_y)
+ else if(!istype(A, /area/space))
+ var/color = A.minimap_color || "#FF00FF"
+ if(locate(/obj/machinery/power/solar) in T)
+ color = "#02026a"
+ if((locate(/obj/effect/spawner/structure/window) in T) || (locate(/obj/structure/grille) in T))
+ color = BlendRGB(color, "#000000", 0.5)
+ map_icon.DrawBox(color, img_x, img_y)
+ map_icon.Crop(crop_x1, crop_y1, crop_x2, crop_y2)
+ meta_icon.Crop(crop_x1, crop_y1, crop_x2, crop_y2)
+ minx = crop_x1
+ maxx = crop_x2
+ miny = crop_y1
+ maxy = crop_y2
+ overlay_icon = new(map_icon)
+ overlay_icon.Scale(16, 16)
+
+/datum/minimap/proc/send(mob/user)
+ register_asset("minimap-[id].png", map_icon)
+ register_asset("minimap-[id]-meta.png", meta_icon)
+ send_asset_list(user, list("minimap-[id].png" = map_icon, "minimap-[id]-meta.png" = meta_icon), verify=FALSE)
+
+/datum/minimap_group
+ var/list/minimaps
+ var/static/next_id = 0
+ var/id
+ var/name
+
+/datum/minimap_group/New(list/maps, name)
+ id = ++next_id
+ src.name = name
+ minimaps = maps || list()
+
+/datum/minimap_group/proc/show(mob/user)
+ if(!length(minimaps))
+ to_chat(user, "ERROR: Attempted to access an empty datum/minimap_group. This should probably not happen.")
+ return
+ var/list/datas = list()
+ var/list/info = list()
+ var/datum/minimap/first_map = minimaps[1]
+ for(var/i in 1 to length(minimaps))
+ var/datum/minimap/M = minimaps[i]
+ M.send(user)
+ info += "
"
+ datas += json_encode(M.color_area_names);
+ info = info.Join()
+
+ var/html = {"
+
+
+
+
+
+
+[name]
+
+[info]
+"}
+
+ user << browse(html, "window=minimap_[id];size=768x[round(768 / first_map.map_icon.Width() * first_map.map_icon.Height() + 50)]")
diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm
index 5165d95340..e83ac26153 100644
--- a/code/modules/mining/equipment/explorer_gear.dm
+++ b/code/modules/mining/equipment/explorer_gear.dm
@@ -110,12 +110,12 @@
AddComponent(/datum/component/spraycan_paintable)
update_icon()
-/obj/item/clothing/head/helmet/space/hostile_environment/update_icon()
- ..()
- cut_overlays()
+
+/obj/item/clothing/head/helmet/space/hostile_environment/update_overlays()
+ . = ..()
var/mutable_appearance/glass_overlay = mutable_appearance(icon, "hostile_env_glass")
glass_overlay.appearance_flags = RESET_COLOR
- add_overlay(glass_overlay)
+ . += glass_overlay
/obj/item/clothing/head/helmet/space/hostile_environment/worn_overlays(isinhands, icon_file, style_flags = NONE)
. = ..()
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index d4b86d5138..b515ebbc13 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -155,19 +155,16 @@
else
set_light(0)
-/obj/item/twohanded/kinetic_crusher/update_icon()
- ..()
- cut_overlays()
- if(!charged)
- add_overlay("[icon_state]_uncharged")
- if(light_on)
- add_overlay("[icon_state]_lit")
- spawn(1)
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
+/obj/item/twohanded/kinetic_crusher/update_icon_state()
item_state = "crusher[wielded]"
+/obj/item/twohanded/kinetic_crusher/update_overlays()
+ . = ..()
+ if(!charged)
+ . += "[icon_state]_uncharged"
+ if(light_on)
+ . += "[icon_state]_lit"
+
//destablizing force
/obj/item/projectile/destabilizer
name = "destabilizing force"
diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm
index 9d595664ff..8853a56911 100644
--- a/code/modules/mining/equipment/marker_beacons.dm
+++ b/code/modules/mining/equipment/marker_beacons.dm
@@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(marker_beacon_colors, list(
. += "Use in-hand to place a [singular_name]."
. += "Alt-click to select a color. Current color is [picked_color]."
-/obj/item/stack/marker_beacon/update_icon()
+/obj/item/stack/marker_beacon/update_icon_state()
icon_state = "[initial(icon_state)][lowertext(picked_color)]"
/obj/item/stack/marker_beacon/attack_self(mob/user)
diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm
index e8ba72e62f..ae451ba1a8 100644
--- a/code/modules/mining/equipment/regenerative_core.dm
+++ b/code/modules/mining/equipment/regenerative_core.dm
@@ -129,14 +129,13 @@
. = ..()
update_icon()
-/obj/item/organ/regenerative_core/update_icon()
+/obj/item/organ/regenerative_core/update_icon_state()
icon_state = inert ? "legion_soul_inert" : "legion_soul"
- cut_overlays()
+
+/obj/item/organ/regenerative_core/update_overlays()
+ . = ..()
if(!inert && !preserved)
- add_overlay("legion_soul_crackle")
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
+ . += "legion_soul_crackle"
/obj/item/organ/regenerative_core/legion/go_inert()
..()
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 68e70dc882..b9f85a5c8f 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -139,11 +139,10 @@
icon = 'icons/obj/lavaland/survival_pod.dmi'
icon_state = "sleeper"
-/obj/machinery/sleeper/survival_pod/update_icon()
- if(state_open)
- cut_overlays()
- else
- add_overlay("sleeper_cover")
+/obj/machinery/sleeper/survival_pod/update_overlays()
+ . = ..()
+ if(!state_open)
+ . += "sleeper_cover"
//Computer
/obj/item/gps/computer
@@ -190,8 +189,9 @@
flags_1 = NODECONSTRUCT_1
var/empty = FALSE
-/obj/machinery/smartfridge/survival_pod/update_icon()
- return
+/obj/machinery/stasis/survival_pod/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/machinery/smartfridge/survival_pod/Initialize(mapload)
. = ..()
diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm
index 43c8dec129..38830fd824 100644
--- a/code/modules/mining/lavaland/ash_flora.dm
+++ b/code/modules/mining/lavaland/ash_flora.dm
@@ -201,15 +201,15 @@
icon = 'icons/obj/lavaland/ash_flora.dmi'
icon_state = "mushroom_bowl"
-/obj/item/reagent_containers/glass/bowl/mushroom_bowl/update_icon()
- cut_overlays()
- if(reagents && reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/lavaland/ash_flora.dmi', "fullbowl")
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
- else
+/obj/item/reagent_containers/glass/bowl/mushroom_bowl/update_icon_state()
+ if(!reagents || !reagents.total_volume)
icon_state = "mushroom_bowl"
+/obj/item/reagent_containers/glass/bowl/mushroom_bowl/update_overlays()
+ . = ..()
+ if(reagents && reagents.total_volume)
+ . += mutable_appearance('icons/obj/lavaland/ash_flora.dmi', "fullbowl", color = mix_color_from_reagents(reagents.reagent_list))
+
/obj/item/reagent_containers/glass/bowl/mushroom_bowl/attackby(obj/item/I,mob/user, params)
if(istype(I, /obj/item/reagent_containers/food/snacks))
var/obj/item/reagent_containers/food/snacks/S = I
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 343083456d..bf25bb9da8 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -463,30 +463,45 @@
if(cooldown < world.time)
SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1)
cooldown = world.time + 600
- user.visible_message("[user] vanishes from reality, leaving a hole in [user.p_their()] place!")
- var/obj/effect/immortality_talisman/Z = new(get_turf(src.loc))
- Z.name = "hole in reality"
- Z.desc = "It's shaped an awful lot like [user.name]."
- Z.setDir(user.dir)
- user.forceMove(Z)
- user.notransform = 1
- user.status_flags |= GODMODE
- addtimer(CALLBACK(src, .proc/return_to_reality, user, Z), 100)
+ new /obj/effect/immortality_talisman(get_turf(user), user)
else
to_chat(user, "[src] is not ready yet!")
-/obj/item/immortality_talisman/proc/return_to_reality(mob/user, obj/effect/immortality_talisman/Z)
- user.status_flags &= ~GODMODE
- user.notransform = 0
- user.forceMove(get_turf(Z))
- user.visible_message("[user] pops back into reality!")
- Z.can_destroy = TRUE
- qdel(Z)
-
/obj/effect/immortality_talisman
+ name = "hole in reality"
+ desc = "It's shaped an awful lot like a person."
icon_state = "blank"
icon = 'icons/effects/effects.dmi'
- var/can_destroy = FALSE
+ var/vanish_description = "vanishes from reality"
+ var/can_destroy = TRUE
+
+/obj/effect/immortality_talisman/Initialize(mapload, mob/new_user)
+ . = ..()
+ if(new_user)
+ vanish(new_user)
+
+/obj/effect/immortality_talisman/proc/vanish(mob/user)
+ user.visible_message("[user] [vanish_description], leaving a hole in [user.p_their()] place!")
+
+ desc = "It's shaped an awful lot like [user.name]."
+ setDir(user.dir)
+
+ user.forceMove(src)
+ user.notransform = TRUE
+ user.status_flags |= GODMODE
+
+ can_destroy = FALSE
+
+ addtimer(CALLBACK(src, .proc/unvanish, user), 10 SECONDS)
+
+/obj/effect/immortality_talisman/proc/unvanish(mob/user)
+ user.status_flags &= ~GODMODE
+ user.notransform = FALSE
+ user.forceMove(get_turf(src))
+
+ user.visible_message("[user] pops back into reality!")
+ can_destroy = TRUE
+ qdel(src)
/obj/effect/immortality_talisman/attackby()
return
@@ -503,6 +518,9 @@
else
. = ..()
+/obj/effect/immortality_talisman/void
+ vanish_description = "is dragged into the void"
+
//Shared Bag
@@ -561,7 +579,7 @@
desc = "A flask with an almost-holy aura emitting from it. The label on the bottle says: 'erqo'hyy tvi'rf lbh jv'atf'."
list_reagents = list(/datum/reagent/flightpotion = 5)
-/obj/item/reagent_containers/glass/bottle/potion/update_icon()
+/obj/item/reagent_containers/glass/bottle/potion/update_icon_state()
if(reagents.total_volume)
icon_state = "potionflask"
else
@@ -957,7 +975,7 @@
if(1)
new /obj/item/mayhem(src)
if(2)
- new /obj/item/blood_contract(src)
+ new /obj/item/gun/ballistic/revolver/doublebarrel/super(src)
if(3)
new /obj/item/gun/magic/staff/spellblade(src)
@@ -1028,6 +1046,17 @@
log_combat(user, L, "took out a blood contract on", src)
qdel(src)
+/obj/item/gun/ballistic/revolver/doublebarrel/super
+ name = "super combat shotgun"
+ desc = "From the belly of the beast - or rather, demon. Twice as lethal as a less-than-super shotgun, but a tad bulkier."
+ icon_state = "heckgun"
+ slot_flags = null
+ mag_type = /obj/item/ammo_box/magazine/internal/shot/dual/heck
+ burst_size = 2
+ burst_shot_delay = 0
+ unique_reskin = null
+ sawn_off = TRUE
+
//Colossus
/obj/structure/closet/crate/necropolis/colossus
name = "colossus chest"
@@ -1072,6 +1101,10 @@
var/teleporting = FALSE //if we ARE teleporting
var/friendly_fire_check = FALSE //if the blasts we make will consider our faction against the faction of hit targets
+/obj/item/hierophant_club/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
/obj/item/hierophant_club/examine(mob/user)
. = ..()
. += "The[beacon ? " beacon is not currently":"re is a beacon"] attached."
@@ -1134,13 +1167,8 @@
chaser_speed = max(chaser_speed + health_percent, 0.5) //one tenth of a second faster for each missing 10% of health
blast_range -= round(health_percent * 10) //one additional range for each missing 10% of health
-/obj/item/hierophant_club/update_icon()
- icon_state = "hierophant_club[timer <= world.time ? "_ready":""][(beacon && !QDELETED(beacon)) ? "":"_beacon"]"
- item_state = icon_state
- if(ismob(loc))
- var/mob/M = loc
- M.update_inv_hands()
- M.update_inv_back()
+/obj/item/hierophant_club/update_icon_state()
+ icon_state = item_state = "hierophant_club[timer <= world.time ? "_ready":""][(beacon && !QDELETED(beacon)) ? "":"_beacon"]"
/obj/item/hierophant_club/proc/prepare_icon_update()
update_icon()
diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm
index 0f0ca8bf0c..2f6e8a9b7c 100644
--- a/code/modules/mining/machine_redemption.dm
+++ b/code/modules/mining/machine_redemption.dm
@@ -341,9 +341,8 @@
..()
update_icon()
-/obj/machinery/mineral/ore_redemption/update_icon()
+/obj/machinery/mineral/ore_redemption/update_icon_state()
if(powered())
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]-off"
- return
diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm
index c0eba668f1..c0356dd1ab 100644
--- a/code/modules/mining/machine_silo.dm
+++ b/code/modules/mining/machine_silo.dm
@@ -15,14 +15,20 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
/obj/machinery/ore_silo/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/material_container,
- list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace, /datum/material/plastic),
- INFINITY,
- FALSE,
- /obj/item/stack,
- null,
- null,
- TRUE)
+ var/static/list/materials_list = list(
+ /datum/material/iron,
+ /datum/material/glass,
+ /datum/material/silver,
+ /datum/material/gold,
+ /datum/material/diamond,
+ /datum/material/plasma,
+ /datum/material/uranium,
+ /datum/material/bananium,
+ /datum/material/titanium,
+ /datum/material/bluespace,
+ /datum/material/plastic,
+ )
+ AddComponent(/datum/component/material_container, materials_list, INFINITY, allowed_types=/obj/item/stack, _disable_attackby=TRUE)
if (!GLOB.ore_silo_default && mapload && is_station_level(z))
GLOB.ore_silo_default = src
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index 78b1a665db..dd3b13d8dd 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -87,7 +87,7 @@
..()
update_icon()
-/obj/machinery/mineral/equipment_vendor/update_icon()
+/obj/machinery/mineral/equipment_vendor/update_icon_state()
if(powered())
icon_state = initial(icon_state)
else
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index 4f9bad2f0f..c7c34b0389 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -20,25 +20,24 @@
mats_per_stack = MINERAL_MATERIAL_AMOUNT
var/list/stack_overlays
-/obj/item/stack/ore/update_icon()
+/obj/item/stack/ore/update_overlays()
+ . = ..()
var/difference = min(ORESTACK_OVERLAYS_MAX, amount) - (LAZYLEN(stack_overlays)+1)
if(difference == 0)
return
else if(difference < 0 && LAZYLEN(stack_overlays)) //amount < stack_overlays, remove excess.
- cut_overlays()
if (LAZYLEN(stack_overlays)-difference <= 0)
- stack_overlays = null;
+ stack_overlays = null
else
stack_overlays.len += difference
else if(difference > 0) //amount > stack_overlays, add some.
- cut_overlays()
for(var/i in 1 to difference)
var/mutable_appearance/newore = mutable_appearance(icon, icon_state)
newore.pixel_x = rand(-8,8)
newore.pixel_y = rand(-8,8)
LAZYADD(stack_overlays, newore)
if (stack_overlays)
- add_overlay(stack_overlays)
+ . += stack_overlays
/obj/item/stack/ore/welder_act(mob/living/user, obj/item/I)
if(!refined_type)
diff --git a/code/modules/mining/point_bank.dm b/code/modules/mining/point_bank.dm
index 8d0bb4e1e4..11f23a5d7c 100644
--- a/code/modules/mining/point_bank.dm
+++ b/code/modules/mining/point_bank.dm
@@ -41,9 +41,8 @@
..()
update_icon()
-/obj/machinery/point_bank/update_icon()
+/obj/machinery/point_bank/update_icon_state()
if(powered())
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]-off"
- return
\ No newline at end of file
diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm
index ff1d9231f7..a381cc512a 100644
--- a/code/modules/mob/camera/camera.dm
+++ b/code/modules/mob/camera/camera.dm
@@ -11,17 +11,6 @@
invisibility = INVISIBILITY_ABSTRACT // No one can see us
sight = SEE_SELF
move_on_shuttle = FALSE
- var/call_life = FALSE //TRUE if Life() should be called on this camera every tick of the mobs subystem, as if it were a living mob
-
-/mob/camera/Initialize()
- . = ..()
- if(call_life)
- GLOB.living_cameras += src
-
-/mob/camera/Destroy()
- . = ..()
- if(call_life)
- GLOB.living_cameras -= src
/mob/camera/experience_pressure_difference()
return
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index b891525e42..c201f437e4 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -14,6 +14,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
move_resist = INFINITY
see_invisible = SEE_INVISIBLE_OBSERVER
see_in_dark = 100
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
invisibility = INVISIBILITY_OBSERVER
hud_type = /datum/hud/ghost
movement_type = GROUND | FLYING
@@ -132,6 +133,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
. = ..()
AddElement(/datum/element/ghost_role_eligibility)
grant_all_languages()
+ show_data_huds()
+ data_huds_on = 1
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
if(!invisibility || camera.see_ghosts)
@@ -289,9 +292,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
if(world.time < roundstart_quit_limit)
penalty += roundstart_quit_limit - world.time
- var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
- if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
- penalty = CANT_REENTER_ROUND
+ if(SSautotransfer.can_fire && SSautotransfer.maxvotes)
+ var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
+ if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
+ penalty = CANT_REENTER_ROUND
if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, (stat == DEAD) ? TRUE : FALSE, FALSE, (stat == DEAD)? penalty : 0, (stat == DEAD)? TRUE : FALSE) & COMPONENT_BLOCK_GHOSTING)
return
@@ -324,9 +328,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
if(world.time < roundstart_quit_limit)
penalty += roundstart_quit_limit - world.time
- var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
- if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
- penalty = CANT_REENTER_ROUND
+ if(SSautotransfer.can_fire && SSautotransfer.maxvotes)
+ var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
+ if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
+ penalty = CANT_REENTER_ROUND
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst alive you won't be able to re-enter this round [penalty ? "or play ghost roles [penalty == CANT_REENTER_ROUND ? "until the round is over" : "for the next [DisplayTimeText(penalty)]"]" : ""]! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm
index 2a57506df6..77844a4788 100644
--- a/code/modules/mob/living/brain/MMI.dm
+++ b/code/modules/mob/living/brain/MMI.dm
@@ -14,20 +14,20 @@
var/force_replace_ai_name = FALSE
var/overrides_aicore_laws = FALSE // Whether the laws on the MMI, if any, override possible pre-existing laws loaded on the AI core.
-/obj/item/mmi/update_icon()
+/obj/item/mmi/update_icon_state()
if(!brain)
icon_state = "mmi_off"
- return
- if(istype(brain, /obj/item/organ/brain/alien))
+ else if(istype(brain, /obj/item/organ/brain/alien))
icon_state = "mmi_brain_alien"
- braintype = "Xenoborg" //HISS....Beep.
else
icon_state = "mmi_brain"
- braintype = "Cyborg"
+
+/obj/item/mmi/update_overlays()
+ . = ..()
if(brainmob && brainmob.stat != DEAD)
- add_overlay("mmi_alive")
+ . += "mmi_alive"
else
- add_overlay("mmi_dead")
+ . += "mmi_dead"
/obj/item/mmi/Initialize()
. = ..()
@@ -68,6 +68,10 @@
name = "Man-Machine Interface: [brainmob.real_name]"
update_icon()
+ if(istype(brain, /obj/item/organ/brain/alien))
+ braintype = "Xenoborg" //HISS....Beep.
+ else
+ braintype = "Cyborg"
SSblackbox.record_feedback("amount", "mmis_filled", 1)
@@ -85,7 +89,7 @@
to_chat(user, "You unlock and upend the MMI, spilling the brain onto the floor.")
eject_brain(user)
update_icon()
- name = "Man-Machine Interface"
+ name = initial(name)
/obj/item/mmi/proc/eject_brain(mob/user)
brainmob.container = null //Reset brainmob mmi var.
@@ -129,7 +133,10 @@
name = "Man-Machine Interface: [brainmob.real_name]"
update_icon()
- return
+ if(istype(brain, /obj/item/organ/brain/alien))
+ braintype = "Xenoborg" //HISS....Beep.
+ else
+ braintype = "Cyborg"
/obj/item/mmi/proc/replacement_ai_name()
return brainmob.name
diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm
index e7b8250494..4bca3e7f62 100644
--- a/code/modules/mob/living/brain/brain.dm
+++ b/code/modules/mob/living/brain/brain.dm
@@ -40,7 +40,7 @@
return ..()
/mob/living/brain/update_mobility()
- return ((mobility_flags = (in_contents_of(/obj/mecha)? MOBILITY_FLAGS_DEFAULT : NONE)))
+ return ((mobility_flags = (container?.in_contents_of(/obj/mecha)? MOBILITY_FLAGS_DEFAULT : NONE)))
/mob/living/brain/ex_act() //you cant blow up brainmobs because it makes transfer_to() freak out when borgs blow up.
return
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index e2d6b095fe..173b68fc3c 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -28,6 +28,34 @@ GLOBAL_VAR(posibrain_notify_cooldown)
var/list/possible_names //If you leave this blank, it will use the global posibrain names
var/picked_name
+/obj/item/mmi/posibrain/Initialize()
+ . = ..()
+ brainmob = new(src)
+ var/new_name
+ if(!LAZYLEN(possible_names))
+ new_name = pick(GLOB.posibrain_names)
+ else
+ new_name = pick(possible_names)
+ brainmob.name = "[new_name]-[rand(100, 999)]"
+ brainmob.real_name = brainmob.name
+ brainmob.forceMove(src)
+ brainmob.container = src
+ if(autoping)
+ ping_ghosts("created", TRUE)
+ GLOB.poi_list |= src
+ LAZYADD(GLOB.mob_spawners[name], src)
+
+/obj/item/mmi/posibrain/Destroy()
+ latejoin_remove()
+ return ..()
+
+/obj/item/mmi/posibrain/proc/latejoin_remove()
+ GLOB.poi_list -= src
+ var/init_name = initial(name)
+ LAZYREMOVE(GLOB.mob_spawners[init_name], src)
+ if(!LAZYLEN(GLOB.mob_spawners[init_name]))
+ GLOB.mob_spawners -= init_name
+
/obj/item/mmi/posibrain/Topic(href, href_list)
if(href_list["activate"])
var/mob/dead/observer/ghost = usr
@@ -97,15 +125,6 @@ GLOBAL_VAR(posibrain_notify_cooldown)
transfer_personality(user)
latejoin_remove()
-/obj/item/mmi/posibrain/Destroy()
- latejoin_remove()
- return ..()
-
-/obj/item/mmi/posibrain/proc/latejoin_remove()
- GLOB.poi_list -= src
- LAZYREMOVE(GLOB.mob_spawners[name], src)
- if(!LAZYLEN(GLOB.mob_spawners[name]))
- GLOB.mob_spawners -= name
/obj/item/mmi/posibrain/transfer_identity(mob/living/carbon/C)
name = "[initial(name)] ([C])"
@@ -163,32 +182,14 @@ GLOBAL_VAR(posibrain_notify_cooldown)
. += msg
-/obj/item/mmi/posibrain/Initialize()
- . = ..()
- brainmob = new(src)
- var/new_name
- if(!LAZYLEN(possible_names))
- new_name = pick(GLOB.posibrain_names)
- else
- new_name = pick(possible_names)
- brainmob.name = "[new_name]-[rand(100, 999)]"
- brainmob.real_name = brainmob.name
- brainmob.forceMove(src)
- brainmob.container = src
- if(autoping)
- ping_ghosts("created", TRUE)
- GLOB.poi_list |= src
- LAZYADD(GLOB.mob_spawners[name], src)
-
/obj/item/mmi/posibrain/attackby(obj/item/O, mob/user)
return
-/obj/item/mmi/posibrain/update_icon()
+/obj/item/mmi/posibrain/update_icon_state()
if(searching)
icon_state = "[initial(icon_state)]-searching"
- return
- if(brainmob && brainmob.key)
+ else if(brainmob && brainmob.key)
icon_state = "[initial(icon_state)]-occupied"
else
icon_state = initial(icon_state)
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index 81b76c1720..4d362ca554 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -21,7 +21,8 @@
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || health <= crit_threshold)
if(stat == CONSCIOUS)
stat = UNCONSCIOUS
- blind_eyes(1)
+ if(!eye_blind)
+ blind_eyes(1)
update_mobility()
else
if(stat == UNCONSCIOUS)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 32e3696bc5..5bb1e6d17d 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -397,6 +397,7 @@
W.layer = initial(W.layer)
W.plane = initial(W.plane)
changeNext_move(0)
+ update_equipment_speed_mods() // In case cuffs ever change speed
/mob/living/carbon/proc/clear_cuffs(obj/item/I, cuff_break)
if(!I.loc || buckled)
@@ -631,6 +632,13 @@
if(M.name == XRAY)
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = max(see_in_dark, 8)
+ if(HAS_TRAIT(src, TRAIT_THERMAL_VISION))
+ sight |= (SEE_MOBS)
+ lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
+
+ if(HAS_TRAIT(src, TRAIT_XRAY_VISION))
+ sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
+ see_in_dark = max(see_in_dark, 8)
if(see_override)
see_invisible = see_override
@@ -812,7 +820,8 @@
return
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)))
stat = UNCONSCIOUS
- blind_eyes(1)
+ if(!eye_blind)
+ blind_eyes(1)
if(combatmode)
toggle_combat_mode(TRUE, TRUE)
else
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 5bbc6d6a64..646e41f94c 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -71,6 +71,7 @@
L.embedded_objects |= I
I.add_mob_blood(src)//it embedded itself in you, of course it's bloody!
I.forceMove(src)
+ I.embedded()
L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier)
visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
@@ -104,7 +105,8 @@
var/basebloodychance = affecting.brute_dam + totitemdamage
if(prob(basebloodychance))
I.add_mob_blood(src)
- bleed(totitemdamage)
+ var/turf/location = get_turf(src)
+ add_splatter_floor(location)
if(totitemdamage >= 10 && get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
@@ -238,19 +240,19 @@
var/obj/item/organ/O = X
O.emp_act(severity)
-/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
- if(tesla_shock && (flags_1 & TESLA_IGNORE_1))
+/mob/living/carbon/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ if((flags & SHOCK_TESLA) && (flags_1 & TESLA_IGNORE_1))
return FALSE
if(HAS_TRAIT(src, TRAIT_SHOCKIMMUNE))
return FALSE
shock_damage *= siemens_coeff
if(dna && dna.species)
shock_damage *= dna.species.siemens_coeff
- if(shock_damage<1 && !override)
+ if(shock_damage < 1)
return 0
if(reagents.has_reagent(/datum/reagent/teslium))
shock_damage *= 1.5 //If the mob has teslium in their body, shocks are 50% more damaging!
- if(illusion)
+ if((flags & SHOCK_ILLUSION))
adjustStaminaLoss(shock_damage)
else
take_overall_damage(0,shock_damage)
@@ -262,16 +264,13 @@
jitteriness += 1000 //High numbers for violent convulsions
do_jitter_animation(jitteriness)
stuttering += 2
- if((!tesla_shock || (tesla_shock && siemens_coeff > 0.5)) && stun)
+ if((!(flags & SHOCK_TESLA) || siemens_coeff > 0.5) && (flags & SHOCK_NOSTUN))
Stun(40)
spawn(20)
jitteriness = max(jitteriness - 990, 10) //Still jittery, but vastly less
- if((!tesla_shock || (tesla_shock && siemens_coeff > 0.5)) && stun)
+ if((!(flags & SHOCK_TESLA) || siemens_coeff > 0.5) && (flags & SHOCK_NOSTUN))
DefaultCombatKnockdown(60)
- if(override)
- return override
- else
- return shock_damage
+ return shock_damage
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/M)
if(on_fire)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index fec82b357f..cb21534d25 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -25,15 +25,10 @@
var/list/obscured = check_obscured_slots()
var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))
- if(ishuman(src)) //user just returned, y'know, the user's own species. dumb.
- var/mob/living/carbon/human/H = src
- var/datum/species/pref_species = H.dna.species
- if(get_visible_name() == "Unknown") // same as flavor text, but hey it works.
- . += "You can't make out what species they are."
- else if(skipface)
- . += "You can't make out what species they are."
- else
- . += "[t_He] [t_is] a [H.dna.custom_species ? H.dna.custom_species : pref_species.name]!"
+ if(skipface || get_visible_name() == "Unknown")
+ . += "You can't make out what species they are."
+ else
+ . += "[t_He] [t_is] a [dna.custom_species ? dna.custom_species : dna.species.name]!"
//uniform
if(w_uniform && !(SLOT_W_UNIFORM in obscured))
@@ -389,18 +384,8 @@
else if(isobserver(user) && traitstring)
. += "Traits: [traitstring]"
- //No flavor text unless the face can be seen. Prevents certain metagaming with impersonation.
- var/invisible_man = skipface || get_visible_name() == "Unknown"
- if(invisible_man)
- . += "...?"
- else
- var/flavor = print_flavor_text(flavor_text)
- if(flavor)
- . += flavor
- var/temp_flavor = print_flavor_text(flavor_text_2,TRUE)
- if(temp_flavor)
- . += temp_flavor
- SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
+ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) //This also handles flavor texts now
+
. += "*---------*"
/mob/living/proc/status_effect_examines(pronoun_replacement) //You can include this in any mob's examine() to show the examine texts of status effects!
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index feb19ceaae..1b57796250 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -9,8 +9,6 @@
verbs += /mob/living/proc/mob_sleep
verbs += /mob/living/proc/lay_down
verbs += /mob/living/carbon/human/proc/underwear_toggle //fwee
- verbs += /mob/proc/set_flavor
- verbs += /mob/proc/set_flavor_2
//initialize limbs first
create_bodyparts()
@@ -40,6 +38,8 @@
. = ..()
if(!CONFIG_GET(flag/disable_human_mood))
AddComponent(/datum/component/mood)
+ AddElement(/datum/element/flavor_text/carbon)
+ AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!")
/mob/living/carbon/human/Destroy()
QDEL_NULL(physiology)
@@ -959,7 +959,7 @@
var/health_deficiency = ((100 + stambufferinfluence) - health + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
if(health_deficiency >= 40)
add_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN, override = TRUE, multiplicative_slowdown = ((health_deficiency-39) / 75), blacklisted_movetypes = FLOATING|FLYING)
- add_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING, override = TRUE, multiplicative_slowdown = ((health_deficiency-39) / 25), movetypes = FLOATING)
+ add_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING, override = TRUE, multiplicative_slowdown = ((health_deficiency-39) / 25), movetypes = FLYING, blacklisted_movetypes = FLOATING)
else
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN)
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 5bedce359b..1fc43f0d41 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -45,31 +45,11 @@
if(spec_return)
return spec_return
- if(mind)
- if (mind.martial_art && mind.martial_art.dodge_chance)
- if(!lying && dna && !dna.check_mutation(HULK))
- if(prob(mind.martial_art.dodge_chance))
- var/static/dodgemessages = list("dodges under",0,-4,"dodges to the right of",-4,0,"dodges to the left of",4,0,"jumps over",0,4)
- var/pick = pick(1,4,7,10)
- var/oldx = pixel_x
- var/oldy = pixel_y
- animate(src,pixel_x = pixel_x + dodgemessages[pick+1],pixel_y = pixel_y + dodgemessages[pick+2],time=3)
- animate(src,pixel_x = oldx,pixel_y = oldy,time=2)
- visible_message("[src] [dodgemessages[pick]] the projectile!", "You dodge the projectile!")
- return BULLET_ACT_FORCE_PIERCE
- if(mind.martial_art && !incapacitated(FALSE, TRUE) && mind.martial_art.can_use(src) && mind.martial_art.deflection_chance) //Some martial arts users can deflect projectiles!
- if(prob(mind.martial_art.deflection_chance))
- if(!lying && dna && !dna.check_mutation(HULK)) //But only if they're not lying down, and hulks can't do it
- if(mind.martial_art.deflection_chance >= 100) //if they can NEVER be hit, lets clue sec in ;)
- visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!")
- else
- visible_message("[src] deflects the projectile!", "You deflect the projectile!")
- playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
- if(mind.martial_art.reroute_deflection)
- P.firer = src
- P.setAngle(rand(0, 360))//SHING
- return BULLET_ACT_FORCE_PIERCE
-
+ if(mind) //martial art stuff
+ if(mind.martial_art && mind.martial_art.can_use(src)) //Some martial arts users can deflect projectiles!
+ var/martial_art_result = mind.martial_art.on_projectile_hit(src, P, def_zone)
+ if(!(martial_art_result == BULLET_ACT_HIT))
+ return martial_art_result
return ..()
/mob/living/carbon/human/check_reflect(def_zone)
@@ -397,8 +377,8 @@
//Added a safety check in case you want to shock a human mob directly through electrocute_act.
-/mob/living/carbon/human/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
- if(tesla_shock)
+/mob/living/carbon/human/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ if(flags & SHOCK_TESLA)
var/total_coeff = 1
if(gloves)
var/obj/item/clothing/gloves/G = gloves
@@ -413,20 +393,20 @@
siemens_coeff = total_coeff
if(flags_1 & TESLA_IGNORE_1)
siemens_coeff = 0
- else if(!safety)
+ else if(!(flags & SHOCK_NOGLOVES))
var/gloves_siemens_coeff = 1
if(gloves)
var/obj/item/clothing/gloves/G = gloves
gloves_siemens_coeff = G.siemens_coefficient
siemens_coeff = gloves_siemens_coeff
- if(undergoing_cardiac_arrest() && !illusion)
+ if(undergoing_cardiac_arrest() && !(flags & SHOCK_ILLUSION))
if(shock_damage * siemens_coeff >= 1 && prob(25))
var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
heart.beating = TRUE
if(stat == CONSCIOUS)
to_chat(src, "You feel your heart beating again!")
siemens_coeff *= physiology.siemens_coeff
- . = ..(shock_damage,source,siemens_coeff,safety,override,tesla_shock, illusion, stun)
+ . = ..()
if(.)
electrocution_animation(40)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 122a06b17b..580b0aaaf0 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -147,7 +147,7 @@
/mob/living/carbon/human/equipped_speed_mods()
. = ..()
- for(var/sloties in get_all_slots())
+ for(var/sloties in get_all_slots() - list(l_store, r_store, s_store))
var/obj/item/thing = sloties
. += thing?.slowdown
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 7507067597..9316a36e6b 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -316,6 +316,7 @@
BP.receive_damage(I.w_class*I.embedding.embedded_fall_pain_multiplier)
BP.embedded_objects -= I
I.forceMove(drop_location())
+ I.unembedded()
visible_message("[I] falls out of [name]'s [BP.name]!","[I] falls out of your [BP.name]!")
if(!has_embedded_objects())
clear_alert("embeddedobject")
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 85a7a09d5c..b428f49578 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -69,6 +69,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/siemens_coeff = 1 //base electrocution coefficient
var/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded?
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
+ var/inert_mutation = DWARFISM
var/list/special_step_sounds //Sounds to override barefeet walkng
var/grab_sound //Special sound for grabbing
var/datum/outfit/outfit_important_for_life // A path to an outfit that is important for species life e.g. plasmaman outfit
@@ -356,6 +357,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
C.type_of_meat = initial(meat)
+ //If their inert mutation is not the same, swap it out
+ if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
+ C.dna.remove_mutation(inert_mutation)
+ //keep it at the right spot, so we can't have people taking shortcuts
+ var/location = C.dna.mutation_index.Find(inert_mutation)
+ C.dna.mutation_index[location] = new_species.inert_mutation
+ C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
@@ -816,9 +825,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
//A little rename so we don't have to use tail_lizard or tail_human when naming the sprites.
if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "mam_tail" || bodypart == "xenotail")
bodypart = "tail"
- else if(bodypart == "waggingtail_lizard")
- bodypart = "waggingtail"
- if(bodypart == "mam_waggingtail" || bodypart == "waggingtail_human")
+ if(bodypart == "mam_waggingtail" || bodypart == "waggingtail_human" || bodypart == "waggingtail_lizard")
bodypart = "tailwag"
if(bodypart == "mam_ears" || bodypart == "ears")
bodypart = "ears"
@@ -1396,7 +1403,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(radiation > RAD_MOB_MUTATE)
if(prob(1))
to_chat(H, "You mutate!")
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
H.emote("gasp")
H.domutcheck()
@@ -1712,7 +1719,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
bloody = 1
var/turf/location = H.loc
if(istype(location))
- H.bleed(totitemdamage)
+ H.add_splatter_floor(location)
if(get_dist(user, H) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(H)
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index dfa1725026..6468bd1544 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -23,6 +23,7 @@
exotic_bloodtype = "L"
disliked_food = GRAIN | DAIRY
liked_food = GROSS | MEAT
+ inert_mutation = FIREBREATH
/datum/species/lizard/after_equip_job(datum/job/J, mob/living/carbon/human/H)
H.grant_language(/datum/language/draconic)
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index d2d4c6c658..7ccccd588d 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -61,9 +61,9 @@
H.DefaultCombatKnockdown(100)
H.visible_message("[H] writhes in pain as [H.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.")
if(prob(80))
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- H.randmutg()
+ H.easy_randmut(POSITIVE)
H.domutcheck()
else
H.adjustFireLoss(rand(5,15))
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index 70abba5a01..e229860cf7 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -100,6 +100,9 @@
var/obj/item/light_eater/blade
decay_factor = 0
+/obj/item/organ/heart/nightmare/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/item/organ/heart/nightmare/attack(mob/M, mob/living/carbon/user, obj/target)
if(M != user)
@@ -130,9 +133,6 @@
/obj/item/organ/heart/nightmare/Stop()
return 0
-/obj/item/organ/heart/nightmare/update_icon()
- return //always beating visually
-
/obj/item/organ/heart/nightmare/on_death()
if(!owner)
return
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index 24acccdd2e..50801e1c0d 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -122,9 +122,9 @@
update_inv_wear_mask()
/mob/living/carbon/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
- if(C.tint || initial(C.tint))
+ if(isclothing(C) && (C.tint || initial(C.tint)))
update_tint()
- update_inv_wear_mask()
+ return ..()
//handle stuff to update when a mob equips/unequips a headgear.
/mob/living/carbon/proc/head_update(obj/item/I, forced)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 639cfc40e2..4aae10c444 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -427,7 +427,6 @@
/mob/living/carbon/handle_mutations_and_radiation()
if(dna && dna.temporary_mutations.len)
- var/datum/mutation/human/HM
for(var/mut in dna.temporary_mutations)
if(dna.temporary_mutations[mut] < world.time)
if(mut == UI_CHANGED)
@@ -450,9 +449,9 @@
dna.previous.Remove("blood_type")
dna.temporary_mutations.Remove(mut)
continue
- HM = GLOB.mutations_list[mut]
- HM.force_lose(src)
- dna.temporary_mutations.Remove(mut)
+ for(var/datum/mutation/human/HM in dna.mutations)
+ if(HM && HM.timed)
+ dna.remove_mutation(HM.type)
radiation -= min(radiation, RAD_LOSS_PER_TICK)
if(radiation > RAD_MOB_SAFE)
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index ef2f4b80c7..3d567b0eff 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -42,7 +42,7 @@
if(radiation > RAD_MOB_MUTATE)
if(prob(1))
to_chat(src, "You mutate!")
- randmutb()
+ easy_randmut(NEGATIVE+MINOR_NEGATIVE)
emote("gasp")
domutcheck()
if(radiation > RAD_MOB_VOMIT && prob(RAD_MOB_VOMIT_PROB))
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index e48fc722cd..86b9826c12 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -43,7 +43,7 @@
/mob/living/carbon/monkey/ComponentInitialize()
. = ..()
- AddElement(/datum/element/mob_holder, "monkey", null, null, null, ITEM_SLOT_HEAD)
+ AddElement(/datum/element/mob_holder, worn_state = "monkey", inv_slots = ITEM_SLOT_HEAD)
/mob/living/carbon/monkey/Destroy()
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 2bf17bc265..d3199d6f8b 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -1,4 +1,5 @@
-/mob/living/Life(seconds, times_fired)
+/mob/living/proc/Life(seconds, times_fired)
+ set waitfor = FALSE
set invisibility = 0
if(digitalinvis)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index a2a69cb49a..3544f1b82f 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -490,7 +490,8 @@
GLOB.alive_mob_list += src
suiciding = 0
stat = UNCONSCIOUS //the mob starts unconscious,
- blind_eyes(1)
+ if(!eye_blind)
+ blind_eyes(1)
updatehealth() //then we check if the mob should wake up.
update_mobility()
update_sight()
@@ -794,6 +795,13 @@
what.forceMove(drop_location())
log_combat(src, who, "stripped [what] off")
+ if(Adjacent(who)) //update inventory window
+ who.show_inv(src)
+ else
+ src << browse(null,"window=mob[REF(who)]")
+
+ who.update_equipment_speed_mods() // Updates speed in case stripped speed affecting item
+
// The src mob is trying to place an item on someone
// Override if a certain mob should be behave differently when placing items (can't, for example)
/mob/living/stripPanelEquip(obj/item/what, mob/who, where)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index af8dc6dca9..27ecaf30d5 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -427,14 +427,14 @@
take_bodypart_damage(acidpwr * min(1, acid_volume * 0.1))
return 1
-/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
- SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage)
- if(tesla_shock && (flags_1 & TESLA_IGNORE_1))
+/mob/living/proc/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage, source, siemens_coeff, flags)
+ if((flags & SHOCK_TESLA) && (flags_1 & TESLA_IGNORE_1))
return FALSE
if(HAS_TRAIT(src, TRAIT_SHOCKIMMUNE))
return FALSE
if(shock_damage > 0)
- if(!illusion)
+ if(!(flags & SHOCK_ILLUSION))
adjustFireLoss(shock_damage)
visible_message(
"[src] was shocked by \the [source]!", \
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
index f278d22891..410e5b28db 100644
--- a/code/modules/mob/living/living_movement.dm
+++ b/code/modules/mob/living/living_movement.dm
@@ -1,6 +1,10 @@
/mob/living/Moved()
. = ..()
update_turf_movespeed(loc)
+ if(is_shifted)
+ is_shifted = FALSE
+ pixel_x = get_standard_pixel_x_offset(lying)
+ pixel_y = get_standard_pixel_y_offset(lying)
/mob/living/CanPass(atom/movable/mover, turf/target)
if((mover.pass_flags & PASSMOB))
diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm
index c6710141f9..63fb82082f 100644
--- a/code/modules/mob/living/silicon/pai/pai_shell.dm
+++ b/code/modules/mob/living/silicon/pai/pai_shell.dm
@@ -97,8 +97,7 @@
resist_a_rest(FALSE, TRUE)
update_icon()
if(possible_chassis[old_chassis])
- var/datum/element/mob_holder/M = SSdcs.GetElement(/datum/element/mob_holder, old_chassis, 'icons/mob/pai_item_head.dmi', 'icons/mob/pai_item_rh.dmi', 'icons/mob/pai_item_lh.dmi', ITEM_SLOT_HEAD)
- M.Detach(src)
+ RemoveElement(/datum/element/mob_holder, old_chassis, 'icons/mob/pai_item_head.dmi', 'icons/mob/pai_item_rh.dmi', 'icons/mob/pai_item_lh.dmi', ITEM_SLOT_HEAD)
if(possible_chassis[chassis])
AddElement(/datum/element/mob_holder, chassis, 'icons/mob/pai_item_head.dmi', 'icons/mob/pai_item_rh.dmi', 'icons/mob/pai_item_lh.dmi', ITEM_SLOT_HEAD)
to_chat(src, "You switch your holochassis projection composite to [chassis]")
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index d4f520a611..eca997beae 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -968,7 +968,8 @@
if(IsUnconscious() || IsStun() || IsParalyzed() || getOxyLoss() > maxHealth*0.5)
if(stat == CONSCIOUS)
stat = UNCONSCIOUS
- blind_eyes(1)
+ if(!eye_blind)
+ blind_eyes(1)
update_mobility()
update_headlamp()
else
@@ -1236,4 +1237,4 @@
if(usr.stat == DEAD)
return //won't work if dead
- ai_roster()
\ No newline at end of file
+ ai_roster()
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 5d1d2610b9..a93bc8662e 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -85,11 +85,11 @@
return
return ..()
-/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/silicon/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
if(buckled_mobs)
for(var/mob/living/M in buckled_mobs)
unbuckle_mob(M)
- M.electrocute_act(shock_damage/100, source, siemens_coeff, safety, tesla_shock, illusion, stun) //Hard metal shell conducts!
+ M.electrocute_act(shock_damage/100, source, siemens_coeff, flags) //Hard metal shell conducts!
return 0 //So borgs they don't die trying to fix wiring
/mob/living/silicon/emp_act(severity)
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 64b783b692..708c9ea2cd 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -103,7 +103,7 @@
/mob/living/simple_animal/hostile/construct/narsie_act()
return
-/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return 0
/mob/living/simple_animal/hostile/construct/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
index 726eda6c45..0d344f1a9b 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -277,7 +277,7 @@
// Why would bees pay attention to drones?
return 1
-/mob/living/simple_animal/drone/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/simple_animal/drone/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return 0 //So they don't die trying to fix wiring
/mob/living/simple_animal/drone/can_see_reagents()
diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm
index dbcaed1ba2..9ae46f29e5 100644
--- a/code/modules/mob/living/simple_animal/friendly/lizard.dm
+++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm
@@ -26,7 +26,7 @@
/mob/living/simple_animal/hostile/lizard/ComponentInitialize()
. = ..()
- AddElement(/datum/element/mob_holder, "lizard", null, null, null, ITEM_SLOT_HEAD) //you can hold lizards now.
+ AddElement(/datum/element/mob_holder, worn_state = "lizard", inv_slots = ITEM_SLOT_HEAD) //you can hold lizards now.
/mob/living/simple_animal/hostile/lizard/CanAttack(atom/the_target)//Can we actually attack a possible target?
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 9ad3cbd373..ff42512666 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -633,8 +633,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
"}
-/obj/item/paper/guides/antag/guardian/update_icon()
- return
+/obj/item/paper/guides/antag/guardian/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/item/paper/guides/antag/guardian/wizard
name = "Guardian Guide"
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index b019019c71..dca7bc7006 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -99,9 +99,57 @@
to_chat(user, "You strap the armor plating to [A] and sharpen [A.p_their()] claws with the nail filer. This was a great idea.")
qdel(src)
-
-
-
+mob/living/simple_animal/hostile/bear/butter //The mighty companion to Cak. Several functions used from it.
+ name = "Terrygold"
+ icon_state = "butterbear"
+ icon_living = "butterbear"
+ icon_dead = "butterbear_dead"
+ desc = "I can't believe its not a bear!"
+ faction = list("neutral", "russian")
+ obj_damage = 11
+ melee_damage_lower = 1
+ melee_damage_upper = 1
+ armour_penetration = 0
+ response_harm = "takes a bite out of"
+ attacked_sound = 'sound/items/eatfood.ogg'
+ deathmessage = "loses its false life and collapses!"
+ butcher_results = list(/obj/item/reagent_containers/food/snacks/butter = 6, /obj/item/reagent_containers/food/snacks/meat/slab = 3, /obj/item/organ/brain = 1, /obj/item/organ/heart = 1)
+ attack_sound = 'sound/weapons/slap.ogg'
+ attacktext = "slaps"
+
+/mob/living/simple_animal/hostile/bear/butter/Life() //Heals butter bear really fast when he takes damage.
+ if(stat)
+ return
+ if(health < maxHealth)
+ heal_overall_damage(10) //Fast life regen, makes it hard for you to get eaten to death.
+
+/mob/living/simple_animal/hostile/bear/butter/attack_hand(mob/living/L) //Borrowed code from Cak, feeds people if they hit you. More nutriment but less vitamin to represent BUTTER.
+ ..()
+ if(L.a_intent == INTENT_HARM && L.reagents && !stat)
+ L.reagents.add_reagent(/datum/reagent/consumable/nutriment, 1)
+ L.reagents.add_reagent(/datum/reagent/consumable/nutriment/vitamin, 0.1)
+
+/mob/living/simple_animal/hostile/bear/butter/CheckParts(list/parts) //Borrowed code from Cak, allows the brain used to actually control the bear.
+ ..()
+ var/obj/item/organ/brain/B = locate(/obj/item/organ/brain) in contents
+ if(!B || !B.brainmob || !B.brainmob.mind)
+ return
+ B.brainmob.mind.transfer_to(src)
+ to_chat(src, "You are a butter bear! You're a mostly harmless bear/butter hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \
+ so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \
+ free butter to the station!")
+ var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Terrygold.", "Name Change")
+ if(new_name)
+ to_chat(src, "Your name is now \"new_name\"!")
+ name = new_name
+
+mob/living/simple_animal/hostile/bear/butter/AttackingTarget() //Makes some attacks by the butter bear slip those who dare cross its path.
+ if(isliving(target))
+ var/mob/living/L = target
+ if((L.mobility_flags & MOBILITY_STAND))
+ L.Knockdown(20)
+ playsound(loc, 'sound/misc/slip.ogg', 15)
+ L.visible_message("[L] slips on butter!")
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index 83abde2fca..b50ebfe160 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -17,7 +17,7 @@ From these blood pools Bubblegum may summon slaughterlings - weak, low-damage mi
When Bubblegum dies, it leaves behind a H.E.C.K. suit+helmet as well as a chest that can contain three things:
1. A spellblade that can slice off limbs at range
2. A bottle that, when activated, drives everyone nearby into a frenzy
- 3. A contract that marks for death the chosen target
+ 3. A super double-barrel shotgun that shoots both shells at the same time.
Difficulty: Hard
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index d544671d3e..eb0b2948e5 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -123,7 +123,7 @@ Difficulty: Very Hard
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.mind)
- if(H.mind.martial_art && prob(H.mind.martial_art.deflection_chance))
+ if(istype(H.mind.martial_art, /datum/martial_art/the_sleeping_carp) & istype(H.mind.martial_art, /datum/martial_art/the_rising_bass))
. = TRUE
/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots()
@@ -244,8 +244,9 @@ Difficulty: Very Hard
var/list/stored_items = list()
var/list/blacklist = list()
-/obj/machinery/smartfridge/black_box/update_icon()
- return
+/obj/machinery/smartfridge/black_box/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/machinery/smartfridge/black_box/accept_check(obj/item/O)
if(!istype(O))
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
index 1ddd9079b2..50c6025378 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
@@ -275,7 +275,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
else
var/mob/living/L = target
L.attack_animal(src)
- L.electrocute_act(10, src, safety = TRUE) //safety = TRUE means we don't check gloves... Ok?
+ L.electrocute_act(10, src, flags = SHOCK_NOGLOVES)
return TRUE
else
return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
index 56d112237e..d940305080 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
@@ -84,10 +84,12 @@
pass_flags = PASSTABLE | PASSMOB //they shouldn't get stuck behind hivelords.
density = FALSE
del_on_death = 1
+ var/swarming = FALSE
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/Initialize()
. = ..()
- AddComponent(/datum/component/swarming) //oh god not the bees
+ if(swarming)
+ AddComponent(/datum/component/swarming) //oh god not the bees
addtimer(CALLBACK(src, .proc/death), 100)
//Legion
@@ -184,6 +186,7 @@
del_on_death = TRUE
stat_attack = UNCONSCIOUS
robust_searching = 1
+ swarming = TRUE
var/can_infest_dead = FALSE
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/Life()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index e6cdf81e38..5225534cee 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -202,9 +202,6 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
if(self_message)
show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
-/mob/proc/Life()
- set waitfor = FALSE
-
/mob/proc/get_item_by_slot(slot_id)
return null
@@ -506,14 +503,6 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
unset_machine()
src << browse(null, t1)
- if(href_list["flavor_more"])
- usr << browse(text("[][]", name, replacetext(flavor_text, "\n", "
")), text("window=[];size=500x200", name))
- onclose(usr, "[name]")
-
- if(href_list["flavor2_more"])
- usr << browse(text("[][]", name, replacetext(flavor_text_2, "\n", "
")), text("window=[];size=500x200", name))
- onclose(usr, "[name]")
-
if(href_list["refresh"])
if(machine && in_range(src, usr))
show_inv(machine)
@@ -708,6 +697,38 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
return TRUE
+/mob/verb/eastshift()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_x <= 16)
+ pixel_x++
+ is_shifted = TRUE
+
+/mob/verb/westshift()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_x >= -16)
+ pixel_x--
+ is_shifted = TRUE
+
+/mob/verb/northshift()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_y <= 16)
+ pixel_y++
+ is_shifted = TRUE
+
+/mob/verb/southshift()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_y >= -16)
+ pixel_y--
+ is_shifted = TRUE
+
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
return FALSE
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 6725e29610..f638281f27 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -38,6 +38,7 @@
var/resting = 0 //Carbon
var/lying = 0
var/lying_prev = 0
+ var/is_shifted = FALSE
//MOVEMENT SPEED
var/list/movespeed_modification //Lazy list, see mob_movespeed.dm
@@ -124,6 +125,3 @@
var/siliconaccesstoggle = FALSE
var/voluntary_ghosted = FALSE //whether or not they voluntarily ghosted.
-
- var/flavor_text = ""
- var/flavor_text_2 = "" //version of the above that only lasts for the current round.
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 4b360df8c7..42aaa110c8 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -49,9 +49,8 @@
O.updateappearance(icon_update=0)
if(tr_flags & TR_KEEPSE)
- O.dna.struc_enzymes = dna.struc_enzymes
- var/datum/mutation/human/race/R = GLOB.mutations_list[RACEMUT]
- O.dna.struc_enzymes = R.set_se(O.dna.struc_enzymes, on=1)//we don't want to keep the race block inactive
+ O.dna.mutation_index = dna.mutation_index
+ O.dna.set_se(1, GET_INITIALIZED_MUTATION(RACEMUT))
if(suiciding)
O.suiciding = suiciding
@@ -208,9 +207,8 @@
O.name = O.real_name
if(tr_flags & TR_KEEPSE)
- O.dna.struc_enzymes = dna.struc_enzymes
- var/datum/mutation/human/race/R = GLOB.mutations_list[RACEMUT]
- O.dna.struc_enzymes = R.set_se(O.dna.struc_enzymes, on=0)//we don't want to keep the race block active
+ O.dna.mutation_index = dna.mutation_index
+ O.dna.set_se(0, GET_INITIALIZED_MUTATION(RACEMUT))
O.domutcheck()
if(suiciding)
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index a83720dc23..d722197bd9 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -26,6 +26,7 @@
var/icon_state_unpowered = null // Icon state when the computer is turned off.
var/icon_state_powered = null // Icon state when the computer is turned on.
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
+ var/display_overlays = TRUE // If FALSE, don't draw overlays on this device at all
var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4.
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
@@ -208,20 +209,26 @@
else if(obj_integrity < max_integrity)
. += "It is damaged."
-/obj/item/modular_computer/update_icon()
- cut_overlays()
+/obj/item/modular_computer/update_icon_state()
if(!enabled)
icon_state = icon_state_unpowered
else
icon_state = icon_state_powered
+
+
+/obj/item/modular_computer/update_overlays()
+ . = ..()
+ if(!display_overlays)
+ return
+ if(enabled)
if(active_program)
- add_overlay(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu)
+ . += active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu
else
- add_overlay(icon_state_menu)
+ . += icon_state_menu
if(obj_integrity <= integrity_failure * max_integrity)
- add_overlay("bsod")
- add_overlay("broken")
+ . += "bsod"
+ . += "broken"
// On-click handling. Turns on the computer if it's off and opens the GUI.
diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm
index 1f19cf64ff..a4d2e74657 100644
--- a/code/modules/modular_computers/computers/item/laptop.dm
+++ b/code/modules/modular_computers/computers/item/laptop.dm
@@ -18,6 +18,7 @@
screen_on = 0 // Starts closed
var/start_open = TRUE // unless this var is set to 1
var/icon_state_closed = "laptop-closed"
+ display_overlays = FALSE
var/w_class_open = WEIGHT_CLASS_BULKY
/obj/item/modular_computer/laptop/examine(mob/user)
@@ -31,12 +32,11 @@
if(start_open && !screen_on)
toggle_open()
-/obj/item/modular_computer/laptop/update_icon()
- if(screen_on)
- ..()
- else
- cut_overlays()
+/obj/item/modular_computer/laptop/update_icon_state()
+ if(!screen_on)
icon_state = icon_state_closed
+ else
+ . = ..()
/obj/item/modular_computer/laptop/attack_self(mob/user)
if(!screen_on)
@@ -97,6 +97,7 @@
w_class = w_class_open
screen_on = !screen_on
+ display_overlays = screen_on
update_icon()
diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm
index b86d4e5d0d..295ecc66c0 100644
--- a/code/modules/modular_computers/computers/item/processor.dm
+++ b/code/modules/modular_computers/computers/item/processor.dm
@@ -16,15 +16,15 @@
machinery_computer.cpu = null
machinery_computer = null
-/obj/item/modular_computer/processor/New(comp)
+/obj/item/modular_computer/processor/Initialize(mapload)
. = ..()
- if(!comp || !istype(comp, /obj/machinery/modular_computer))
+ if(!loc || !istype(loc, /obj/machinery/modular_computer))
CRASH("Inapropriate type passed to obj/item/modular_computer/processor/New()! Aborting.")
return
// Obtain reference to machinery computer
all_components = list()
idle_threads = list()
- machinery_computer = comp
+ machinery_computer = loc
machinery_computer.cpu = src
hardware_flag = machinery_computer.hardware_flag
max_hardware_size = machinery_computer.max_hardware_size
@@ -34,14 +34,11 @@
integrity_failure = machinery_computer.integrity_failure
base_active_power_usage = machinery_computer.base_active_power_usage
base_idle_power_usage = machinery_computer.base_idle_power_usage
+ machinery_computer.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /atom/proc/update_icon) //when we update_icon, also update the computer
/obj/item/modular_computer/processor/relay_qdel()
qdel(machinery_computer)
-/obj/item/modular_computer/processor/update_icon()
- if(machinery_computer)
- return machinery_computer.update_icon()
-
// This thing is not meant to be used on it's own, get topic data from our machinery owner.
//obj/item/modular_computer/processor/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
// if(!machinery_computer)
diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm
index 0818ce8034..a371e97ec6 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -15,23 +15,20 @@
var/has_variants = TRUE
var/finish_color = null
-/obj/item/modular_computer/tablet/update_icon()
- ..()
+/obj/item/modular_computer/tablet/update_icon_state()
if(has_variants)
if(!finish_color)
finish_color = pick("red","blue","brown","green","black")
- icon_state = "tablet-[finish_color]"
- icon_state_unpowered = "tablet-[finish_color]"
- icon_state_powered = "tablet-[finish_color]"
+ icon_state = icon_state_powered = icon_state_unpowered = "tablet-[finish_color]"
/obj/item/modular_computer/tablet/syndicate_contract_uplink
name = "contractor tablet"
icon = 'icons/obj/contractor_tablet.dmi'
- icon_state = "tablet-red"
+ icon_state = "tablet"
icon_state_unpowered = "tablet"
icon_state_powered = "tablet"
icon_state_menu = "assign"
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
comp_light_luminosity = 6.3
- has_variants = FALSE
\ No newline at end of file
+ has_variants = FALSE
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index a1f13878c1..89c6166a0d 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -48,26 +48,28 @@
if(cpu)
. |= cpu.emag_act(user)
-/obj/machinery/modular_computer/update_icon()
- cut_overlays()
- icon_state = icon_state_powered
+/obj/machinery/modular_computer/update_icon_state()
+ if(cpu?.enabled)
+ icon_state = icon_state_powered
+ else if(stat & NOPOWER || !(cpu?.use_power()))
+ icon_state = icon_state_unpowered
+/obj/machinery/modular_computer/update_overlays()
+ . = ..()
if(!cpu || !cpu.enabled)
if (!(stat & NOPOWER) && (cpu && cpu.use_power()))
- add_overlay(screen_icon_screensaver)
- else
- icon_state = icon_state_unpowered
+ . += screen_icon_screensaver
set_light(0)
else
set_light(light_strength)
if(cpu.active_program)
- add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu)
+ . += cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu
else
- add_overlay(screen_icon_state_menu)
+ . += screen_icon_state_menu
if(cpu && cpu.obj_integrity <= cpu.integrity_failure * cpu.max_integrity)
- add_overlay("bsod")
- add_overlay("broken")
+ . += "bsod"
+ . += "broken"
// Eject ID card from computer, if it has ID slot with card inside.
/obj/machinery/modular_computer/proc/eject_id()
diff --git a/code/modules/newscaster/newscaster_machine.dm b/code/modules/newscaster/newscaster_machine.dm
index 93d5dc3b31..95a24817a0 100644
--- a/code/modules/newscaster/newscaster_machine.dm
+++ b/code/modules/newscaster/newscaster_machine.dm
@@ -53,7 +53,7 @@ GLOBAL_LIST_EMPTY(allCasters)
picture = null
return ..()
-/obj/machinery/newscaster/update_icon()
+/obj/machinery/newscaster/update_icon_state()
if(stat & (NOPOWER|BROKEN))
icon_state = "newscaster_off"
else
diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm
index f75569ef7a..1a93661c76 100644
--- a/code/modules/paperwork/clipboard.dm
+++ b/code/modules/paperwork/clipboard.dm
@@ -25,14 +25,14 @@
QDEL_NULL(toppaper) //let movable/Destroy handle the rest
return ..()
-/obj/item/clipboard/update_icon()
- cut_overlays()
+/obj/item/clipboard/update_overlays()
+ . = ..()
if(toppaper)
- add_overlay(toppaper.icon_state)
- copy_overlays(toppaper)
+ . += toppaper.icon_state
+ . += toppaper.overlays
if(haspen)
- add_overlay("clipboard_pen")
- add_overlay("clipboard_over")
+ . += "clipboard_pen"
+ . += "clipboard_over"
/obj/item/clipboard/attackby(obj/item/W, mob/user, params)
diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm
index 5f7db07190..2c02903d3e 100644
--- a/code/modules/paperwork/contract.dm
+++ b/code/modules/paperwork/contract.dm
@@ -7,13 +7,14 @@
var/datum/mind/target
item_flags = NOBLUDGEON
+/obj/item/paper/contract/ComponentInitialize()
+ return
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
+
/obj/item/paper/contract/proc/update_text()
return
-/obj/item/paper/contract/update_icon()
- return
-
-
/obj/item/paper/contract/employment
icon_state = "paper_words"
diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm
index 4cd44af5ab..a599ec9deb 100644
--- a/code/modules/paperwork/folders.dm
+++ b/code/modules/paperwork/folders.dm
@@ -28,11 +28,10 @@
icon_state = "folder_white"
-/obj/item/folder/update_icon()
- cut_overlays()
+/obj/item/folder/update_overlays()
+ . = ..()
if(contents.len)
- add_overlay("folder_paper")
-
+ . += "folder_paper"
/obj/item/folder/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/paper) || istype(W, /obj/item/photo) || istype(W, /obj/item/documents))
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 20f4ab0c86..88b6230b7d 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -78,8 +78,7 @@
return get_dist(src, target) < 2
return ..()
-/obj/item/paper/update_icon()
-
+/obj/item/paper/update_icon_state()
if(resistance_flags & ON_FIRE)
icon_state = "paper_onfire"
return
@@ -379,8 +378,9 @@
icon_state = "scrap"
slot_flags = null
-/obj/item/paper/crumpled/update_icon()
- return
+/obj/item/paper/crumpled/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/item/paper/crumpled/bloody
icon_state = "scrap_bloodied"
diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm
index 0f57d53111..0a7bf011a7 100644
--- a/code/modules/paperwork/paper_cutter.dm
+++ b/code/modules/paperwork/paper_cutter.dm
@@ -34,13 +34,13 @@
return (BRUTELOSS)
-/obj/item/papercutter/update_icon()
- ..()
- cut_overlays()
+/obj/item/papercutter/update_icon_state()
icon_state = (storedcutter ? "[initial(icon_state)]-cutter" : "[initial(icon_state)]")
- if(storedpaper)
- add_overlay("paper")
+/obj/item/papercutter/update_overlays()
+ . = ..()
+ if(storedpaper)
+ . += "paper"
/obj/item/papercutter/attackby(obj/item/P, mob/user, params)
if(istype(P, /obj/item/paper) && !storedpaper)
diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm
index 5648bc7af8..b8bcbedbbe 100644
--- a/code/modules/paperwork/paperbin.dm
+++ b/code/modules/paperwork/paperbin.dm
@@ -51,7 +51,7 @@
else if(istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
-
+
else
. = ..()
@@ -125,14 +125,16 @@
. += "It doesn't contain anything."
-/obj/item/paper_bin/update_icon()
+/obj/item/paper_bin/update_icon_state()
if(total_paper < 1)
icon_state = "paper_bin0"
else
icon_state = "[initial(icon_state)]"
- cut_overlays()
+
+/obj/item/paper_bin/update_overlays()
+ . = ..()
if(bin_pen)
- add_overlay(mutable_appearance(bin_pen.icon, bin_pen.icon_state))
+ . += mutable_appearance(bin_pen.icon, bin_pen.icon_state)
/obj/item/paper_bin/construction
name = "construction paper bin"
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index 06c16810b7..a9baf60c0c 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -55,12 +55,12 @@
sleep(10)
return (BRUTELOSS)
-/obj/item/paperplane/update_icon()
- cut_overlays()
+/obj/item/paperplane/update_overlays()
+ . = ..()
var/list/stamped = internalPaper.stamped
if(stamped)
for(var/S in stamped)
- add_overlay("paperplane_[S]")
+ . += "paperplane_[S]"
/obj/item/paperplane/attack_self(mob/user)
to_chat(user, "You unfold [src].")
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index a98f057c4a..227e22d499 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -179,9 +179,10 @@
sharpness = IS_SHARP
var/on = FALSE
-/obj/item/pen/edagger/Initialize()
+/obj/item/pen/edagger/ComponentInitialize()
. = ..()
AddComponent(/datum/component/butchering, 60, 100, 0, 'sound/weapons/blade1.ogg')
+ AddElement(/datum/element/update_icon_updates_onmob)
/obj/item/pen/edagger/get_sharpness()
return on * sharpness
@@ -209,10 +210,9 @@
to_chat(user, "[src] is now active.")
update_icon()
-/obj/item/pen/edagger/update_icon()
+/obj/item/pen/edagger/update_icon_state()
if(on)
- icon_state = "edagger"
- item_state = "edagger"
+ icon_state = item_state = "edagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
else
diff --git a/code/modules/photography/photos/frame.dm b/code/modules/photography/photos/frame.dm
index d20a71d1c6..67559c0381 100644
--- a/code/modules/photography/photos/frame.dm
+++ b/code/modules/photography/photos/frame.dm
@@ -43,10 +43,10 @@
return list()
return ..()
-/obj/item/wallframe/picture/update_icon()
- cut_overlays()
+/obj/item/wallframe/picture/update_overlays()
+ . = ..()
if(displayed)
- add_overlay(image(displayed))
+ . += image(displayed)
/obj/item/wallframe/picture/after_attach(obj/O)
..()
@@ -145,10 +145,10 @@
if(framed)
framed.show(user)
-/obj/structure/sign/picture_frame/update_icon()
- cut_overlays()
+/obj/structure/sign/picture_frame/update_overlays()
+ . = ..()
if(framed)
- add_overlay(image(framed))
+ . += image(framed)
/obj/structure/sign/picture_frame/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index d45a119553..2f827f7d51 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -32,7 +32,7 @@
if(setdesc && P.picture_desc)
desc = P.picture_desc
-/obj/item/photo/update_icon()
+/obj/item/photo/update_icon_state()
if(!istype(picture) || !picture.picture_image)
return
var/icon/I = picture.get_small_icon()
diff --git a/code/modules/pool/pool_controller.dm b/code/modules/pool/pool_controller.dm
index 6f28a7c8dd..c9bc6be3e5 100644
--- a/code/modules/pool/pool_controller.dm
+++ b/code/modules/pool/pool_controller.dm
@@ -297,8 +297,7 @@
mist_on()
update_icon()
-/obj/machinery/pool/controller/update_icon()
- . = ..()
+/obj/machinery/pool/controller/update_icon_state()
icon_state = "poolc_[temperature]"
/obj/machinery/pool/controller/proc/CanUpTemp(mob/user)
@@ -412,7 +411,7 @@
/obj/machinery/pool/controller/proc/mist_on() //Spawn /obj/effect/mist (from the shower) on all linked pool tiles
if(mist_state)
return
- mist_off() //make sure it cycles and deletes everything
+ mist_off() //make sure it cycles and deletes everything
mist_state = TRUE
for(var/X in linked_turfs)
var/turf/open/pool/W = X
diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm
index 1ff398bdd3..568ad893a2 100644
--- a/code/modules/power/antimatter/control.dm
+++ b/code/modules/power/antimatter/control.dm
@@ -151,10 +151,11 @@
return
-/obj/machinery/power/am_control_unit/update_icon()
+/obj/machinery/power/am_control_unit/update_icon_state()
if(active)
icon_state = "control_on"
- else icon_state = "control"
+ else
+ icon_state = "control"
//No other icons for it atm
diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm
index 624c4c0316..766c4d4392 100644
--- a/code/modules/power/antimatter/shielding.dm
+++ b/code/modules/power/antimatter/shielding.dm
@@ -107,10 +107,9 @@
check_stability()
-/obj/machinery/am_shielding/update_icon()
+/obj/machinery/am_shielding/update_icon_state()
dirs = 0
coredirs = 0
- cut_overlays()
for(var/direction in GLOB.alldirs)
var/turf/T = get_step(loc, direction)
for(var/obj/machinery/machine in T)
@@ -137,8 +136,10 @@
icon_state = "[prefix]shield_[icondirs]"
+/obj/machinery/am_shielding/update_overlays()
+ . = ..()
if(core_check())
- add_overlay("core[control_unit && control_unit.active]")
+ . += "core[control_unit?.active]"
if(!processing)
setup_core()
else if(processing)
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index b9d4d1adf1..8d5de661ae 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -130,7 +130,7 @@ By design, d1 is the smallest direction and d2 is the highest
invisibility = i ? INVISIBILITY_MAXIMUM : 0
update_icon()
-/obj/structure/cable/update_icon()
+/obj/structure/cable/update_icon_state()
icon_state = "[d1]-[d2]"
color = null
add_atom_colour(cable_color, FIXED_COLOUR_PRIORITY)
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index a15580498c..93fe7e8fa6 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -58,16 +58,16 @@
else
return PROCESS_KILL
-/obj/item/stock_parts/cell/update_icon()
- cut_overlays()
+/obj/item/stock_parts/cell/update_overlays()
+ . = ..()
if(grown_battery)
- add_overlay(image('icons/obj/power.dmi',"grown_wires"))
+ . += image('icons/obj/power.dmi',"grown_wires")
if(charge < 0.01)
return
else if(charge/maxcharge >=0.995)
- add_overlay("cell-o2")
+ . += "cell-o2"
else
- add_overlay("cell-o1")
+ . += "cell-o1"
/obj/item/stock_parts/cell/proc/percent() // return % charge of cell
return 100*charge/maxcharge
@@ -281,8 +281,9 @@
maxcharge = 50000
ratingdesc = FALSE
-/obj/item/stock_parts/cell/infinite/abductor/update_icon()
- return
+/obj/item/stock_parts/cell/infinite/abductor/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/item/stock_parts/cell/potato
diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm
index 011a2e4fd2..6d63a57c88 100644
--- a/code/modules/power/generator.dm
+++ b/code/modules/power/generator.dm
@@ -30,19 +30,15 @@
SSair.atmos_machinery -= src
return ..()
-/obj/machinery/power/generator/update_icon()
-
- if(stat & (NOPOWER|BROKEN))
- cut_overlays()
- else
- cut_overlays()
-
+/obj/machinery/power/generator/update_overlays()
+ . = ..()
+ if(!(stat & (NOPOWER|BROKEN)))
var/L = min(round(lastgenlev/100000),11)
if(L != 0)
- add_overlay(image('icons/obj/power.dmi', "teg-op[L]"))
+ . += image('icons/obj/power.dmi', "teg-op[L]")
if(hot_circ && cold_circ)
- add_overlay("teg-oc[lastcirc]")
+ . += "teg-oc[lastcirc]"
#define GENRATE 800 // generator output coefficient from Q
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index d0d1e2b475..582d72f6c0 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -44,8 +44,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
-/obj/machinery/gravity_generator/update_icon()
- ..()
+/obj/machinery/gravity_generator/update_icon_state()
icon_state = "[get_status()]_[sprite_number]"
/obj/machinery/gravity_generator/proc/get_status()
@@ -156,6 +155,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
part.main_part = src
parts += part
part.update_icon()
+ part.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /atom/proc/update_icon)
/obj/machinery/gravity_generator/main/proc/connected_parts()
return parts.len == 8
@@ -235,7 +235,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
data["charging_state"] = charging_state
data["on"] = on
data["operational"] = (stat & BROKEN) ? FALSE : TRUE
-
+
return data
/obj/machinery/gravity_generator/main/ui_act(action, params)
@@ -259,11 +259,6 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
return "fix[min(broken_state, 3)]"
return on || charging_state != POWER_IDLE ? "on" : "off"
-/obj/machinery/gravity_generator/main/update_icon()
- ..()
- for(var/obj/O in parts)
- O.update_icon()
-
// Set the charging state based on power/breaker.
/obj/machinery/gravity_generator/main/proc/set_power()
var/new_state = FALSE
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 6cfcc5192a..8d041efb76 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -291,8 +291,7 @@
QDEL_NULL(cell)
return ..()
-/obj/machinery/light/update_icon()
- cut_overlays()
+/obj/machinery/light/update_icon_state()
switch(status) // set icon_states
if(LIGHT_OK)
var/area/A = get_base_area(src)
@@ -303,17 +302,20 @@
icon_state = "[base_state]_hijacked"
else
icon_state = "[base_state]"
- if(on)
- var/mutable_appearance/glowybit = mutable_appearance(overlayicon, base_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
- glowybit.alpha = CLAMP(light_power*250, 30, 200)
- add_overlay(glowybit)
if(LIGHT_EMPTY)
icon_state = "[base_state]-empty"
if(LIGHT_BURNED)
icon_state = "[base_state]-burned"
if(LIGHT_BROKEN)
icon_state = "[base_state]-broken"
- return
+
+/obj/machinery/light/update_overlays()
+ . = ..()
+ if(on && status == LIGHT_OK)
+ var/mutable_appearance/glowybit = mutable_appearance(overlayicon, base_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ glowybit.alpha = CLAMP(light_power*250, 30, 200)
+ . += glowybit
+
// update the icon_state and luminosity of the light depending on its state
/obj/machinery/light/proc/update(trigger = TRUE)
diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index 3a6a98d681..3c20f2f69c 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -39,7 +39,7 @@
/obj/machinery/power/port_gen/proc/handleInactive()
return
-/obj/machinery/power/port_gen/update_icon()
+/obj/machinery/power/port_gen/update_icon_state()
icon_state = "[base_icon]_[active]"
/obj/machinery/power/port_gen/process()
diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm
index be8d456f20..2e6ec57f77 100644
--- a/code/modules/power/singularity/collector.dm
+++ b/code/modules/power/singularity/collector.dm
@@ -204,14 +204,14 @@
if(loaded_tank && active && pulse_strength > RAD_COLLECTOR_EFFICIENCY)
stored_power += (pulse_strength-RAD_COLLECTOR_EFFICIENCY)*RAD_COLLECTOR_COEFFICIENT
-/obj/machinery/power/rad_collector/update_icon()
- cut_overlays()
+/obj/machinery/power/rad_collector/update_overlays()
+ . = ..()
if(loaded_tank)
- add_overlay("ptank")
+ . += "ptank"
if(stat & (NOPOWER|BROKEN))
return
if(active)
- add_overlay("on")
+ . += "on"
/obj/machinery/power/rad_collector/proc/toggle_power()
diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm
index e956742e92..d57f78e5b6 100644
--- a/code/modules/power/singularity/emitter.dm
+++ b/code/modules/power/singularity/emitter.dm
@@ -112,7 +112,7 @@
QDEL_NULL(sparks)
return ..()
-/obj/machinery/power/emitter/update_icon()
+/obj/machinery/power/emitter/update_icon_state()
if (active && powernet && avail(active_power_usage))
icon_state = icon_state_on
else
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 54aca32cf7..950e34098c 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -44,14 +44,14 @@ field_generator power level display
var/list/obj/machinery/field/generator/connected_gens
var/clean_up = 0
-/obj/machinery/field/generator/update_icon()
- cut_overlays()
+/obj/machinery/field/generator/update_overlays()
+ . = ..()
if(warming_up)
- add_overlay("+a[warming_up]")
+ . += "+a[warming_up]"
if(fields.len)
- add_overlay("+on")
+ . += "+on"
if(power_level)
- add_overlay("+p[power_level]")
+ . += "+p[power_level]"
/obj/machinery/field/generator/Initialize()
diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm
index 09fb3ad4e9..59792b6539 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm
@@ -124,7 +124,7 @@
investigate_log("was moved whilst active; it powered down.", INVESTIGATE_SINGULO)
-/obj/structure/particle_accelerator/update_icon()
+/obj/structure/particle_accelerator/update_icon_state()
switch(construction_state)
if(PA_CONSTRUCTION_UNSECURED,PA_CONSTRUCTION_UNWIRED)
icon_state="[reference]"
diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm
index f583fc138d..af83f7ebca 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_control.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm
@@ -60,7 +60,7 @@
active = 0
connected_parts.Cut()
-/obj/machinery/particle_accelerator/control_box/update_icon()
+/obj/machinery/particle_accelerator/control_box/update_icon_state()
if(active)
icon_state = "control_boxp1"
else
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index 774f7ba14d..47de07cd71 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -199,29 +199,25 @@
stat |= BROKEN
-/obj/machinery/power/smes/update_icon()
- cut_overlays()
- if(stat & BROKEN)
- return
-
- if(panel_open)
+/obj/machinery/power/smes/update_overlays()
+ . = ..()
+ if((stat & BROKEN) || panel_open)
return
if(outputting)
- add_overlay("smes-op1")
+ . += "smes-op1"
else
- add_overlay("smes-op0")
+ . += "smes-op0"
if(inputting)
- add_overlay("smes-oc1")
+ . += "smes-oc1"
else
if(input_attempt)
- add_overlay("smes-oc0")
+ . += "smes-oc0"
var/clevel = chargedisplay()
if(clevel>0)
- add_overlay("smes-og[clevel]")
-
+ . += "smes-og[clevel]"
/obj/machinery/power/smes/proc/chargedisplay()
return CLAMP(round(5.5*charge/capacity),0,5)
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 67cc371c3b..2548620a7f 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -807,7 +807,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
. = zapdir
if(target_mob)
- target_mob.electrocute_act(rand(5,10), "Supermatter Discharge Bolt", 1, stun = 0)
+ target_mob.electrocute_act(rand(5,10), "Supermatter Discharge Bolt", 1, SHOCK_NOSTUN)
if(prob(15))
supermatter_zap(target_mob, 5, power / 2)
supermatter_zap(target_mob, 5, power / 2)
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index 13f53f14d0..1b08693d1c 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -297,7 +297,7 @@
else if(closest_mob)
var/shock_damage = (tesla_flags & TESLA_MOB_DAMAGE)? (min(round(power/600), 90) + rand(-5, 5)) : 0
- closest_mob.electrocute_act(shock_damage, source, 1, tesla_shock = 1, stun = (tesla_flags & TESLA_MOB_STUN))
+ closest_mob.electrocute_act(shock_damage, source, 1, SHOCK_TESLA | ((tesla_flags & TESLA_MOB_STUN) ? NONE : SHOCK_NOSTUN))
if(issilicon(closest_mob))
var/mob/living/silicon/S = closest_mob
if((tesla_flags & TESLA_MOB_STUN) && (tesla_flags & TESLA_MOB_DAMAGE))
diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
index 28b70bf276..ee6a25d8e4 100644
--- a/code/modules/projectiles/ammunition/_ammunition.dm
+++ b/code/modules/projectiles/ammunition/_ammunition.dm
@@ -39,8 +39,7 @@
QDEL_NULL(BB)
return ..()
-/obj/item/ammo_casing/update_icon()
- ..()
+/obj/item/ammo_casing/update_icon_state()
icon_state = "[initial(icon_state)][BB ? "-live" : ""]"
desc = "[initial(desc)][BB ? "" : " This one is spent."]"
diff --git a/code/modules/projectiles/ammunition/caseless/_caseless.dm b/code/modules/projectiles/ammunition/caseless/_caseless.dm
index db1aa6562c..33d096007d 100644
--- a/code/modules/projectiles/ammunition/caseless/_caseless.dm
+++ b/code/modules/projectiles/ammunition/caseless/_caseless.dm
@@ -11,6 +11,5 @@
else
return FALSE
-/obj/item/ammo_casing/caseless/update_icon()
- ..()
+/obj/item/ammo_casing/caseless/update_icon_state()
icon_state = "[initial(icon_state)]"
diff --git a/code/modules/projectiles/ammunition/caseless/foam.dm b/code/modules/projectiles/ammunition/caseless/foam.dm
index 311f76797c..91ed69f7a5 100644
--- a/code/modules/projectiles/ammunition/caseless/foam.dm
+++ b/code/modules/projectiles/ammunition/caseless/foam.dm
@@ -9,8 +9,7 @@
harmful = FALSE
var/modified = FALSE
-/obj/item/ammo_casing/caseless/foam_dart/update_icon()
- ..()
+/obj/item/ammo_casing/caseless/foam_dart/update_icon_state()
if (modified)
icon_state = "foamdart_empty"
desc = "It's nerf or nothing! ... Although, this one doesn't look too safe."
diff --git a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
index 09d056a5f2..eb2572187c 100644
--- a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
@@ -37,6 +37,10 @@
name = "double-barrel shotgun internal magazine"
max_ammo = 2
+/obj/item/ammo_box/magazine/internal/shot/dual/heck
+ name = "heckgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+
/obj/item/ammo_box/magazine/internal/shot/improvised
name = "improvised shotgun internal magazine"
ammo_type = /obj/item/ammo_casing/shotgun/improvised
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 9ea50562dd..e45483569e 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -19,8 +19,7 @@
chamber_round()
update_icon()
-/obj/item/gun/ballistic/update_icon()
- ..()
+/obj/item/gun/ballistic/update_icon_state()
if(current_skin)
icon_state = "[unique_reskin[current_skin]][suppressed ? "-suppressed" : ""][sawn_off ? "-sawn" : ""]"
else
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index 0ff0c79f1e..39428275c8 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -39,7 +39,6 @@
magazine = AM
if(oldmag)
to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.")
- oldmag.dropped()
oldmag.forceMove(get_turf(src.loc))
oldmag.update_icon()
else
diff --git a/code/modules/projectiles/guns/ballistic/bow.dm b/code/modules/projectiles/guns/ballistic/bow.dm
index 0fc181f71c..83f95c2c54 100644
--- a/code/modules/projectiles/guns/ballistic/bow.dm
+++ b/code/modules/projectiles/guns/ballistic/bow.dm
@@ -44,7 +44,7 @@
to_chat(user, "You notch the arrow.")
update_icon()
-/obj/item/gun/ballistic/bow/update_icon()
+/obj/item/gun/ballistic/bow/update_icon_state()
icon_state = "[initial(icon_state)]_[get_ammo() ? (chambered ? "firing" : "loaded") : "unloaded"]"
/obj/item/gun/ballistic/bow/can_shoot()
@@ -63,4 +63,4 @@
icon_state = "pipebow"
item_state = "pipebow"
inaccuracy_modifier = 1.1 //Made of pipe and in a rush
- force = 0
\ No newline at end of file
+ force = 0
diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm
index 82f6fa700b..366aa367b4 100644
--- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm
+++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm
@@ -73,7 +73,7 @@
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
-/obj/item/minigunpack/update_icon()
+/obj/item/minigunpack/update_icon_state()
if(armed)
icon_state = "notholstered"
else
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index 571525d8f0..7f01db466f 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -171,7 +171,7 @@
pump()
gun_type = type
-/obj/item/gun/ballistic/shotgun/boltaction/enchanted/dropped()
+/obj/item/gun/ballistic/shotgun/boltaction/enchanted/dropped(mob/user)
..()
guns_left = 0
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index 6cd567ce16..c5660624a2 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -34,7 +34,7 @@
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
ammo_type = list(/obj/item/ammo_casing/energy/kinetic/premium)
-/obj/item/gun/energy/kinetic_accelerator/premiumka/dropped()
+/obj/item/gun/energy/kinetic_accelerator/premiumka/dropped(mob/user)
. = ..()
if(!QDELING(src) && !holds_charge)
// Put it on a delay because moving item from slot to hand
@@ -137,7 +137,7 @@
if(!can_shoot())
attempt_reload()
-/obj/item/gun/energy/kinetic_accelerator/dropped()
+/obj/item/gun/energy/kinetic_accelerator/dropped(mob/user)
. = ..()
if(!QDELING(src) && !holds_charge)
// Put it on a delay because moving item from slot to hand
diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm
index f724f982d0..ce87eddc67 100644
--- a/code/modules/projectiles/guns/magic.dm
+++ b/code/modules/projectiles/guns/magic.dm
@@ -73,9 +73,6 @@
recharge_newshot()
return 1
-/obj/item/gun/magic/update_icon()
- return
-
/obj/item/gun/magic/shoot_with_empty_chamber(mob/living/user as mob|obj)
to_chat(user, "The [name] whizzles quietly.")
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index 4857369363..f48020aeb5 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -21,7 +21,7 @@
. = ..()
. += "Has [charges] charge\s remaining."
-/obj/item/gun/magic/wand/update_icon()
+/obj/item/gun/magic/wand/update_icon_state()
icon_state = "[initial(icon_state)][charges ? "" : "-drained"]"
/obj/item/gun/magic/wand/attack(atom/target, mob/living/user)
diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm
index 3bb1b3794e..6ae6564671 100644
--- a/code/modules/projectiles/projectile/energy/stun.dm
+++ b/code/modules/projectiles/projectile/energy/stun.dm
@@ -27,7 +27,7 @@
C.IgniteMob()
if(C.dna && C.dna.check_mutation(HULK))
C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
- else if(tase_duration && (C.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(C, TRAIT_STUNIMMUNE))
+ else if(tase_duration && (C.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(C, TRAIT_STUNIMMUNE) && !HAS_TRAIT(C, TRAIT_TASED_RESISTANCE))
C.apply_status_effect(strong_tase? STATUS_EFFECT_TASED : STATUS_EFFECT_TASED_WEAK, tase_duration)
addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5)
diff --git a/code/modules/projectiles/projectile/special/temperature.dm b/code/modules/projectiles/projectile/special/temperature.dm
index dae7d5f8b3..c17fd0a447 100644
--- a/code/modules/projectiles/projectile/special/temperature.dm
+++ b/code/modules/projectiles/projectile/special/temperature.dm
@@ -16,3 +16,8 @@
/obj/item/projectile/temp/hot
name = "heat beam"
temperature = 400
+
+/obj/item/projectile/temp/cryo
+ name = "cryo beam"
+ range = 3
+ temperature = -240 // Single slow shot reduces temp greatly
\ No newline at end of file
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index be15bb70cf..2d177318aa 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -144,14 +144,14 @@
..()
icon_state = "[(nopower_state && !powered()) ? nopower_state : initial(icon_state)]"
-/obj/machinery/chem_dispenser/update_icon()
- cut_overlays()
+/obj/machinery/chem_dispenser/update_overlays()
+ . = ..()
if(has_panel_overlay && panel_open)
- add_overlay(mutable_appearance(icon, "[initial(icon_state)]_panel-o"))
+ . += mutable_appearance(icon, "[initial(icon_state)]_panel-o")
if(beaker)
beaker_overlay = display_beaker()
- add_overlay(beaker_overlay)
+ . += beaker_overlay
/obj/machinery/chem_dispenser/emag_act(mob/user)
. = ..()
diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm
index 4c4d0fdb6e..63e9d724a4 100644
--- a/code/modules/reagents/chemistry/machinery/chem_heater.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm
@@ -22,7 +22,7 @@
beaker = null
update_icon()
-/obj/machinery/chem_heater/update_icon()
+/obj/machinery/chem_heater/update_icon_state()
if(beaker)
icon_state = "mixer1b"
else
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index abfd00ed90..22ff88d793 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -67,15 +67,17 @@
bottle = null
update_icon()
-/obj/machinery/chem_master/update_icon()
- cut_overlays()
- if (stat & BROKEN)
- add_overlay("waitlight")
+/obj/machinery/chem_master/update_icon_state()
if(beaker)
icon_state = "mixer1"
else
icon_state = "mixer0"
+/obj/machinery/chem_master/update_overlays()
+ . = ..()
+ if (stat & BROKEN)
+ . += "waitlight"
+
/obj/machinery/chem_master/blob_act(obj/structure/blob/B)
if (prob(50))
qdel(src)
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index 7b25aa769d..25315f457d 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -109,16 +109,16 @@
update_icon()
playsound(loc, 'sound/machines/ping.ogg', 30, 1)
-/obj/machinery/computer/pandemic/update_icon()
+/obj/machinery/computer/pandemic/update_icon_state()
if(stat & BROKEN)
icon_state = (beaker ? "mixer1_b" : "mixer0_b")
- return
-
- icon_state = "mixer[(beaker) ? "1" : "0"][powered() ? "" : "_nopower"]"
- if(wait)
- add_overlay("waitlight")
else
- cut_overlays()
+ icon_state = "mixer[(beaker) ? "1" : "0"][powered() ? "" : "_nopower"]"
+
+/obj/machinery/computer/pandemic/update_overlays()
+ . = ..()
+ if(!(stat & BROKEN) && wait)
+ . += "waitlight"
/obj/machinery/computer/pandemic/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open = FALSE, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
index 85cdc33e72..75848f89b9 100644
--- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -66,7 +66,7 @@
AM.forceMove(drop_location())
holdingitems = list()
-/obj/machinery/reagentgrinder/update_icon()
+/obj/machinery/reagentgrinder/update_icon_state()
if(beaker)
icon_state = "juicer1"
else
diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
index 189f58fec3..0a08395c1b 100644
--- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm
+++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
@@ -34,7 +34,7 @@
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
reagents.maximum_volume += REAGENTS_BASE_VOLUME * B.rating
-/obj/machinery/smoke_machine/update_icon()
+/obj/machinery/smoke_machine/update_icon_state()
if((!is_operational()) || (!on) || (reagents.total_volume == 0))
if (panel_open)
icon_state = "smoke0-o"
@@ -42,7 +42,6 @@
icon_state = "smoke0"
else
icon_state = "smoke1"
- return ..()
/obj/machinery/smoke_machine/RefreshParts()
var/new_volume = REAGENTS_BASE_VOLUME
diff --git a/code/modules/reagents/chemistry/reagents/blob_reagents.dm b/code/modules/reagents/chemistry/reagents/blob_reagents.dm
deleted file mode 100644
index fba0b47eb4..0000000000
--- a/code/modules/reagents/chemistry/reagents/blob_reagents.dm
+++ /dev/null
@@ -1,487 +0,0 @@
-// These can only be applied by blobs. They are what blobs are made out of.
-/datum/reagent/blob
- name = "Unknown"
- description = "shouldn't exist and you should adminhelp immediately."
- color = "#FFFFFF"
- taste_description = "bad code and slime"
- var/complementary_color = "#000000" //a color that's complementary to the normal blob color
- var/shortdesc = null //just damage and on_mob effects, doesn't include special, blob-tile only effects
- var/effectdesc = null //any long, blob-tile specific effects
- var/analyzerdescdamage = "Unknown. Report this bug to a coder, or just adminhelp."
- var/analyzerdesceffect = "N/A"
- var/blobbernaut_message = "slams" //blobbernaut attack verb
- var/message = "The blob strikes you" //message sent to any mob hit by the blob
- var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt
- can_synth = FALSE
-
-/datum/reagent/blob/proc/send_message(mob/living/M)
- var/totalmessage = message
- if(message_living && !issilicon(M))
- totalmessage += message_living
- totalmessage += "!"
- to_chat(M, "[totalmessage]")
-
-/datum/reagent/blob/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
- return 0 //the dead, and blob mobs, don't cause reactions
- return round(reac_volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume.
-
-/datum/reagent/blob/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) //when the blob takes damage, do this
- return damage
-
-/datum/reagent/blob/proc/death_reaction(obj/structure/blob/B, damage_flag) //when a blob dies, do this
- return
-
-/datum/reagent/blob/proc/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O) //when the blob expands, do this
- return
-
-/datum/reagent/blob/proc/tesla_reaction(obj/structure/blob/B, power) //when the blob is hit by a tesla bolt, do this
- return 1 //return 0 to ignore damage
-
-/datum/reagent/blob/proc/extinguish_reaction(obj/structure/blob/B) //when the blob is hit with water, do this
- return
-
-/datum/reagent/blob/proc/emp_reaction(obj/structure/blob/B, severity) //when the blob is hit with an emp, do this
- return
-
-//does brute damage but can replicate when damaged and has a chance of expanding again
-/datum/reagent/blob/replicating_foam
- name = "Replicating Foam"
- description = "will do medium brute damage and occasionally expand again when expanding."
- shortdesc = "will do medium brute damage."
- effectdesc = "will also expand when attacked with burn damage, but takes more brute damage."
- taste_description = "duplication"
- analyzerdescdamage = "Does medium brute damage."
- analyzerdesceffect = "Expands when attacked with burn damage, will occasionally expand again when expanding, and is fragile to brute damage."
- color = "#7B5A57"
- complementary_color = "#57787B"
-
-/datum/reagent/blob/replicating_foam/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.7*reac_volume, BRUTE)
-
-/datum/reagent/blob/replicating_foam/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_type == BRUTE)
- damage = damage * 2
- else if(damage_type == BURN && damage > 0 && B.obj_integrity - damage > 0 && prob(60))
- var/obj/structure/blob/newB = B.expand(null, null, 0)
- if(newB)
- newB.obj_integrity = B.obj_integrity - damage
- newB.update_icon()
- return ..()
-
-/datum/reagent/blob/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(prob(30))
- newB.expand(null, null, 0) //do it again!
-
-//does massive brute and burn damage, but can only expand manually
-/datum/reagent/blob/networked_fibers
- name = "Networked Fibers"
- description = "will do high brute and burn damage and will generate resources quicker, but can only expand manually."
- shortdesc = "will do high brute and burn damage."
- taste_description = "efficiency"
- effectdesc = "will move your core when manually expanding near it."
- analyzerdescdamage = "Does high brute and burn damage."
- analyzerdesceffect = "Is highly mobile and generates resources rapidly."
- color = "#CDC0B0"
- complementary_color = "#FFF68F"
-
-/datum/reagent/blob/networked_fibers/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.6*reac_volume, BRUTE)
- if(M)
- M.apply_damage(0.6*reac_volume, BURN)
-
-/datum/reagent/blob/networked_fibers/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(!O && newB.overmind)
- if(!istype(B, /obj/structure/blob/node))
- newB.overmind.add_points(1)
- qdel(newB)
- else
- var/area/A = get_area(T)
- if(!isspaceturf(T) && !istype(A, /area/shuttle))
- for(var/obj/structure/blob/core/C in range(1, newB))
- if(C.overmind == O)
- newB.forceMove(get_turf(C))
- C.forceMove(T)
- C.setDir(get_dir(newB, C))
- O.add_points(1)
-
-//does brute damage, shifts away when damaged
-/datum/reagent/blob/shifting_fragments
- name = "Shifting Fragments"
- description = "will do medium brute damage."
- effectdesc = "will also cause blob parts to shift away when attacked."
- taste_description = "something other-dimensional"
- analyzerdescdamage = "Does medium brute damage."
- analyzerdesceffect = "When attacked, may shift away from the attacker."
- color = "#C8963C"
- complementary_color = "#3C6EC8"
-
-/datum/reagent/blob/shifting_fragments/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.7*reac_volume, BRUTE)
-
-/datum/reagent/blob/shifting_fragments/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25)))
- newB.forceMove(get_turf(B))
- B.forceMove(T)
-
-/datum/reagent/blob/shifting_fragments/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage > 0 && B.obj_integrity - damage > 0 && prob(60-damage))
- var/list/blobstopick = list()
- for(var/obj/structure/blob/OB in orange(1, B))
- if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind.blob_reagent_datum.type == B.overmind.blob_reagent_datum.type)
- blobstopick += OB //as long as the blob picked is valid; ie, a normal or shield blob that has the same chemical as we do, we can swap with it
- if(blobstopick.len)
- var/obj/structure/blob/targeted = pick(blobstopick) //randomize the blob chosen, because otherwise it'd tend to the lower left
- var/turf/T = get_turf(targeted)
- targeted.forceMove(get_turf(B))
- B.forceMove(T) //swap the blobs
- return ..()
-
-//sets you on fire, does burn damage, explodes into flame when burnt, weak to water
-/datum/reagent/blob/blazing_oil
- name = "Blazing Oil"
- description = "will do medium burn damage and set targets on fire."
- effectdesc = "will also release bursts of flame when burnt, but takes damage from water."
- taste_description = "burning oil"
- analyzerdescdamage = "Does medium burn damage and sets targets on fire."
- analyzerdesceffect = "Releases fire when burnt, but takes damage from water and other extinguishing liquids."
- color = "#B68D00"
- complementary_color = "#BE5532"
- blobbernaut_message = "splashes"
- message = "The blob splashes you with burning oil"
- message_living = ", and you feel your skin char and melt"
-
-/datum/reagent/blob/blazing_oil/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.adjust_fire_stacks(round(reac_volume/10))
- M.IgniteMob()
- if(M)
- M.apply_damage(0.8*reac_volume, BURN)
- if(iscarbon(M))
- M.emote("scream")
-
-/datum/reagent/blob/blazing_oil/extinguish_reaction(obj/structure/blob/B)
- B.take_damage(1.5, BURN, "energy")
-
-/datum/reagent/blob/blazing_oil/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_type == BURN && damage_flag != "energy")
- for(var/turf/open/T in range(1, B))
- var/obj/structure/blob/C = locate() in T
- if(!(C && C.overmind && C.overmind.blob_reagent_datum.type == B.overmind.blob_reagent_datum.type) && prob(80))
- new /obj/effect/hotspot(T)
- if(damage_flag == "fire")
- return 0
- return ..()
-
-//does toxin damage, hallucination, targets think they're not hurt at all
-/datum/reagent/blob/regenerative_materia
- name = "Regenerative Materia"
- description = "will do toxin damage and cause targets to believe they are fully healed."
- analyzerdescdamage = "Does toxin damage and injects a toxin that causes the target to believe they are fully healed."
- taste_description = "heaven"
- color = "#5e7842"
- complementary_color = "#CD7794"
- message_living = ", and you feel alive"
-
-/datum/reagent/blob/regenerative_materia/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.adjust_drugginess(reac_volume)
- if(M.reagents)
- M.reagents.add_reagent(/datum/reagent/blob/regenerative_materia, 0.2*reac_volume)
- M.reagents.add_reagent(/datum/reagent/toxin/spore, 0.2*reac_volume)
- M.apply_damage(0.7*reac_volume, TOX)
-
-/datum/reagent/blob/regenerative_materia/on_mob_life(mob/living/carbon/C)
- C.adjustToxLoss(1*REM)
- C.hal_screwyhud = SCREWYHUD_HEALTHY //fully healed, honest
- ..()
-
-/datum/reagent/blob/regenerative_materia/on_mob_end_metabolize(mob/living/M)
- if(iscarbon(M))
- var/mob/living/carbon/N = M
- N.hal_screwyhud = 0
- ..()
-
-//kills sleeping targets and turns them into blob zombies, produces fragile spores when killed or on expanding
-/datum/reagent/blob/zombifying_pods
- name = "Zombifying Pods"
- description = "will do very low toxin damage and harvest sleeping targets for additional resources and a blob zombie."
- effectdesc = "will also produce fragile spores when killed and on expanding."
- taste_description = "fungi"
- shortdesc = "will do very low toxin damage and harvest sleeping targets for additional resources(for your overmind) and a blob zombie."
- analyzerdescdamage = "Does very low toxin damage and kills unconscious humans, turning them into blob zombies."
- analyzerdesceffect = "Produces spores when expanding and when killed."
- color = "#E88D5D"
- complementary_color = "#823ABB"
- message_living = ", and you feel tired"
-
-/datum/reagent/blob/zombifying_pods/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.6*reac_volume, TOX)
- if(O && ishuman(M) && M.stat == UNCONSCIOUS)
- M.death() //sleeping in a fight? bad plan.
- var/points = rand(5, 10)
- var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(get_turf(M))
- BS.overmind = O
- BS.update_icons()
- O.blob_mobs.Add(BS)
- BS.Zombify(M)
- O.add_points(points)
- to_chat(O, "Gained [points] resources from the zombification of [M].")
-
-/datum/reagent/blob/zombifying_pods/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage <= 20 && B.obj_integrity - damage <= 0 && prob(30)) //if the cause isn't fire or a bomb, the damage is less than 21, we're going to die from that damage, 20% chance of a shitty spore.
- B.visible_message("A spore floats free of the blob!")
- var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(B.loc)
- BS.overmind = B.overmind
- BS.update_icons()
- B.overmind.blob_mobs.Add(BS)
- return ..()
-
-/datum/reagent/blob/zombifying_pods/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(prob(10))
- var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(T)
- BS.overmind = B.overmind
- BS.update_icons()
- newB.overmind.blob_mobs.Add(BS)
-
-//does tons of oxygen damage and a little stamina, immune to tesla bolts, weak to EMP
-/datum/reagent/blob/energized_jelly
- name = "Energized Jelly"
- description = "will cause low stamina and high oxygen damage, and cause targets to be unable to breathe."
- taste_description = "gelatin"
- effectdesc = "will also conduct electricity, but takes damage from EMPs."
- analyzerdescdamage = "Does low stamina damage, high oxygen damage, and prevents targets from breathing."
- analyzerdesceffect = "Is immune to electricity and will easily conduct it, but is weak to EMPs."
- color = "#EFD65A"
- complementary_color = "#00E5B1"
- message_living = ", and you feel a horrible tingling sensation"
-
-/datum/reagent/blob/energized_jelly/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.losebreath += round(0.2*reac_volume)
- M.adjustStaminaLoss(0.4*reac_volume)
- if(M)
- M.apply_damage(0.6*reac_volume, OXY)
-
-/datum/reagent/blob/energized_jelly/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && B.obj_integrity - damage <= 0 && prob(10))
- do_sparks(rand(2, 4), FALSE, B)
- return ..()
-
-/datum/reagent/blob/energized_jelly/tesla_reaction(obj/structure/blob/B, power)
- return 0
-
-/datum/reagent/blob/energized_jelly/emp_reaction(obj/structure/blob/B, severity)
- var/damage = rand(30, 50) - severity * rand(10, 15)
- B.take_damage(damage, BURN, "energy")
-
-//does aoe brute damage when hitting targets, is immune to explosions
-/datum/reagent/blob/explosive_lattice
- name = "Explosive Lattice"
- description = "will do brute damage in an area around targets."
- taste_description = "the bomb"
- effectdesc = "will also resist explosions, but takes increased damage from fire and other energy sources."
- analyzerdescdamage = "Does medium brute damage and causes damage to everyone near its targets."
- analyzerdesceffect = "Is highly resistant to explosions, but takes increased damage from fire and other energy sources."
- color = "#8B2500"
- complementary_color = "#00668B"
- blobbernaut_message = "blasts"
- message = "The blob blasts you"
-
-/datum/reagent/blob/explosive_lattice/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- var/initial_volume = reac_volume
- reac_volume = ..()
- if(reac_volume >= 10) //if it's not a spore cloud, bad time incoming
- var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M))
- E.alpha = 150
- for(var/mob/living/L in orange(get_turf(M), 1))
- if(ROLE_BLOB in L.faction) //no friendly fire
- continue
- var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
- L.apply_damage(0.4*aoe_volume, BRUTE)
- if(M)
- M.apply_damage(0.6*reac_volume, BRUTE)
- else
- M.apply_damage(0.6*reac_volume, BRUTE)
-
-/datum/reagent/blob/explosive_lattice/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_flag == "bomb")
- return 0
- else if(damage_flag != "melee" && damage_flag != "bullet" && damage_flag != "laser")
- return damage * 1.5
- return ..()
-
-//does brute, burn, and toxin damage, and cools targets down
-/datum/reagent/blob/cryogenic_poison
- name = "Cryogenic Poison"
- description = "will inject targets with a freezing poison that does high damage over time."
- analyzerdescdamage = "Injects targets with a freezing poison that will gradually solidify the target's internal organs."
- color = "#8BA6E9"
- taste_description = "brain freeze"
- complementary_color = "#7D6EB4"
- blobbernaut_message = "injects"
- message = "The blob stabs you"
- message_living = ", and you feel like your insides are solidifying"
-
-/datum/reagent/blob/cryogenic_poison/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- if(M.reagents)
- M.reagents.add_reagent(/datum/reagent/consumable/frostoil, 0.3*reac_volume)
- M.reagents.add_reagent(/datum/reagent/consumable/ice, 0.3*reac_volume)
- M.reagents.add_reagent(/datum/reagent/blob/cryogenic_poison, 0.3*reac_volume)
- M.apply_damage(0.2*reac_volume, BRUTE)
-
-/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/M)
- M.adjustBruteLoss(0.3*REM, 0)
- M.adjustFireLoss(0.3*REM, 0)
- M.adjustToxLoss(0.3*REM, 0)
- . = 1
- ..()
-
-//does burn damage and EMPs, slightly fragile
-/datum/reagent/blob/electromagnetic_web
- name = "Electromagnetic Web"
- description = "will do high burn damage and EMP targets."
- taste_description = "pop rocks"
- effectdesc = "will also take massively increased damage and release an EMP when killed."
- analyzerdescdamage = "Does low burn damage and EMPs targets."
- analyzerdesceffect = "Is fragile to all types of damage, but takes massive damage from brute. In addition, releases a small EMP when killed."
- color = "#83ECEC"
- complementary_color = "#EC8383"
- blobbernaut_message = "lashes"
- message = "The blob lashes you"
- message_living = ", and you hear a faint buzzing"
-
-/datum/reagent/blob/electromagnetic_web/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- if(prob(reac_volume*2))
- M.emp_act(EMP_LIGHT)
- if(M)
- M.apply_damage(reac_volume, BURN)
-
-/datum/reagent/blob/electromagnetic_web/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_type == BRUTE) //take full brute
- switch(B.brute_resist)
- if(0.5)
- return damage * 2
- if(0.25)
- return damage * 4
- if(0.1)
- return damage * 10
- return damage * 1.25 //a laser will do 25 damage, which will kill any normal blob
-
-/datum/reagent/blob/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag)
- if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
- empulse(B.loc, 1, 3) //less than screen range, so you can stand out of range to avoid it
-
-//does brute damage, bonus damage for each nearby blob, and spreads damage out
-/datum/reagent/blob/synchronous_mesh
- name = "Synchronous Mesh"
- description = "will do massively increased brute damage for each blob near the target."
- effectdesc = "will also spread damage between each blob near the attacked blob."
- taste_description = "toxic mold"
- analyzerdescdamage = "Does brute damage, increasing for each blob near the target."
- analyzerdesceffect = "When attacked, spreads damage between all blobs near the attacked blob."
- color = "#65ADA2"
- complementary_color = "#AD6570"
- blobbernaut_message = "synchronously strikes"
- message = "The blobs strike you"
-
-/datum/reagent/blob/synchronous_mesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.2*reac_volume, BRUTE)
- if(M && reac_volume)
- for(var/obj/structure/blob/B in range(1, M)) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume
- if(M)
- B.blob_attack_animation(M) //show them they're getting a bad time
- M.apply_damage(0.3*reac_volume, BRUTE)
-
-/datum/reagent/blob/synchronous_mesh/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") //the cause isn't fire or bombs, so split the damage
- var/damagesplit = 1 //maximum split is 9, reducing the damage each blob takes to 11% but doing that damage to 9 blobs
- for(var/obj/structure/blob/C in orange(1, B))
- if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blob_reagent_datum.type == B.overmind.blob_reagent_datum.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it
- damagesplit += 1
- for(var/obj/structure/blob/C in orange(1, B))
- if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blob_reagent_datum.type == B.overmind.blob_reagent_datum.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes
- C.take_damage(damage/damagesplit, CLONE, 0, 0)
- return damage / damagesplit
- else
- return damage * 1.25
-
-//does brute damage through armor and bio resistance
-/datum/reagent/blob/reactive_spines
- name = "Reactive Spines"
- description = "will do medium brute damage through armor and bio resistance."
- taste_description = "rock"
- effectdesc = "will also react when attacked with brute damage, attacking all near the attacked blob."
- analyzerdescdamage = "Does medium brute damage, ignoring armor and bio resistance."
- analyzerdesceffect = "When attacked with brute damage, will lash out, attacking everything near it."
- color = "#9ACD32"
- complementary_color = "#FFA500"
- blobbernaut_message = "stabs"
- message = "The blob stabs you"
-
-/datum/reagent/blob/reactive_spines/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
- return 0 //the dead, and blob mobs, don't cause reactions
- M.adjustBruteLoss(0.8*reac_volume)
-
-/datum/reagent/blob/reactive_spines/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage && damage_type == BRUTE && B.obj_integrity - damage > 0) //is there any damage, is it brute, and will we be alive
- if(damage_flag == "melee")
- B.visible_message("The blob retaliates, lashing out!")
- for(var/atom/A in range(1, B))
- A.blob_act(B)
- return ..()
-
-//does low brute damage, oxygen damage, and stamina damage and wets tiles when damaged
-/datum/reagent/blob/pressurized_slime
- name = "Pressurized Slime"
- description = "will do low brute, oxygen, and stamina damage, and wet tiles under targets."
- effectdesc = "will also wet tiles near blobs that are attacked or killed."
- taste_description = "a sponge"
- analyzerdescdamage = "Does low brute damage, low oxygen damage, drains stamina, and wets tiles under targets, extinguishing them."
- analyzerdesceffect = "When attacked or killed, wets nearby tiles, extinguishing anything on them."
- color = "#AAAABB"
- complementary_color = "#BBBBAA"
- blobbernaut_message = "emits slime at"
- message = "The blob splashes into you"
- message_living = ", and you gasp for breath"
-
-/datum/reagent/blob/pressurized_slime/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- var/turf/open/T = get_turf(M)
- if(istype(T) && prob(reac_volume))
- T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
- M.adjust_fire_stacks(-(reac_volume / 10))
- M.ExtinguishMob()
- M.apply_damage(0.4*reac_volume, BRUTE)
- if(M)
- M.apply_damage(0.4*reac_volume, OXY)
- if(M)
- M.adjustStaminaLoss(0.2*reac_volume)
-
-/datum/reagent/blob/pressurized_slime/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") || damage_type != BURN)
- extinguisharea(B, damage)
- return ..()
-
-/datum/reagent/blob/pressurized_slime/death_reaction(obj/structure/blob/B, damage_flag)
- if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
- B.visible_message("The blob ruptures, spraying the area with liquid!")
- extinguisharea(B, 50)
-
-/datum/reagent/blob/pressurized_slime/proc/extinguisharea(obj/structure/blob/B, probchance)
- for(var/turf/open/T in range(1, B))
- if(prob(probchance))
- T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
- for(var/obj/O in T)
- O.extinguish()
- for(var/mob/living/L in T)
- L.adjust_fire_stacks(-2.5)
- L.ExtinguishMob()
diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
index 04e7c0a74e..0b634ba2d8 100644
--- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
@@ -171,9 +171,11 @@
/datum/reagent/drug/methamphetamine/on_mob_metabolize(mob/living/L)
..()
L.ignore_slowdown(type)
+ ADD_TRAIT(L, TRAIT_TASED_RESISTANCE, type)
/datum/reagent/drug/methamphetamine/on_mob_end_metabolize(mob/living/L)
L.unignore_slowdown(type)
+ REMOVE_TRAIT(L, TRAIT_TASED_RESISTANCE, type)
..()
/datum/reagent/drug/methamphetamine/on_mob_life(mob/living/carbon/M)
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 4b2c1447c5..46002287dd 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -218,7 +218,8 @@
. = ..()
if(iscarbon(M))
var/mob/living/carbon/patient = M
- if(reac_volume >= 5 && HAS_TRAIT_FROM(patient, TRAIT_HUSK, "burn") && patient.getFireLoss() < THRESHOLD_UNHUSK) //One carp yields 12u rezadone.
+ var/vol = reac_volume + M.reagents.get_reagent_amount(/datum/reagent/medicine/rezadone)
+ if(vol >= 5 && HAS_TRAIT_FROM(patient, TRAIT_HUSK, "burn") && patient.getFireLoss() < THRESHOLD_UNHUSK) //One carp yields 12u rezadone.
patient.cure_husk("burn")
patient.visible_message("[patient]'s body rapidly absorbs moisture from the enviroment, taking on a more healthy appearance.")
@@ -425,7 +426,7 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
overdose_threshold = 40
value = 6
-/datum/reagent/medicine/synthflesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume,show_message = 1)
+/datum/reagent/medicine/synthflesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1)
if(iscarbon(M))
if (M.stat == DEAD)
show_message = 0
@@ -442,8 +443,9 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
if(show_message)
to_chat(M, "You feel your burns and bruises healing! It stings like hell!")
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "painful_medicine", /datum/mood_event/painful_medicine)
+ var/vol = reac_volume + M.reagents.get_reagent_amount(/datum/reagent/medicine/synthflesh)
//Has to be at less than THRESHOLD_UNHUSK burn damage and have 100 synthflesh before unhusking. Corpses dont metabolize.
- if(HAS_TRAIT_FROM(M, TRAIT_HUSK, "burn") && M.getFireLoss() < THRESHOLD_UNHUSK && M.reagents.has_reagent(/datum/reagent/medicine/synthflesh, 100))
+ if(HAS_TRAIT_FROM(M, TRAIT_HUSK, "burn") && M.getFireLoss() < THRESHOLD_UNHUSK && (vol > 100))
M.cure_husk("burn")
M.visible_message("Most of [M]'s burnt off or charred flesh has been restored.")
..()
@@ -957,7 +959,7 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
/datum/reagent/medicine/mutadone/on_mob_life(mob/living/carbon/M)
M.jitteriness = 0
if(M.has_dna())
- M.dna.remove_all_mutations()
+ M.dna.remove_all_mutations(mutadone = TRUE)
if(!QDELETED(M)) //We were a monkey, now a human
..()
@@ -992,9 +994,11 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
/datum/reagent/medicine/stimulants/on_mob_metabolize(mob/living/L)
..()
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.5, blacklisted_movetypes=(FLYING|FLOATING))
+ ADD_TRAIT(L, TRAIT_TASED_RESISTANCE, type)
/datum/reagent/medicine/stimulants/on_mob_end_metabolize(mob/living/L)
L.remove_movespeed_modifier(type)
+ REMOVE_TRAIT(L, TRAIT_TASED_RESISTANCE, type)
..()
/datum/reagent/medicine/stimulants/on_mob_life(mob/living/carbon/M)
@@ -1299,6 +1303,14 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
color = "#918e53"
overdose_threshold = 30
+/datum/reagent/medicine/changelingadrenaline/on_mob_metabolize(mob/living/L)
+ ..()
+ ADD_TRAIT(L, TRAIT_TASED_RESISTANCE, type)
+
+/datum/reagent/medicine/changelingadrenaline/on_mob_end_metabolize(mob/living/L)
+ REMOVE_TRAIT(L, TRAIT_TASED_RESISTANCE, type)
+ ..()
+
/datum/reagent/medicine/changelingadrenaline/on_mob_life(mob/living/carbon/M as mob)
M.AdjustUnconscious(-20, 0)
M.AdjustAllImmobility(-20, 0)
diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
index cd63fff0db..939d5c9707 100644
--- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
@@ -213,7 +213,7 @@
shock_timer++
if(shock_timer >= rand(5,30)) //Random shocks are wildly unpredictable
shock_timer = 0
- M.electrocute_act(rand(5,20), "Teslium in their body", 1, 1) //Override because it's caused from INSIDE of you
+ M.electrocute_act(rand(5,20), "Teslium in their body", 1, SHOCK_NOGLOVES) //Override because it's caused from INSIDE of you
playsound(M, "sparks", 50, 1)
..()
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 025587bc2a..cfb0588f8f 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -40,9 +40,9 @@
if((method==VAPOR && prob(min(33, reac_volume))) || method==INGEST || method==PATCH || method==INJECT)
M.randmuti()
if(prob(98))
- M.randmutb()
+ M.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- M.randmutg()
+ M.easy_randmut(POSITIVE)
M.updateappearance()
M.domutcheck()
..()
diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm
index 57748acb1a..ce1940994d 100644
--- a/code/modules/reagents/chemistry/recipes/others.dm
+++ b/code/modules/reagents/chemistry/recipes/others.dm
@@ -75,6 +75,28 @@
for(var/i = 1, i <= multiplier, i++)
new /obj/item/stack/sheet/mineral/gold(location)
+/datum/chemical_reaction/uraniumsolidification
+ name = "Solid Uranium"
+ id = "soliduranium"
+ required_reagents = list(/datum/reagent/consumable/frostoil = 5, /datum/reagent/uranium = 20, /datum/reagent/bromine = 1)
+ mob_react = FALSE
+
+/datum/chemical_reaction/uraniumsolidification/on_reaction(datum/reagents/holder, multiplier)
+ var/location = get_turf(holder.my_atom)
+ for(var/i = 1, i <= multiplier, i++)
+ new /obj/item/stack/sheet/mineral/uranium(location)
+
+/datum/chemical_reaction/bluespacecrystalifaction
+ name = "Crystal Bluespace"
+ id = "crystalbluespace"
+ required_reagents = list(/datum/reagent/consumable/frostoil = 5, /datum/reagent/bluespace = 20, /datum/reagent/iron = 1)
+ mob_react = FALSE
+
+/datum/chemical_reaction/bluespacecrystalifaction/on_reaction(datum/reagents/holder, multiplier)
+ var/location = get_turf(holder.my_atom)
+ for(var/i = 1, i <= multiplier, i++)
+ new /obj/item/stack/sheet/bluespace_crystal(location)
+
/datum/chemical_reaction/capsaicincondensation
name = "Capsaicincondensation"
id = /datum/reagent/consumable/condensedcapsaicin
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index 3296586c94..6be2e658c1 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -34,15 +34,11 @@
else
name = "blood pack"
-/obj/item/reagent_containers/blood/update_icon()
- cut_overlays()
-
+/obj/item/reagent_containers/blood/update_overlays()
+ . = ..()
var/v = min(round(reagents.total_volume / volume * 10), 10)
if(v > 0)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "bloodpack1")
- filling.icon_state = "bloodpack[v]"
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
+ . += mutable_appearance('icons/obj/reagentfillings.dmi', "bloodpack[v]", color = mix_color_from_reagents(reagents.reagent_list))
/obj/item/reagent_containers/blood/random
icon_state = "random_bloodpack"
@@ -120,7 +116,7 @@
else
if(!do_mob(user, C, 10))
return
-
+
to_chat(user, "You take a sip from the [src].")
user.visible_message("[user] puts the [src] up to their mouth.")
if(reagents.total_volume <= 0) // Safety: In case you spam clicked the blood bag on yourself, and it is now empty (below will divide by zero)
diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm
index e4741bb858..cc1cd21bac 100644
--- a/code/modules/reagents/reagent_containers/bottle.dm
+++ b/code/modules/reagents/reagent_containers/bottle.dm
@@ -18,26 +18,27 @@
/obj/item/reagent_containers/glass/bottle/on_reagent_change(changetype)
update_icon()
-/obj/item/reagent_containers/glass/bottle/update_icon()
- cut_overlays()
+/obj/item/reagent_containers/glass/bottle/update_overlays()
+ . = ..()
+ if(!cached_icon)
+ cached_icon = icon_state
if(reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "[icon_state]-10")
+ var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "[cached_icon]-10", color = mix_color_from_reagents(reagents.reagent_list))
var/percent = round((reagents.total_volume / volume) * 100)
switch(percent)
if(0 to 9)
- filling.icon_state = "[icon_state]-10"
+ filling.icon_state = "[cached_icon]-10"
if(10 to 29)
- filling.icon_state = "[icon_state]25"
+ filling.icon_state = "[cached_icon]25"
if(30 to 49)
- filling.icon_state = "[icon_state]50"
+ filling.icon_state = "[cached_icon]50"
if(50 to 69)
- filling.icon_state = "[icon_state]75"
+ filling.icon_state = "[cached_icon]75"
if(70 to INFINITY)
- filling.icon_state = "[icon_state]100"
+ filling.icon_state = "[cached_icon]100"
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
+ . += filling
/obj/item/reagent_containers/glass/bottle/epinephrine
diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm
index 4e1de85fce..846637850b 100644
--- a/code/modules/reagents/reagent_containers/dropper.dm
+++ b/code/modules/reagents/reagent_containers/dropper.dm
@@ -86,12 +86,10 @@
update_icon()
-/obj/item/reagent_containers/dropper/update_icon()
- cut_overlays()
+/obj/item/reagent_containers/dropper/update_overlays()
+ . = ..()
if(reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "dropper")
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
+ . += mutable_appearance('icons/obj/reagentfillings.dmi', "dropper", color = mix_color_from_reagents(reagents.reagent_list))
/obj/item/reagent_containers/dropper/get_belt_overlay()
return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "pouch")
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index bf888dc50a..74e99cc326 100644
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -133,13 +133,13 @@
/obj/item/reagent_containers/glass/beaker/on_reagent_change(changetype)
update_icon()
-/obj/item/reagent_containers/glass/beaker/update_icon()
+/obj/item/reagent_containers/glass/beaker/update_overlays()
+ . = ..()
if(!cached_icon)
cached_icon = icon_state
- cut_overlays()
if(reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "[cached_icon]10")
+ var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "[cached_icon]10", color = mix_color_from_reagents(reagents.reagent_list))
var/percent = round((reagents.total_volume / volume) * 100)
switch(percent)
@@ -157,9 +157,7 @@
filling.icon_state = "[cached_icon]80"
if(91 to INFINITY)
filling.icon_state = "[cached_icon]100"
-
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
+ . += filling
/obj/item/reagent_containers/glass/beaker/jar
name = "honey jar"
@@ -215,11 +213,7 @@
amount_per_transfer_from_this = 10
possible_transfer_amounts = list(5,10,15,20,25,30,40,50,60,120,180)
container_flags = TEMP_WEAK|APTFT_ALTCLICK|APTFT_VERB
-
-/obj/item/reagent_containers/glass/beaker/plastic/update_icon()
- icon_state = "beakerlarge" // hack to lets us reuse the large beaker reagent fill states
- ..()
- icon_state = "beakerwhite"
+ cached_icon = "beakerlarge"
/obj/item/reagent_containers/glass/beaker/meta
name = "metamaterial beaker"
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index f29b785c95..3c4be7969d 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -119,7 +119,7 @@
reagents.add_reagent_list(list_reagents)
update_icon()
-/obj/item/reagent_containers/hypospray/medipen/update_icon()
+/obj/item/reagent_containers/hypospray/medipen/update_icon_state()
if(reagents.total_volume > 0)
icon_state = initial(icon_state)
else
@@ -282,13 +282,12 @@
vial = new start_vial
update_icon()
-/obj/item/hypospray/mkii/update_icon()
- ..()
+/obj/item/hypospray/mkii/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/hypospray/mkii/update_icon_state()
icon_state = "[initial(icon_state)][vial ? "" : "-e"]"
- if(ismob(loc))
- var/mob/M = loc
- M.update_inv_hands()
- return
/obj/item/hypospray/mkii/examine(mob/user)
. = ..()
diff --git a/code/modules/reagents/reagent_containers/hypovial.dm b/code/modules/reagents/reagent_containers/hypovial.dm
index db2d73c697..8d99248d97 100644
--- a/code/modules/reagents/reagent_containers/hypovial.dm
+++ b/code/modules/reagents/reagent_containers/hypovial.dm
@@ -19,37 +19,15 @@
"pink hypovial" = "hypovial-pink"
)
always_reskinnable = TRUE
+ cached_icon = "hypovial"
/obj/item/reagent_containers/glass/bottle/vial/Initialize()
. = ..()
- if(!icon_state)
- icon_state = "hypovial"
update_icon()
/obj/item/reagent_containers/glass/bottle/vial/on_reagent_change()
update_icon()
-/obj/item/reagent_containers/glass/bottle/vial/update_icon()
- cut_overlays()
- if(reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "hypovial10")
-
- var/percent = round((reagents.total_volume / volume) * 100)
- switch(percent)
- if(0 to 9)
- filling.icon_state = "hypovial10"
- if(10 to 29)
- filling.icon_state = "hypovial25"
- if(30 to 49)
- filling.icon_state = "hypovial50"
- if(50 to 85)
- filling.icon_state = "hypovial75"
- if(86 to INFINITY)
- filling.icon_state = "hypovial100"
-
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
-
/obj/item/reagent_containers/glass/bottle/vial/tiny
name = "small hypovial"
//Shouldn't be possible to get this without adminbuse
@@ -80,27 +58,7 @@
"large purple hypovial" = "hypoviallarge-p",
"large black hypovial" = "hypoviallarge-t"
)
-
-/obj/item/reagent_containers/glass/bottle/vial/large/update_icon()
- cut_overlays()
- if(reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "hypoviallarge10")
-
- var/percent = round((reagents.total_volume / volume) * 100)
- switch(percent)
- if(0 to 9)
- filling.icon_state = "hypoviallarge10"
- if(10 to 29)
- filling.icon_state = "hypoviallarge25"
- if(30 to 49)
- filling.icon_state = "hypoviallarge50"
- if(50 to 85)
- filling.icon_state = "hypoviallarge75"
- if(86 to INFINITY)
- filling.icon_state = "hypoviallarge100"
-
- filling.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling)
+ cached_icon = "hypoviallarge"
/obj/item/reagent_containers/glass/bottle/vial/large/bluespace
possible_transfer_amounts = list(1,2,5,10,20)
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 45bf8591a8..b8957775b1 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -12,6 +12,7 @@
var/mode = SYRINGE_DRAW
var/busy = FALSE // needed for delayed drawing of blood
var/proj_piercing = 0 //does it pierce through thick clothes when shot with syringe gun
+ var/show_filling = TRUE
custom_materials = list(/datum/material/iron=10, /datum/material/glass=20)
reagent_flags = TRANSPARENT
@@ -21,6 +22,10 @@
mode = SYRINGE_INJECT
update_icon()
+/obj/item/reagent_containers/syringe/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
/obj/item/reagent_containers/syringe/on_reagent_change(changetype)
update_icon()
@@ -152,29 +157,32 @@
mode = SYRINGE_DRAW
update_icon()
-
-/obj/item/reagent_containers/syringe/update_icon()
- cut_overlays()
- var/rounded_vol
- if(reagents && reagents.total_volume)
- rounded_vol = CLAMP(round((reagents.total_volume / volume * 15),5), 1, 15)
- var/image/filling_overlay = mutable_appearance('icons/obj/reagentfillings.dmi', "syringe[rounded_vol]")
- filling_overlay.color = mix_color_from_reagents(reagents.reagent_list)
- add_overlay(filling_overlay)
- else
- rounded_vol = 0
+/obj/item/reagent_containers/syringe/update_icon_state()
+ var/rounded_vol = get_rounded_vol()
icon_state = "[rounded_vol]"
item_state = "syringe_[rounded_vol]"
+
+/obj/item/reagent_containers/syringe/update_overlays()
+ . = ..()
+ if(show_filling)
+ var/rounded_vol = get_rounded_vol()
+ if(reagents && reagents.total_volume)
+ . += mutable_appearance('icons/obj/reagentfillings.dmi', "syringe[rounded_vol]", color = mix_color_from_reagents(reagents.reagent_list))
if(ismob(loc))
- var/mob/M = loc
var/injoverlay
switch(mode)
if (SYRINGE_DRAW)
injoverlay = "draw"
if (SYRINGE_INJECT)
injoverlay = "inject"
- add_overlay(injoverlay)
- M.update_inv_hands()
+ . += injoverlay
+
+///Used by update_icon() and update_overlays()
+/obj/item/reagent_containers/syringe/proc/get_rounded_vol()
+ if(reagents && reagents.total_volume)
+ return CLAMP(round((reagents.total_volume / volume * 15),5), 1, 15)
+ else
+ return 0
/obj/item/reagent_containers/syringe/epinephrine
name = "syringe (epinephrine)"
@@ -267,6 +275,7 @@
amount_per_transfer_from_this = 20
icon_state = "empty"
item_state = "syringe_empty"
+ show_filling = FALSE
var/emptrig = FALSE
/obj/item/reagent_containers/syringe/dart/afterattack(atom/target, mob/user , proximity)
@@ -317,28 +326,13 @@
/obj/item/reagent_containers/syringe/dart/attack_self(mob/user)
return
-/obj/item/reagent_containers/syringe/dart/update_icon()
- cut_overlays()
- var/rounded_vol
-
- rounded_vol = "empty"
- if(reagents && reagents.total_volume)
- if(volume/round(reagents.total_volume, 1) == 1)
- rounded_vol="full"
- mode = SYRINGE_INJECT
-
- icon_state = "[rounded_vol]"
- item_state = "syringe_[rounded_vol]"
- if(ismob(loc))
- var/mob/M = loc
- var/injoverlay
- switch(mode)
- if (SYRINGE_DRAW)
- injoverlay = "draw"
- if (SYRINGE_INJECT)
- injoverlay = "ready"
- add_overlay(injoverlay)
- M.update_inv_hands()
+/obj/item/reagent_containers/syringe/dart/update_icon_state()
+ var/empty_full = "empty"
+ if(round(reagents.total_volume, 1) == reagents.maximum_volume)
+ empty_full = "full"
+ mode = SYRINGE_INJECT
+ icon_state = "[empty_full]"
+ item_state = "syringe_[empty_full]"
/obj/item/reagent_containers/syringe/dart/emp_act(severity)
emptrig = TRUE
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index 11c80b14ee..425c94cd65 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -252,7 +252,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
// update the icon depending on the position
-/obj/machinery/conveyor_switch/update_icon()
+/obj/machinery/conveyor_switch/update_icon_state()
if(position<0)
if(invert_icon)
icon_state = "switch-fwd"
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 78e18a01e2..65bfa1d98f 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -191,10 +191,6 @@
AM.pipe_eject(0)
update_icon()
-// update the icon & overlays to reflect mode & status
-/obj/machinery/disposal/update_icon()
- return
-
/obj/machinery/disposal/proc/flush()
flushing = TRUE
flushAnimation()
@@ -386,8 +382,8 @@
pressure_charging = TRUE
update_icon()
-/obj/machinery/disposal/bin/update_icon()
- cut_overlays()
+/obj/machinery/disposal/bin/update_overlays()
+ . = ..()
if(stat & BROKEN)
pressure_charging = FALSE
flush = FALSE
@@ -395,7 +391,7 @@
//flush handle
if(flush)
- add_overlay("dispover-handle")
+ . += "dispover-handle"
//only handle is shown if no power
if(stat & NOPOWER || panel_open)
@@ -403,13 +399,13 @@
//check for items in disposal - occupied light
if(contents.len > 0)
- add_overlay("dispover-full")
+ . += "dispover-full"
//charging and ready light
if(pressure_charging)
- add_overlay("dispover-charge")
+ . += "dispover-charge"
else if(full_pressure)
- add_overlay("dispover-ready")
+ . += "dispover-ready"
/obj/machinery/disposal/bin/proc/do_flush()
set waitfor = FALSE
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index c0d48301e1..6b3ba6fe28 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -162,6 +162,16 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/datum/design/genescanner
+ name = "Genetic Sequence Analyzer"
+ desc = "A handy hand-held analyzers for quickly determining mutations and collecting the full sequence."
+ id = "genescanner"
+ build_path = /obj/item/sequence_scanner
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron = 1000, /datum/material/glass = 500)
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+
/datum/design/healthanalyzer_advanced
name = "Advanced Health Analyzer"
desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy."
diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm
index 10566b7de2..42b8a4dcbb 100644
--- a/code/modules/research/destructive_analyzer.dm
+++ b/code/modules/research/destructive_analyzer.dm
@@ -51,7 +51,7 @@ Note: Must be placed within 3 tiles of the R&D Console
update_icon()
reset_busy()
-/obj/machinery/rnd/destructive_analyzer/update_icon()
+/obj/machinery/rnd/destructive_analyzer/update_icon_state()
if(loaded_item)
icon_state = "d_analyzer_l"
else
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index b43abf97b2..3d4bf2b4c9 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -514,7 +514,7 @@
update_icon()
recentlyExperimented = FALSE
-/obj/machinery/rnd/experimentor/update_icon()
+/obj/machinery/rnd/experimentor/update_icon_state()
icon_state = "h_lathe"
/obj/machinery/rnd/experimentor/proc/warn_admins(user, ReactionName)
diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm
index 0731d3067c..439d0c5750 100644
--- a/code/modules/research/nanites/nanite_cloud_controller.dm
+++ b/code/modules/research/nanites/nanite_cloud_controller.dm
@@ -48,7 +48,7 @@
return
var/datum/nanite_cloud_backup/backup = new(src)
- var/datum/component/nanites/cloud_copy = new(backup)
+ var/datum/component/nanites/cloud_copy = backup.AddComponent(/datum/component/nanites)
backup.cloud_id = cloud_id
backup.nanites = cloud_copy
investigate_log("[key_name(user)] created a new nanite cloud backup with id #[cloud_id]", INVESTIGATE_NANITES)
@@ -213,7 +213,7 @@
var/datum/component/nanites/nanites = backup.nanites
var/datum/nanite_program/P = nanites.programs[text2num(params["program_id"])]
var/datum/nanite_rule/rule = rule_template.make_rule(P)
-
+
investigate_log("[key_name(usr)] added rule [rule.display()] to program [P.name] in cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
if("remove_rule")
@@ -224,7 +224,7 @@
var/datum/nanite_program/P = nanites.programs[text2num(params["program_id"])]
var/datum/nanite_rule/rule = P.rules[text2num(params["rule_id"])]
rule.remove()
-
+
investigate_log("[key_name(usr)] removed rule [rule.display()] from program [P.name] in cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
diff --git a/code/modules/research/nanites/nanite_programs/suppression.dm b/code/modules/research/nanites/nanite_programs/suppression.dm
index 1c882cead3..3b0d6d0d06 100644
--- a/code/modules/research/nanites/nanite_programs/suppression.dm
+++ b/code/modules/research/nanites/nanite_programs/suppression.dm
@@ -40,7 +40,7 @@
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/shocking/on_trigger(comm_message)
- host_mob.electrocute_act(rand(5,10), "shock nanites", TRUE, TRUE)
+ host_mob.electrocute_act(rand(5,10), "shock nanites", 1, SHOCK_NOGLOVES)
/datum/nanite_program/stun
name = "Neural Shock"
@@ -107,7 +107,7 @@
/datum/nanite_program/fake_death/enable_passive_effect()
. = ..()
host_mob.emote("deathgasp")
- host_mob.fakedeath("nanites")
+ host_mob.fakedeath("nanites", TRUE)
/datum/nanite_program/fake_death/disable_passive_effect()
. = ..()
diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm
index 9a55e53659..388fa3785c 100644
--- a/code/modules/research/techweb/_techweb.dm
+++ b/code/modules/research/techweb/_techweb.dm
@@ -20,6 +20,7 @@
var/largest_bomb_value = 0
var/organization = "Third-Party" //Organization name, used for display.
var/list/last_bitcoins = list() //Current per-second production, used for display only.
+ var/list/discovered_mutations = list() //Mutations discovered by genetics, this way they are shared and cant be destroyed by destroying a single console
var/list/tiers = list() //Assoc list, id = number, 1 is available, 2 is all reqs are 1, so on
/datum/techweb/New()
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 5c7b6b57d1..b0d95a87d7 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -71,7 +71,7 @@
display_name = "Biological Technology"
description = "What makes us tick." //the MC, silly!
prereq_ids = list("base")
- design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibrillator", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag", "bloodbankgen", "telescopiciv", "medspray")
+ design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibrillator", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag", "bloodbankgen", "telescopiciv", "medspray","genescanner")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/adv_biotech
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 9919a6bf26..cd08b7290a 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -2,6 +2,7 @@
#define ENGINES_START_TIME 100
#define ENGINES_STARTED (SSshuttle.emergency.mode == SHUTTLE_IGNITING)
#define IS_DOCKED (SSshuttle.emergency.mode == SHUTTLE_DOCKED || (ENGINES_STARTED))
+#define MAX_AUTH_INPUTS 6
#define NOT_BEGUN 0
#define STAGE_1 1
@@ -18,6 +19,9 @@
resistance_flags = INDESTRUCTIBLE
var/auth_need = 3
var/list/authorized = list()
+ var/auth_cooldown //these two vars are used to quell spam.
+ var/auth_combo = 0
+
var/hijack_last_stage_increase = 0
var/hijack_stage_time = 50
var/hijack_stage_cooldown = 50
@@ -92,6 +96,13 @@
to_chat(user, "The access level of your card is not high enough.")
return
+ if(auth_cooldown <= world.time)
+ auth_combo = 0
+
+ else if(auth_combo >= MAX_AUTH_INPUTS)
+ to_chat(user, "Authorizations controller lockdown engaged, please wait [CEILING(auth_cooldown - world.time, 1)] before trying again.")
+ return
+
var/old_len = authorized.len
switch(action)
@@ -116,6 +127,10 @@
minor_announce("[remaining] authorizations needed until shuttle is launched early", null, alert)
if(repeal)
minor_announce("Early launch authorization revoked, [remaining] authorizations needed")
+ auth_cooldown = world.time + 15 SECONDS
+ if(++auth_combo == MAX_AUTH_INPUTS) //C-c-combo breaker!
+ say("Authorization controller abuse detected, lockdown engaged.")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
/obj/machinery/computer/emergency_shuttle/proc/authorize(mob/user, source)
var/obj/item/card/id/ID = user.get_idcard(TRUE)
@@ -519,8 +534,9 @@
density = FALSE
clockwork = TRUE //it'd look weird
-/obj/machinery/computer/shuttle/pod/update_icon()
- return
+/obj/machinery/computer/shuttle/pod/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
/obj/machinery/computer/shuttle/pod/emag_act(mob/user)
. = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
@@ -628,7 +644,7 @@
#undef ENGINES_START_TIME
#undef ENGINES_STARTED
#undef IS_DOCKED
-
+#undef MAX_AUTH_INPUTS
#undef NOT_BEGUN
#undef STAGE_1
#undef STAGE_2
diff --git a/code/modules/shuttle/manipulator.dm b/code/modules/shuttle/manipulator.dm
index dcfb0793b5..3ba2198b35 100644
--- a/code/modules/shuttle/manipulator.dm
+++ b/code/modules/shuttle/manipulator.dm
@@ -31,14 +31,14 @@
SSshuttle.manipulator = null
. = ..()
-/obj/machinery/shuttle_manipulator/update_icon()
- cut_overlays()
+/obj/machinery/shuttle_manipulator/update_overlays()
+ . = ..()
var/mutable_appearance/hologram_projection = mutable_appearance(icon, "hologram_on")
hologram_projection.pixel_y = 22
var/mutable_appearance/hologram_ship = mutable_appearance(icon, "hologram_whiteship")
hologram_ship.pixel_y = 27
- add_overlay(hologram_projection)
- add_overlay(hologram_ship)
+ . += hologram_projection
+ . += hologram_ship
/obj/machinery/shuttle_manipulator/can_interact(mob/user)
// Only admins can use this, but they can use it from anywhere
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 44a70f539d..2c466564ff 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -660,7 +660,7 @@
if(timeleft > 1 HOURS)
return "--:--"
else if(timeleft > 0)
- return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, " ")]"
+ return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, "0")]"
else
return "00:00"
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index f05adb9309..033187e353 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -21,7 +21,7 @@
desc = "Oh no, not again."
update_icon()
-/obj/machinery/power/emitter/energycannon/magical/update_icon()
+/obj/machinery/power/emitter/energycannon/magical/update_icon_state()
if(active)
icon_state = icon_state_on
else
diff --git a/code/modules/spells/spell_types/conjure.dm b/code/modules/spells/spell_types/conjure.dm
index 8336c06edb..7ff4f30e33 100644
--- a/code/modules/spells/spell_types/conjure.dm
+++ b/code/modules/spells/spell_types/conjure.dm
@@ -80,11 +80,11 @@
school = "conjuration"
charge_max = 150
cooldown_min = 10
+ var/delete_old = TRUE
/obj/effect/proc_holder/spell/targeted/conjure_item/cast(list/targets, mob/user = usr)
- if (item && !QDELETED(item))
- qdel(item)
- item = null
+ if (delete_old && item && !QDELETED(item))
+ QDEL_NULL(item)
else
for(var/mob/living/carbon/C in targets)
if(C.dropItemToGround(C.get_active_held_item()))
diff --git a/code/modules/spells/spell_types/lightning.dm b/code/modules/spells/spell_types/lightning.dm
index 56d7dc771f..f4812e7462 100644
--- a/code/modules/spells/spell_types/lightning.dm
+++ b/code/modules/spells/spell_types/lightning.dm
@@ -69,10 +69,10 @@
playsound(get_turf(current), 'sound/magic/lightningshock.ogg', 50, 1, -1)
current.visible_message("[current] absorbs the spell, remaining unharmed!", "You absorb the spell, remaining unharmed!")
if(bounces < 1)
- current.electrocute_act(bolt_energy,"Lightning Bolt",safety=1)
+ current.electrocute_act(bolt_energy,"Lightning Bolt", flags = SHOCK_NOGLOVES)
playsound(get_turf(current), 'sound/magic/lightningshock.ogg', 50, 1, -1)
else
- current.electrocute_act(bolt_energy,"Lightning Bolt",safety=1)
+ current.electrocute_act(bolt_energy,"Lightning Bolt", flags = SHOCK_NOGLOVES)
playsound(get_turf(current), 'sound/magic/lightningshock.ogg', 50, 1, -1)
var/list/possible_targets = new
for(var/mob/living/M in view_or_range(range,target,"view"))
diff --git a/code/modules/spells/spell_types/mime.dm b/code/modules/spells/spell_types/mime.dm
index d44c6a3e3d..621efdc821 100644
--- a/code/modules/spells/spell_types/mime.dm
+++ b/code/modules/spells/spell_types/mime.dm
@@ -128,6 +128,49 @@
invocation_type ="none"
..()
+/obj/effect/proc_holder/spell/targeted/touch/mimerope
+ name = "Invisible Rope"
+ desc = "Form an invisible rope to tie people or trip people with."
+ school = "mime"
+ panel = "Mime"
+ invocation_type = "emote"
+ invocation_emote_self = "You start fabricate an invisible rope."
+ charge_max = 700
+ sound = null
+ clothes_req = 0
+ range = -1
+ include_user = 1
+ action_icon_state = "mime"
+ action_background_icon_state = "bg_mime"
+ hand_path = /obj/item/melee/touch_attack/mimerope
+
+/obj/effect/proc_holder/spell/targeted/touch/mimerope/Click()
+ if(usr && usr.mind)
+ if(!usr.mind.miming)
+ to_chat(usr, "You must dedicate yourself to silence first.")
+ return
+ if (usr.get_active_held_item())
+ to_chat(usr, "Your hands must be free to create the invisible rope.")
+ return
+ invocation = "[usr.real_name] is twirling an invisible rope in [usr.p_their()] hands."
+ else
+ invocation_type ="none"
+
+/obj/effect/proc_holder/spell/targeted/touch/mimerope/cast(list/targets,mob/user = usr)
+
+ usr.put_in_hands()
+
+/obj/item/melee/touch_attack/mimerope
+ item_state = ""
+ icon_state = ""
+ name = "mime rope"
+ desc = "An invisible rope."
+
+/obj/item/restraints/handcuffs/cable/mime
+ name = "mime restraints"
+ desc = "An invisible rope."
+ item_state = ""
+ icon_state = ""
/obj/item/book/granter/spell/mimery_blockade
spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime
diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm
index 5f984fa7e3..e5b9144535 100644
--- a/code/modules/spells/spell_types/touch_attacks.dm
+++ b/code/modules/spells/spell_types/touch_attacks.dm
@@ -1,6 +1,8 @@
/obj/effect/proc_holder/spell/targeted/touch
var/hand_path = /obj/item/melee/touch_attack
var/obj/item/melee/touch_attack/attached_hand = null
+ var/drawmessage = "You channel the power of the spell to your hand."
+ var/dropmessage = "You draw the power out of your hand."
invocation_type = "none" //you scream on connecting, not summoning
include_user = 1
range = -1
@@ -21,7 +23,7 @@
/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr)
if(!QDELETED(attached_hand))
remove_hand(TRUE)
- to_chat(user, "You draw the power out of your hand.")
+ to_chat(user, "[dropmessage]")
return
for(var/mob/living/carbon/C in targets)
@@ -43,7 +45,7 @@
remove_hand(TRUE)
to_chat(user, "Your hands are full!")
return FALSE
- to_chat(user, "You channel the power of the spell to your hand.")
+ to_chat(user, "[drawmessage]")
return TRUE
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 124159e910..405bde212a 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -379,7 +379,7 @@
if(isliving(hit_atom))
var/mob/living/M = hit_atom
if(!M.anti_magic_check())
- M.electrocute_act(80, src, illusion = 1)
+ M.electrocute_act(80, src, SHOCK_ILLUSION)
qdel(src)
/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm
index b09dee04c7..cf0d79c742 100644
--- a/code/modules/station_goals/shield.dm
+++ b/code/modules/station_goals/shield.dm
@@ -118,7 +118,7 @@
anchored = FALSE
update_icon()
-/obj/machinery/satellite/update_icon()
+/obj/machinery/satellite/update_icon_state()
icon_state = active ? "sat_active" : "sat_inactive"
/obj/machinery/satellite/attackby(obj/item/I, mob/user, params)
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 8e47d6dbde..276d4893de 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -21,6 +21,7 @@
C.emote("scream")
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered)
drop_limb()
+ C.update_equipment_speed_mods() // Update in case speed affecting item unequipped by dismemberment
C.bleed(40)
@@ -102,6 +103,7 @@
for(var/obj/item/I in embedded_objects)
embedded_objects -= I
I.forceMove(src)
+ I.unembedded()
if(!C.has_embedded_objects())
C.clear_alert("embeddedobject")
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "embedded")
@@ -111,7 +113,7 @@
for(var/X in C.dna.mutations) //some mutations require having specific limbs to be kept.
var/datum/mutation/human/MT = X
if(MT.limb_req && MT.limb_req == body_zone)
- MT.force_lose(C)
+ C.dna.force_lose(MT)
for(var/X in C.internal_organs) //internal organs inside the dismembered limb are dropped.
var/obj/item/organ/O = X
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index eed8f62e10..aaca33685b 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -161,6 +161,7 @@
for(var/obj/item/I in L.embedded_objects)
L.embedded_objects -= I
I.forceMove(T)
+ I.unembedded()
clear_alert("embeddedobject")
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "embedded")
diff --git a/code/modules/surgery/emergency_cardioversion_recovery.dm b/code/modules/surgery/emergency_cardioversion_recovery.dm
index 25f499fa76..5646c43f00 100644
--- a/code/modules/surgery/emergency_cardioversion_recovery.dm
+++ b/code/modules/surgery/emergency_cardioversion_recovery.dm
@@ -35,7 +35,7 @@
"[user] screws up, causing [H] to flop around violently as they're zapped!",
"[user] screws up, causing [H] to flop around violently as they're zapped!")
H.emote("scream")
- H.electrocute_act(25, (tool), 1, FALSE, FALSE, FALSE, TRUE)
+ H.electrocute_act(25, (tool), 1, SHOCK_ILLUSION)
H.adjustFireLoss(10)
H.emote("flip")
H.Jitter(100)
@@ -50,7 +50,7 @@
var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain)
if(BR.organ_flags & ORGAN_FAILING)
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, -5)
- H.electrocute_act(0, (tool), 1, FALSE, FALSE, FALSE, TRUE)
+ H.electrocute_act(0, (tool), 1, SHOCK_ILLUSION)
//If we're using a defib, let the defib handle the revive.
if(istype(tool, /obj/item/twohanded/shockpaddles))
return
@@ -70,7 +70,7 @@
display_results(user, target, "You screw up, sending a current through their body!",
"[user] screws up, causing [H] to flop around violently as they're zapped!",
"[user] screws up, causing [H] to flop around violently as they're zapped!")
- H.electrocute_act(25, (tool), 1, FALSE, FALSE, FALSE, TRUE)
+ H.electrocute_act(25, (tool), 1, SHOCK_ILLUSION)
H.adjustFireLoss(10)
H.emote("flip")
H.adjustOrganLoss(ORGAN_SLOT_HEART, 10)
diff --git a/code/modules/surgery/organs/appendix.dm b/code/modules/surgery/organs/appendix.dm
index feace77018..9da20e4921 100644
--- a/code/modules/surgery/organs/appendix.dm
+++ b/code/modules/surgery/organs/appendix.dm
@@ -19,7 +19,7 @@
if(M)
M.adjustToxLoss(4, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person
-/obj/item/organ/appendix/update_icon()
+/obj/item/organ/appendix/update_icon_state()
if(inflamed)
icon_state = "appendixinflamed"
name = "inflamed appendix"
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index 37ee253d4b..daf3324980 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -31,7 +31,7 @@
else
CRASH("Invalid zone for [type]")
-/obj/item/organ/cyberimp/arm/update_icon()
+/obj/item/organ/cyberimp/arm/update_icon_state()
if(zone == BODY_ZONE_R_ARM)
transform = null
else // Mirroring the icon
diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm
index 8426440bbb..47a452cc76 100644
--- a/code/modules/surgery/organs/augments_chest.dm
+++ b/code/modules/surgery/organs/augments_chest.dm
@@ -178,14 +178,11 @@
on = FALSE
update_icon()
-/obj/item/organ/cyberimp/chest/thrusters/update_icon()
+/obj/item/organ/cyberimp/chest/thrusters/update_icon_state()
if(on)
icon_state = "imp_jetpack-on"
else
icon_state = "imp_jetpack"
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
/obj/item/organ/cyberimp/chest/thrusters/proc/move_react()
allow_thrust(0.01)
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index 547ca38ead..ea98ecd32b 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -22,7 +22,7 @@
var/failed = FALSE //to prevent constantly running failing code
var/operated = FALSE //whether the heart's been operated on to fix some of its damages
-/obj/item/organ/heart/update_icon()
+/obj/item/organ/heart/update_icon_state()
if(beating)
icon_state = "[icon_base]-on"
else
diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm
index 0c3a3b55a5..7d7cd2df63 100644
--- a/code/modules/surgery/remove_embedded_object.dm
+++ b/code/modules/surgery/remove_embedded_object.dm
@@ -25,6 +25,7 @@
objects++
I.forceMove(get_turf(H))
L.embedded_objects -= I
+ I.unembedded()
if(!H.has_embedded_objects())
H.clear_alert("embeddedobject")
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "embedded")
diff --git a/code/modules/uplink/uplink_items/uplink_stealth.dm b/code/modules/uplink/uplink_items/uplink_stealth.dm
index 0ad06e7418..80aba02e2e 100644
--- a/code/modules/uplink/uplink_items/uplink_stealth.dm
+++ b/code/modules/uplink/uplink_items/uplink_stealth.dm
@@ -48,7 +48,7 @@
/datum/uplink_item/stealthy_weapons/martialarts
name = "Sleeping Carp Scroll"
desc = "This scroll contains the secrets of an ancient martial arts technique. You will master unarmed combat, \
- deflecting all ranged weapon fire, but you also refuse to use dishonorable ranged weaponry."
+ gain skin as hard as steel and swat bullets from the air, but you also refuse to use dishonorable ranged weaponry."
item = /obj/item/book/granter/martial/carp
cost = 17
surplus = 0
diff --git a/code/modules/vehicles/pimpin_ride.dm b/code/modules/vehicles/pimpin_ride.dm
index c398b528d8..ef374f5db0 100644
--- a/code/modules/vehicles/pimpin_ride.dm
+++ b/code/modules/vehicles/pimpin_ride.dm
@@ -55,12 +55,12 @@
else
return ..()
-/obj/vehicle/ridden/janicart/update_icon()
- cut_overlays()
+/obj/vehicle/ridden/janicart/update_overlays()
+ . = ..()
if(mybag)
- add_overlay("cart_garbage")
+ . += "cart_garbage"
if(floorbuffer)
- add_overlay("cart_buffer")
+ . += "cart_buffer"
/obj/vehicle/ridden/janicart/attack_hand(mob/user)
. = ..()
diff --git a/config/config.txt b/config/config.txt
index 1d987070dd..306d1d9a6d 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -185,12 +185,15 @@ VOTE_DELAY 6000
VOTE_PERIOD 600
## autovote initial delay (deciseconds) before first automatic transfer vote call (default 120 minutes)
+## Set to 0 to disable the subsystem altogether.
VOTE_AUTOTRANSFER_INITIAL 72000
## autovote delay (deciseconds) before sequential automatic transfer votes are called (default 30 minutes)
VOTE_AUTOTRANSFER_INTERVAL 18000
-## autovote maximum votes until automatic transfer call (default 4)
+## autovote maximum votes until automatic transfer call. (default 4)
+## Set to 0 to force automatic crew transfer after the 'vote_autotransfer_initial' elapsed.
+## Set to -1 to disable the maximum votes cap.
VOTE_AUTOTRANSFER_MAXIMUM 4
## prevents dead players from voting or starting votes
diff --git a/html/blank.png b/html/blank.png
new file mode 100644
index 0000000000..86c9630485
Binary files /dev/null and b/html/blank.png differ
diff --git a/html/browser/scannernew.css b/html/browser/scannernew.css
index 0e809a6760..6746a61a0d 100644
--- a/html/browser/scannernew.css
+++ b/html/browser/scannernew.css
@@ -16,3 +16,24 @@
float: left;
}
+img.selected
+{
+ border: 1px solid blue;
+}
+img.unselected
+{
+ border: 2px solid black;
+}
+div>table {
+ float: left;
+}
+td
+{
+ text-align: center;
+}
+a.clean
+{
+ background: none;
+ border: none;
+ marging: none;
+}
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-10327.yml b/html/changelogs/AutoChangeLog-pr-10327.yml
new file mode 100644
index 0000000000..3e43dbe1fd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10327.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "tg genetics"
diff --git a/html/changelogs/AutoChangeLog-pr-10702.yml b/html/changelogs/AutoChangeLog-pr-10702.yml
new file mode 100644
index 0000000000..b29bd95333
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10702.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - tweak: "Added a few cooldowns to chill nuclear bomb and communications console security level change spam, as well as the emergency shuttle's authorization announcements."
diff --git a/html/changelogs/AutoChangeLog-pr-11307.yml b/html/changelogs/AutoChangeLog-pr-11307.yml
new file mode 100644
index 0000000000..5b70dd7b0c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11307.yml
@@ -0,0 +1,5 @@
+author: "kappa-sama"
+delete-after: True
+changes:
+ - balance: "removed (mostly aesthetic) requirement to become hulk"
+ - bugfix: "changed add to remove on crit"
diff --git a/html/changelogs/AutoChangeLog-pr-11349.yml b/html/changelogs/AutoChangeLog-pr-11349.yml
new file mode 100644
index 0000000000..77f33b58c5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11349.yml
@@ -0,0 +1,4 @@
+author: "monster860"
+delete-after: True
+changes:
+ - rscadd: "Use Ctrl-Shift-direction key to shift your characters position. Use for ERP."
diff --git a/html/changelogs/AutoChangeLog-pr-11371.yml b/html/changelogs/AutoChangeLog-pr-11371.yml
new file mode 100644
index 0000000000..16c2df1819
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11371.yml
@@ -0,0 +1,6 @@
+author: "Naksu"
+delete-after: True
+changes:
+ - rscadd: "Time based free rerolls"
+ - refactor: "Refactored Blobs"
+ - balance: "Blob rerolls now give the blob 4 different options to choose from, rather than forcing a single random one."
diff --git a/html/changelogs/AutoChangeLog-pr-11373.yml b/html/changelogs/AutoChangeLog-pr-11373.yml
new file mode 100644
index 0000000000..f6b8401fce
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11373.yml
@@ -0,0 +1,5 @@
+author: "kappa-sama"
+delete-after: True
+changes:
+ - rscadd: "a super combat shotgun that loads and fires 2 shells at a time"
+ - balance: "replaces bubblegum's blood contract drop with the super shotgun"
diff --git a/html/changelogs/AutoChangeLog-pr-11382.yml b/html/changelogs/AutoChangeLog-pr-11382.yml
new file mode 100644
index 0000000000..5fe88159ba
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11382.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - rscadd: "tauric contractor space suits"
diff --git a/html/changelogs/AutoChangeLog-pr-11385.yml b/html/changelogs/AutoChangeLog-pr-11385.yml
new file mode 100644
index 0000000000..f9f5adb929
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11385.yml
@@ -0,0 +1,4 @@
+author: "bunny232"
+delete-after: True
+changes:
+ - bugfix: "moved a scrubber and vent down 5 pixels"
diff --git a/html/changelogs/AutoChangeLog-pr-11388.yml b/html/changelogs/AutoChangeLog-pr-11388.yml
new file mode 100644
index 0000000000..a6f13c843c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11388.yml
@@ -0,0 +1,9 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - rscadd: "Butter Bear"
+ - rscadd: "Crab Burger, Bisque, Crab Rangoon, French Onion Soup, Empowered Burger, Chicken Nugget box."
+ - tweak: "+++ Spider Eggs to Exotic Meat crate. --- Bacon from Exotic Meat crate."
+ - tweak: "Tweaked Crab Recipes"
+ - imageadd: "Butter Bear aka Terrygold"
+ - balance: "Food Crafting is now 5 deciseconds instead of 30."
diff --git a/html/changelogs/AutoChangeLog-pr-11390.yml b/html/changelogs/AutoChangeLog-pr-11390.yml
new file mode 100644
index 0000000000..5b737fef75
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11390.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - tweak: "ghost hud and nv defaults on"
diff --git a/html/changelogs/AutoChangeLog-pr-11392.yml b/html/changelogs/AutoChangeLog-pr-11392.yml
new file mode 100644
index 0000000000..ea1d754983
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11392.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - bugfix: "syndicate elite hardsuit helmet doesnt hide masks anymore"
diff --git a/html/changelogs/AutoChangeLog-pr-11393.yml b/html/changelogs/AutoChangeLog-pr-11393.yml
new file mode 100644
index 0000000000..c4cef6922d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11393.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Vampire mesmerize doesn't permanently disable combat mode."
diff --git a/html/changelogs/AutoChangeLog-pr-11395.yml b/html/changelogs/AutoChangeLog-pr-11395.yml
new file mode 100644
index 0000000000..df56407f2d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11395.yml
@@ -0,0 +1,4 @@
+author: "Timberpoes"
+delete-after: True
+changes:
+ - bugfix: "Shuttle countdowns once again read like \"01:05\" instead of \"01: 5\"."
diff --git a/html/changelogs/AutoChangeLog-pr-11397.yml b/html/changelogs/AutoChangeLog-pr-11397.yml
new file mode 100644
index 0000000000..ef7b84b4ee
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11397.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - balance: "syndicate contractor helmets are no longer secretly lead"
diff --git a/html/changelogs/AutoChangeLog-pr-11398.yml b/html/changelogs/AutoChangeLog-pr-11398.yml
new file mode 100644
index 0000000000..a9c54e468a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11398.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "hands free actions no longer check mobility and only consciousness."
diff --git a/html/changelogs/AutoChangeLog-pr-11399.yml b/html/changelogs/AutoChangeLog-pr-11399.yml
new file mode 100644
index 0000000000..7ad4dc0afa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11399.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "a very specific fix with tails and wagging"
diff --git a/html/changelogs/AutoChangeLog-pr-11401.yml b/html/changelogs/AutoChangeLog-pr-11401.yml
new file mode 100644
index 0000000000..6fbc3f771f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11401.yml
@@ -0,0 +1,4 @@
+author: "InnocentFire made the sprites all thanks to them!"
+delete-after: True
+changes:
+ - imageadd: "All bows now have inhand sprites once again"
diff --git a/html/changelogs/AutoChangeLog-pr-11403.yml b/html/changelogs/AutoChangeLog-pr-11403.yml
new file mode 100644
index 0000000000..2f6835b46f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11403.yml
@@ -0,0 +1,4 @@
+author: "Skoglol"
+delete-after: True
+changes:
+ - bugfix: "Fixed gibber exploit."
diff --git a/html/changelogs/AutoChangeLog-pr-11406.yml b/html/changelogs/AutoChangeLog-pr-11406.yml
new file mode 100644
index 0000000000..b46e346378
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11406.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "Minimaps, accessible via a button on OOC."
diff --git a/html/changelogs/AutoChangeLog-pr-11408.yml b/html/changelogs/AutoChangeLog-pr-11408.yml
new file mode 100644
index 0000000000..d6cf71f643
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11408.yml
@@ -0,0 +1,5 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - rscadd: "Ports New Sleeping Carp"
+ - refactor: "Ports the martial arts refactors, which includes things like moving martial into the martial subfolder and renaming it _martial, and cleans up human_defense rising bass/sleeping carp exclusive code."
diff --git a/html/changelogs/AutoChangeLog-pr-11409.yml b/html/changelogs/AutoChangeLog-pr-11409.yml
new file mode 100644
index 0000000000..6da52ae2fa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11409.yml
@@ -0,0 +1,5 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - balance: "Particle Defender is now a much more sane weapon."
+ - balance: "Tasers are no longer ignoring stimulants."
diff --git a/html/changelogs/AutoChangeLog-pr-11410.yml b/html/changelogs/AutoChangeLog-pr-11410.yml
new file mode 100644
index 0000000000..14a78979f4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11410.yml
@@ -0,0 +1,4 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - bugfix: "Blood beam can't create harvesters out of mech pilots."
diff --git a/html/changelogs/AutoChangeLog-pr-11411.yml b/html/changelogs/AutoChangeLog-pr-11411.yml
new file mode 100644
index 0000000000..1bca49bb3b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11411.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "The flying speed slowdown while hurt now actually affects flying mobs and not floating ones."
diff --git a/html/changelogs/AutoChangeLog-pr-11414.yml b/html/changelogs/AutoChangeLog-pr-11414.yml
new file mode 100644
index 0000000000..e323cf115e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11414.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Joining in as a positronic brain won't break the spawner menu anymore."
diff --git a/html/changelogs/AutoChangeLog-pr-11416.yml b/html/changelogs/AutoChangeLog-pr-11416.yml
new file mode 100644
index 0000000000..3a76a9b2f5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11416.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "Soidifaction of and uranium can be done as well as making new bluespace shards"
diff --git a/html/changelogs/AutoChangeLog-pr-11417.yml b/html/changelogs/AutoChangeLog-pr-11417.yml
new file mode 100644
index 0000000000..fd80d12e96
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11417.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed dynamic voting."
diff --git a/html/changelogs/AutoChangeLog-pr-11421.yml b/html/changelogs/AutoChangeLog-pr-11421.yml
new file mode 100644
index 0000000000..5449bb9c9a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11421.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "unnecessary blindness post-unconsciouss has been fixed with a hack that's almost as garbage as the code that caused it in the first place."
diff --git a/html/changelogs/AutoChangeLog-pr-11422.yml b/html/changelogs/AutoChangeLog-pr-11422.yml
new file mode 100644
index 0000000000..085d767bcd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11422.yml
@@ -0,0 +1,5 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - balance: "Xeno and Fountain Hall will no longer spawn more then once"
+ - tweak: "Makes the game want to spawn in more then one tumor maybe"
diff --git a/html/changelogs/AutoChangeLog-pr-11424.yml b/html/changelogs/AutoChangeLog-pr-11424.yml
new file mode 100644
index 0000000000..2ab88177a4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11424.yml
@@ -0,0 +1,4 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - code_imp: "Some minor fixes to fakedeath and a tg fix port."
diff --git a/html/changelogs/AutoChangeLog-pr-11425.yml b/html/changelogs/AutoChangeLog-pr-11425.yml
new file mode 100644
index 0000000000..82cf14d567
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11425.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "synthflesh and rezadone now take total amounts of both applied and existing reagents rather than only existing and only applied respectively."
diff --git a/html/changelogs/AutoChangeLog-pr-11429.yml b/html/changelogs/AutoChangeLog-pr-11429.yml
new file mode 100644
index 0000000000..3562f3b130
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11429.yml
@@ -0,0 +1,12 @@
+author: "Putnam"
+delete-after: True
+changes:
+ - tweak: "Swarmers, portal storm, wormholes are now controlled by dynamic."
+ - tweak: "Dynamic-controlled events can now have a minimum start time."
+ - balance: "Threatening meteors are more common (though still have pretty strict requirements)"
+ - balance: "Different storytellers now balance around different expected players-per-antag; default was 5, now intrigue/story/random have 7 and calm has 10."
+ - rscadd: "Clown ops is now available as a roundstart antag in dynamic."
+ - balance: "Sentient disease and revenant are now in the event pool rather than the antag pool (with the logic that they're both completely useless and unfun to play if people are actually playing against them)."
+ - rscadd: "A new formulation of extended was added to the storytellers; no antags, but still spending threat on events."
+ - bugfix: "Fixed a runtime in dynamic due to my misunderstanding pickweightAllowZero"
+ - rscdel: "Made conversion storyteller 0-weight-by-default."
diff --git a/html/changelogs/AutoChangeLog-pr-11430.yml b/html/changelogs/AutoChangeLog-pr-11430.yml
new file mode 100644
index 0000000000..9afe29f3ca
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11430.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - tweak: "Autotransfer vote now requires actual transfer votes to transfer."
diff --git a/html/changelogs/AutoChangeLog-pr-11431.yml b/html/changelogs/AutoChangeLog-pr-11431.yml
new file mode 100644
index 0000000000..f80f8c8585
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11431.yml
@@ -0,0 +1,4 @@
+author: "Useroth"
+delete-after: True
+changes:
+ - bugfix: "contractor tablets spawned with invalid icon_state"
diff --git a/html/changelogs/AutoChangeLog-pr-11432.yml b/html/changelogs/AutoChangeLog-pr-11432.yml
new file mode 100644
index 0000000000..f0c0572ca3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11432.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "reverts bubblegum bloodling swarming."
diff --git a/html/changelogs/AutoChangeLog-pr-11436.yml b/html/changelogs/AutoChangeLog-pr-11436.yml
new file mode 100644
index 0000000000..26ddaa7f69
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11436.yml
@@ -0,0 +1,4 @@
+author: "MrJWhit"
+delete-after: True
+changes:
+ - tweak: "Removed egun in every head locker, replaces RPD with air pump in science, fixes a computer in sec, moves hand teleporter, and removes decals under lockers."
diff --git a/html/changelogs/AutoChangeLog-pr-11437.yml b/html/changelogs/AutoChangeLog-pr-11437.yml
new file mode 100644
index 0000000000..4bb865e6f9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11437.yml
@@ -0,0 +1,8 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - balance: "blobs now receive a 50% cost refund on attacks that don't spread"
+ - balance: "reflector blobs are considerably tougher"
+ - bugfix: "fixed an integrity"
+ - bugfix: "attempting to turn a damaged strong blob into a reflector blob now refunds points"
+ - bugfix: "also fixes blob node camera jump (from another PR)"
diff --git a/html/changelogs/AutoChangeLog-pr-11447.yml b/html/changelogs/AutoChangeLog-pr-11447.yml
new file mode 100644
index 0000000000..4ada92210c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11447.yml
@@ -0,0 +1,4 @@
+author: "spookydonut"
+delete-after: True
+changes:
+ - code_imp: "adds selective duplicate component mode"
diff --git a/html/changelogs/AutoChangeLog-pr-11448.yml b/html/changelogs/AutoChangeLog-pr-11448.yml
new file mode 100644
index 0000000000..de94aa1e0d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11448.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "The autotransfer subsystem is slightly more modulable now."
diff --git a/html/changelogs/AutoChangeLog-pr-11450.yml b/html/changelogs/AutoChangeLog-pr-11450.yml
new file mode 100644
index 0000000000..748c1f94c2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11450.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - rscadd: "Space Fashion has discovered a new way to wear bandannas. With some simple minor adjustments and ties, bandannas can be made into fashionable neckerchiefs!"
diff --git a/html/changelogs/AutoChangeLog-pr-11451.yml b/html/changelogs/AutoChangeLog-pr-11451.yml
new file mode 100644
index 0000000000..bc3acb86ea
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11451.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "More dynamic fixes"
diff --git a/html/changelogs/AutoChangeLog-pr-11452.yml b/html/changelogs/AutoChangeLog-pr-11452.yml
new file mode 100644
index 0000000000..8ec140d5b0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11452.yml
@@ -0,0 +1,4 @@
+author: "Linzolle"
+delete-after: True
+changes:
+ - tweak: "cmo hypokit now holds the same amount of items as normal kits"
diff --git a/html/changelogs/AutoChangeLog-pr-11453.yml b/html/changelogs/AutoChangeLog-pr-11453.yml
new file mode 100644
index 0000000000..0113ea3dd6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11453.yml
@@ -0,0 +1,4 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - bugfix: "Bleeding out all your blood from getting love tapped by a toolbox."
diff --git a/html/dna_discovered.gif b/html/dna_discovered.gif
new file mode 100644
index 0000000000..bc6b75f2f9
Binary files /dev/null and b/html/dna_discovered.gif differ
diff --git a/html/dna_extra.gif b/html/dna_extra.gif
new file mode 100644
index 0000000000..c92218a661
Binary files /dev/null and b/html/dna_extra.gif differ
diff --git a/html/dna_undiscovered.gif b/html/dna_undiscovered.gif
new file mode 100644
index 0000000000..a3c182a4a1
Binary files /dev/null and b/html/dna_undiscovered.gif differ
diff --git a/icons/effects/genetics.dmi b/icons/effects/genetics.dmi
index a9f5f433d5..373a9de623 100644
Binary files a/icons/effects/genetics.dmi and b/icons/effects/genetics.dmi differ
diff --git a/icons/mob/actions/actions_genetic.dmi b/icons/mob/actions/actions_genetic.dmi
new file mode 100644
index 0000000000..1b1bf8dc23
Binary files /dev/null and b/icons/mob/actions/actions_genetic.dmi differ
diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi
index 072bfc8fe3..72e72ad6f3 100644
Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index d3ffdb62e4..988ae1a30f 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/icons/mob/head_muzzled.dmi b/icons/mob/head_muzzled.dmi
index 4b8263469f..f1fb09651f 100644
Binary files a/icons/mob/head_muzzled.dmi and b/icons/mob/head_muzzled.dmi differ
diff --git a/icons/mob/inhands/weapons/guns_lefthand.dmi b/icons/mob/inhands/weapons/guns_lefthand.dmi
index a166610826..2fdb9b88d2 100644
Binary files a/icons/mob/inhands/weapons/guns_lefthand.dmi and b/icons/mob/inhands/weapons/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/guns_righthand.dmi b/icons/mob/inhands/weapons/guns_righthand.dmi
index 47ed1adfee..dc8d8a4c3f 100644
Binary files a/icons/mob/inhands/weapons/guns_righthand.dmi and b/icons/mob/inhands/weapons/guns_righthand.dmi differ
diff --git a/icons/mob/mutant_bodyparts.dmi b/icons/mob/mutant_bodyparts.dmi
index 7886ca39fa..2f7f9b5be9 100644
Binary files a/icons/mob/mutant_bodyparts.dmi and b/icons/mob/mutant_bodyparts.dmi differ
diff --git a/icons/mob/suit_digi.dmi b/icons/mob/suit_digi.dmi
index 8bea32eca4..0d34d379a8 100644
Binary files a/icons/mob/suit_digi.dmi and b/icons/mob/suit_digi.dmi differ
diff --git a/icons/mob/taur_canine.dmi b/icons/mob/taur_canine.dmi
index d266d1f135..26e4d488cd 100644
Binary files a/icons/mob/taur_canine.dmi and b/icons/mob/taur_canine.dmi differ
diff --git a/icons/mob/taur_naga.dmi b/icons/mob/taur_naga.dmi
index 479fa5c83a..a4c3644003 100644
Binary files a/icons/mob/taur_naga.dmi and b/icons/mob/taur_naga.dmi differ
diff --git a/icons/obj/chromosomes.dmi b/icons/obj/chromosomes.dmi
new file mode 100644
index 0000000000..a8ff6186d8
Binary files /dev/null and b/icons/obj/chromosomes.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 28fc29cd71..c58fb5d940 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi
index 2639d6860a..641715fd87 100644
Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ
diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi
index 670269da00..9518640416 100644
Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ
diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi
index f859e8b1e7..79045ac92b 100644
Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ
diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi
index eb3ca4b2f7..3d5a825f27 100644
Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ
diff --git a/modular_citadel/code/game/machinery/wishgranter.dm b/modular_citadel/code/game/machinery/wishgranter.dm
index 48024a2228..7758c0d613 100644
--- a/modular_citadel/code/game/machinery/wishgranter.dm
+++ b/modular_citadel/code/game/machinery/wishgranter.dm
@@ -17,7 +17,7 @@
to_chat(user, "Your head pounds for a moment, before your vision clears. The Wish Granter, sensing the darkness in your heart, has given you limitless power, and it's all yours!")
user.dna.add_mutation(HULK)
user.dna.add_mutation(XRAY)
- user.dna.add_mutation(COLDRES)
+ user.dna.add_mutation(SPACEMUT)
user.dna.add_mutation(TK)
user.next_move_modifier *= 0.5 //half the delay between attacks!
to_chat(user, "Things around you feel slower!")
diff --git a/modular_citadel/code/modules/arousal/genitals.dm b/modular_citadel/code/modules/arousal/genitals.dm
index d5191b0532..7cf8fb8ff9 100644
--- a/modular_citadel/code/modules/arousal/genitals.dm
+++ b/modular_citadel/code/modules/arousal/genitals.dm
@@ -312,7 +312,7 @@
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size][dna.species.use_skintones ? "_s" : ""]_[aroused_state]_[layertext]"
- if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
+ if(layers_num[layer] == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
genital_overlay.layer = -GENITALS_EXPOSED_LAYER
LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
else
diff --git a/modular_citadel/code/modules/client/preferences.dm b/modular_citadel/code/modules/client/preferences.dm
index 8b0ba2dd43..f1dbe31d02 100644
--- a/modular_citadel/code/modules/client/preferences.dm
+++ b/modular_citadel/code/modules/client/preferences.dm
@@ -49,6 +49,5 @@
/datum/preferences/copy_to(mob/living/carbon/human/character, icon_updates = 1)
..()
character.give_genitals(TRUE)
- character.flavor_text = features["flavor_text"] //Let's update their flavor_text at least initially
if(icon_updates)
character.update_genitals()
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/human.dm b/modular_citadel/code/modules/mob/living/carbon/human/human.dm
index ee88fcb277..390cac3646 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/human.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/human.dm
@@ -19,6 +19,7 @@
L.embedded_objects -= I
L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class*painmul)//It hurts to rip it out, get surgery you dingus. And if you're ripping it out quickly via resist, it's gonna hurt even more
I.forceMove(get_turf(src))
+ I.unembedded()
user.put_in_hands(I)
user.emote("scream")
user.visible_message("[user] rips [I] out of [user.p_their()] [L.name]!","You remove [I] from your [L.name].")
diff --git a/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm b/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm
index f7c0b343c3..0850a95c47 100644
--- a/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm
+++ b/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm
@@ -185,10 +185,13 @@
name = "electron blast"
icon_state = "stunjectile"
color = null
- nodamage = 1
+ nodamage = TRUE
knockdown = 100
- stamina = 5
+ knockdown_stamoverride = 0
+ knockdown_stam_max = 0
+ stamina = 18
stutter = 5
jitter = 20
hitsound = 'sound/weapons/taserhit.ogg'
- range = 7
+ range = 3
+ strong_tase = FALSE
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
index a7dbe8d799..dac6d97eef 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -191,7 +191,7 @@
var/datum/component/nanites/N = C.GetComponent(/datum/component/nanites)
if(prob(5))
to_chat(C, "The residual voltage from the nanites causes you to seize up!")
- C.electrocute_act(10, (get_turf(C)), 1, FALSE, FALSE, FALSE, TRUE)
+ C.electrocute_act(10, (get_turf(C)), 1, SHOCK_ILLUSION)
if(prob(10))
var/atom/T = C
T.emp_act(EMP_HEAVY)
@@ -218,7 +218,7 @@ datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume)
/datum/reagent/fermi/nanite_b_goneTox/on_mob_life(mob/living/carbon/C)//Damages the taker if their purity is low. Extended use of impure chemicals will make the original die. (thus can't be spammed unless you've very good)
if(prob(15))
to_chat(C, "The residual voltage in your system causes you to seize up!")
- C.electrocute_act(10, (get_turf(C)), 1, FALSE, FALSE, FALSE, TRUE)
+ C.electrocute_act(10, (get_turf(C)), 1, SHOCK_ILLUSION)
if(prob(50))
var/atom/T = C
T.emp_act(EMP_HEAVY)
diff --git a/modular_citadel/icons/mob/mam_tails.dmi b/modular_citadel/icons/mob/mam_tails.dmi
index c2a89eab9a..d91f15a3f7 100644
Binary files a/modular_citadel/icons/mob/mam_tails.dmi and b/modular_citadel/icons/mob/mam_tails.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 1fe4fe9ce1..21f9f98d65 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -36,7 +36,6 @@
#include "code\__DEFINES\clockcult.dm"
#include "code\__DEFINES\colors.dm"
#include "code\__DEFINES\combat.dm"
-#include "code\__DEFINES\components.dm"
#include "code\__DEFINES\configuration.dm"
#include "code\__DEFINES\construction.dm"
#include "code\__DEFINES\contracts.dm"
@@ -118,6 +117,9 @@
#include "code\__DEFINES\vv.dm"
#include "code\__DEFINES\wall_dents.dm"
#include "code\__DEFINES\wires.dm"
+#include "code\__DEFINES\dcs\flags.dm"
+#include "code\__DEFINES\dcs\helpers.dm"
+#include "code\__DEFINES\dcs\signals.dm"
#include "code\__HELPERS\_cit_helpers.dm"
#include "code\__HELPERS\_lists.dm"
#include "code\__HELPERS\_logging.dm"
@@ -127,6 +129,7 @@
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\custom_holoforms.dm"
#include "code\__HELPERS\dates.dm"
+#include "code\__HELPERS\dna.dm"
#include "code\__HELPERS\donator_groupings.dm"
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
@@ -268,6 +271,7 @@
#include "code\controllers\subsystem\mapping.dm"
#include "code\controllers\subsystem\materials.dm"
#include "code\controllers\subsystem\medals.dm"
+#include "code\controllers\subsystem\minimaps.dm"
#include "code\controllers\subsystem\minor_mapping.dm"
#include "code\controllers\subsystem\mobs.dm"
#include "code\controllers\subsystem\moods.dm"
@@ -331,7 +335,6 @@
#include "code\datums\holocall.dm"
#include "code\datums\hud.dm"
#include "code\datums\map_config.dm"
-#include "code\datums\martial.dm"
#include "code\datums\mind.dm"
#include "code\datums\mutable_appearance.dm"
#include "code\datums\mutations.dm"
@@ -498,6 +501,7 @@
#include "code\datums\elements\dusts_on_leaving_area.dm"
#include "code\datums\elements\earhealing.dm"
#include "code\datums\elements\firestacker.dm"
+#include "code\datums\elements\flavor_text.dm"
#include "code\datums\elements\ghost_role_eligibility.dm"
#include "code\datums\elements\mob_holder.dm"
#include "code\datums\elements\swimming.dm"
@@ -513,6 +517,7 @@
#include "code\datums\looping_sounds\item_sounds.dm"
#include "code\datums\looping_sounds\machinery_sounds.dm"
#include "code\datums\looping_sounds\weather.dm"
+#include "code\datums\martial\_martial.dm"
#include "code\datums\martial\boxing.dm"
#include "code\datums\martial\cqc.dm"
#include "code\datums\martial\krav_maga.dm"
@@ -531,11 +536,16 @@
#include "code\datums\mood_events\generic_positive_events.dm"
#include "code\datums\mood_events\mood_event.dm"
#include "code\datums\mood_events\needs_events.dm"
+#include "code\datums\mutations\actions.dm"
+#include "code\datums\mutations\antenna.dm"
#include "code\datums\mutations\body.dm"
#include "code\datums\mutations\chameleon.dm"
-#include "code\datums\mutations\cold_resistance.dm"
+#include "code\datums\mutations\cold.dm"
+#include "code\datums\mutations\combined.dm"
#include "code\datums\mutations\hulk.dm"
+#include "code\datums\mutations\radioactive.dm"
#include "code\datums\mutations\sight.dm"
+#include "code\datums\mutations\space_adaptation.dm"
#include "code\datums\mutations\speech.dm"
#include "code\datums\mutations\telekinesis.dm"
#include "code\datums\ruins\lavaland.dm"
@@ -878,6 +888,7 @@
#include "code\game\objects\items\cardboard_cutouts.dm"
#include "code\game\objects\items\cards_ids.dm"
#include "code\game\objects\items\charter.dm"
+#include "code\game\objects\items\chromosome.dm"
#include "code\game\objects\items\chrono_eraser.dm"
#include "code\game\objects\items\cigs_lighters.dm"
#include "code\game\objects\items\clown_items.dm"
@@ -1300,6 +1311,22 @@
#include "code\modules\antagonists\blob\blob\blobs\node.dm"
#include "code\modules\antagonists\blob\blob\blobs\resource.dm"
#include "code\modules\antagonists\blob\blob\blobs\shield.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\_blobstrain.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\_reagent.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\blazing_oil.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\cryogenic_poison.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\electromagnetic_web.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\energized_jelly.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\explosive_lattice.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\multiplex.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\networked_fibers.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\pressurized_slime.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\reactive_spines.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\regenerative_materia.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\replicating_foam.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\shifting_fragments.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\synchronous_mesh.dm"
+#include "code\modules\antagonists\blob\blob\blobstrains\zombifying_pods.dm"
#include "code\modules\antagonists\blood_contract\blood_contract.dm"
#include "code\modules\antagonists\bloodsucker\bloodsucker_flaws.dm"
#include "code\modules\antagonists\bloodsucker\bloodsucker_integration.dm"
@@ -1655,6 +1682,7 @@
#include "code\modules\client\verbs\aooc.dm"
#include "code\modules\client\verbs\etips.dm"
#include "code\modules\client\verbs\looc.dm"
+#include "code\modules\client\verbs\minimap.dm"
#include "code\modules\client\verbs\ooc.dm"
#include "code\modules\client\verbs\ping.dm"
#include "code\modules\client\verbs\suicide.dm"
@@ -2051,6 +2079,7 @@
#include "code\modules\lighting\lighting_turf.dm"
#include "code\modules\mapping\dmm_suite.dm"
#include "code\modules\mapping\map_template.dm"
+#include "code\modules\mapping\minimaps.dm"
#include "code\modules\mapping\preloader.dm"
#include "code\modules\mapping\reader.dm"
#include "code\modules\mapping\ruins.dm"
@@ -2759,7 +2788,6 @@
#include "code\modules\reagents\chemistry\machinery\reagentgrinder.dm"
#include "code\modules\reagents\chemistry\machinery\smoke_machine.dm"
#include "code\modules\reagents\chemistry\reagents\alcohol_reagents.dm"
-#include "code\modules\reagents\chemistry\reagents\blob_reagents.dm"
#include "code\modules\reagents\chemistry\reagents\drink_reagents.dm"
#include "code\modules\reagents\chemistry\reagents\drug_reagents.dm"
#include "code\modules\reagents\chemistry\reagents\food_reagents.dm"