diff --git a/.github/guides/ISSUE_MANAGER.md b/.github/guides/ISSUE_MANAGER.md index c0ef14ae0cc..9e5745d7731 100644 --- a/.github/guides/ISSUE_MANAGER.md +++ b/.github/guides/ISSUE_MANAGER.md @@ -15,10 +15,10 @@ When examining new issues you should immediately notify a maintainer if you see - **Server Lagging** [[1]](https://github.com/tgstation/tgstation/issues/60193) [[2]](https://github.com/tgstation/tgstation/issues/51927) [[3]](https://github.com/tgstation/tgstation/issues/32762) - Something that is causing a _severe_ amount of lag during the game #### Runtime Issue Reports -If an issue reports a runtime, it must have the actual runtime call stack provided by round logging or in-game debug menu (https://github.com/tgstation/tgstation/issues/70329#issuecomment-1279853883). +If an issue reports a runtime, it must have the actual runtime call stack provided by round logging or in-game debug menu (https://github.com/tgstation/tgstation/issues/70329#issuecomment-1279853883).
Example runtime call stack - + ``` [2022-10-15 16:12:38.902] runtime error: Cannot execute null.add(). - proc name: visibility (/datum/cameranet/proc/visibility) @@ -28,8 +28,8 @@ If an issue reports a runtime, it must have the actual runtime call stack provid - usr.loc: the floor (150,25,4) (/turf/open/floor/circuit) - call stack: - Camera Net (/datum/cameranet): visibility(/list (/list), null, /list (/list), 1) - - AI (/mob/living/silicon/ai): camera visibility(Inactive AI Eye (/mob/camera/ai_eye)) - - Inactive AI Eye (/mob/camera/ai_eye): setLoc(the floor (150,25,4) (/turf/open/floor/circuit), 0) + - AI (/mob/living/silicon/ai): camera visibility(Inactive AI Eye (/mob/eye/camera/ai)) + - Inactive AI Eye (/mob/eye/camera/ai): setLoc(the floor (150,25,4) (/turf/open/floor/circuit), 0) - AI (/mob/living/silicon/ai): create eye() - AI (/mob/living/silicon/ai): Initialize(0, null, TagGamerGame2 (/mob/dead/new_player)) - Atoms (/datum/controller/subsystem/atoms): InitAtom(AI (/mob/living/silicon/ai), 0, /list (/list)) diff --git a/README.md b/README.md index 7dd8520a161..29d7fc7e39f 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,6 @@ Space Station 13 is a paranoia-laden round-based roleplaying game set against th **[How to compile in VSCode and other build options](tools/build/README.md).** -## Contributors -[Guides for Contributors](.github/CONTRIBUTING.md) - ## LICENSE All code after [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 at 4:38 PM PST](https://github.com/psychonaut-station/PsychonautStation/commit/333c566b88108de218d882840e61928a9b759d8f) is licensed under [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.html). diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_hotsprings.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_hotsprings.dmm index 967d02ffbe4..cf9130ffeb5 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_hotsprings.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_hotsprings.dmm @@ -6,7 +6,7 @@ /turf/closed/mineral/random/snow, /area/icemoon/surface/outdoors/unexplored) "c" = ( -/turf/open/water/cursed_spring, +/turf/open/water/hot_spring/cursed, /area/icemoon/surface/outdoors/noteleport) "d" = ( /obj/item/paper/crumpled{ @@ -14,6 +14,14 @@ }, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors) +"l" = ( +/obj/structure/chair/plastic, +/turf/open/misc/asteroid/snow/icemoon, +/area/icemoon/surface/outdoors) +"G" = ( +/obj/item/storage/toolbox/fishing, +/turf/open/misc/asteroid/snow/icemoon, +/area/icemoon/surface/outdoors) (1,1,1) = {" b @@ -70,7 +78,7 @@ b (5,1,1) = {" b a -a +G c c c @@ -83,7 +91,7 @@ b (6,1,1) = {" b a -a +l c c c diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_syndidome.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_syndidome.dmm index e2b11a02964..228c55292fb 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_syndidome.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_syndidome.dmm @@ -90,6 +90,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) +"bp" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/structure/cable/layer1, +/obj/structure/cable/layer3, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/syndibiodome) "bu" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/table/wood, @@ -216,9 +223,15 @@ /obj/effect/turf_decal/siding/wideplating/dark{ dir = 5 }, -/obj/machinery/smartfridge/organ, -/obj/item/organ/alien/resinspinner, -/obj/item/organ/eyes/night_vision, +/obj/structure/table/reinforced/plastitaniumglass, +/obj/item/clothing/gloves/latex/coroner{ + pixel_x = -1; + pixel_y = 8 + }, +/obj/item/clothing/mask/surgical{ + pixel_x = 6; + pixel_y = 10 + }, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "cE" = ( @@ -498,9 +511,10 @@ /obj/effect/decal/cleanable/blood/gibs/down, /turf/open/floor/iron/cafeteria, /area/ruin/syndibiodome) -"gX" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/warm/directional/west, +"gZ" = ( +/obj/machinery/door/airlock/maintenance_hatch, +/obj/structure/cable/layer1, +/obj/structure/cable, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "ha" = ( @@ -509,10 +523,10 @@ /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/noteleport) "he" = ( -/mob/living/basic/gorilla/genetics, /obj/effect/turf_decal/siding/wood/corner{ dir = 1 }, +/mob/living/basic/gorilla/hostile, /turf/open/floor/wood, /area/ruin/syndibiodome) "hf" = ( @@ -527,8 +541,8 @@ /turf/open/floor/iron/dark, /area/ruin/syndibiodome) "hm" = ( -/mob/living/basic/gorilla/genetics, /obj/effect/decal/cleanable/dirt/dust, +/mob/living/basic/gorilla/hostile, /turf/open/floor/iron/dark/small, /area/ruin/syndibiodome) "hr" = ( @@ -639,14 +653,12 @@ }, /turf/open/floor/iron/dark/herringbone, /area/ruin/syndibiodome) -"iq" = ( -/obj/structure/flora/rock/pile/style_random, -/mob/living/carbon/human/species/monkey/angry, -/turf/open/floor/grass, -/area/ruin/syndibiodome) "ir" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/dim/directional/east, +/obj/structure/cable, +/obj/structure/cable/layer1, +/obj/structure/cable/layer3, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "iG" = ( @@ -723,16 +735,6 @@ }, /obj/effect/decal/cleanable/blood/drip, /obj/effect/decal/cleanable/dirt, -/obj/structure/table/reinforced/plastitaniumglass, -/obj/item/surgery_tray/full, -/obj/item/clothing/gloves/latex/coroner{ - pixel_x = -1; - pixel_y = 8 - }, -/obj/item/clothing/mask/surgical{ - pixel_x = 6; - pixel_y = 10 - }, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "jd" = ( @@ -771,9 +773,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) -"jQ" = ( -/turf/closed/indestructible/syndicate/nodiagonal, -/area/icemoon/surface/outdoors/noteleport) "jR" = ( /obj/effect/decal/cleanable/blood/trails{ dir = 4 @@ -1025,9 +1024,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) -"mK" = ( -/turf/open/misc/asteroid/snow/icemoon, -/area/ruin/syndibiodome) "mV" = ( /obj/effect/turf_decal/siding/wideplating/dark, /obj/effect/decal/cleanable/dirt/dust, @@ -1040,6 +1036,9 @@ "mW" = ( /obj/effect/spawner/random/trash, /obj/effect/decal/cleanable/dirt, +/obj/structure/cable/layer3, +/obj/structure/cable/layer1, +/obj/structure/cable, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "mZ" = ( @@ -1113,6 +1112,13 @@ /obj/effect/turf_decal/trimline/dark_red/line, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) +"on" = ( +/obj/machinery/door/airlock/maintenance_hatch, +/obj/structure/cable/layer3, +/obj/structure/cable/layer1, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/syndibiodome) "oq" = ( /obj/effect/decal/cleanable/blood/trails{ dir = 10 @@ -1147,7 +1153,7 @@ /obj/machinery/light/warm/directional/west, /obj/effect/decal/cleanable/dirt/dust, /obj/effect/decal/cleanable/dirt, -/mob/living/basic/gorilla/genetics, +/mob/living/basic/gorilla/hostile, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) "oH" = ( @@ -1346,7 +1352,6 @@ /obj/effect/turf_decal/siding/wideplating/dark{ dir = 1 }, -/mob/living/carbon/human/species/monkey/angry, /obj/machinery/light/warm/directional/north, /obj/machinery/digital_clock/directional/north, /obj/effect/decal/cleanable/dirt, @@ -1354,8 +1359,8 @@ /area/ruin/syndibiodome) "rQ" = ( /obj/effect/decal/cleanable/dirt, -/mob/living/basic/gorilla/genetics, /obj/effect/decal/cleanable/dirt/dust, +/mob/living/basic/gorilla/hostile, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) "rX" = ( @@ -1434,7 +1439,6 @@ }, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt/dust, -/obj/effect/mapping_helpers/broken_machine, /turf/open/floor/iron/dark/herringbone, /area/ruin/syndibiodome) "td" = ( @@ -1701,12 +1705,20 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) +"vH" = ( +/obj/effect/spawner/random/trash, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/structure/cable/layer1, +/obj/structure/cable/layer3, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/syndibiodome) "vJ" = ( /obj/effect/decal/cleanable/dirt, -/mob/living/basic/gorilla/genetics, /obj/effect/turf_decal/trimline/purple/corner, /obj/effect/decal/cleanable/dirt/dust, /obj/effect/decal/cleanable/dirt, +/mob/living/basic/gorilla/hostile, /turf/open/floor/iron/dark/herringbone, /area/ruin/syndibiodome) "vK" = ( @@ -1959,7 +1971,6 @@ /obj/effect/turf_decal/trimline/dark/line{ dir = 1 }, -/mob/living/carbon/human/species/monkey/angry, /obj/effect/turf_decal/siding/wideplating/dark{ dir = 1 }, @@ -2100,7 +2111,6 @@ /turf/open/floor/grass, /area/ruin/syndibiodome) "yU" = ( -/mob/living/carbon/human/species/monkey/angry, /obj/effect/turf_decal/weather/dirt{ dir = 9 }, @@ -2235,6 +2245,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) +"Az" = ( +/obj/machinery/door/airlock/maintenance_hatch, +/obj/structure/cable, +/obj/structure/cable/layer1, +/obj/structure/cable/layer3, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/syndibiodome) "AA" = ( /obj/effect/turf_decal/siding/wideplating/dark{ dir = 5 @@ -2262,9 +2279,9 @@ /obj/effect/decal/cleanable/dirt/dust, /obj/item/storage/belt/security/webbing, /obj/item/storage/toolbox/syndicate, -/obj/item/gun/ballistic/automatic/pistol, -/obj/item/ammo_box/magazine/m10mm, -/obj/item/ammo_box/magazine/m10mm, +/obj/item/gun/ballistic/automatic/pistol/contraband, +/obj/item/ammo_box/magazine/m9mm, +/obj/item/ammo_box/magazine/m9mm, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "Ba" = ( @@ -2496,7 +2513,6 @@ /turf/open/floor/iron/white/small, /area/ruin/syndibiodome) "EE" = ( -/mob/living/carbon/human/species/monkey/angry, /obj/effect/decal/cleanable/blood/trails{ dir = 1 }, @@ -2727,7 +2743,6 @@ "Hs" = ( /obj/structure/flora/bush/flowers_br/style_3, /obj/structure/flora/bush/flowers_yw/style_3, -/mob/living/carbon/human/species/monkey/angry, /obj/effect/gibspawner/human/bodypartless, /obj/effect/mob_spawn/corpse/human/syndicatecommando/lessenedgear, /turf/open/floor/grass, @@ -2810,6 +2825,14 @@ }, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) +"Iq" = ( +/obj/machinery/light/small/dim/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable/layer3, +/obj/structure/cable/layer1, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/syndibiodome) "IF" = ( /obj/effect/mob_spawn/corpse/human/syndicatecommando/lessenedgear, /obj/effect/turf_decal/siding/wideplating/dark/end{ @@ -2947,6 +2970,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark/herringbone, /area/ruin/syndibiodome) +"Kl" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable/layer3, +/obj/structure/cable/layer1, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/syndibiodome) "Kn" = ( /obj/effect/decal/cleanable/blood/footprints{ dir = 2 @@ -3087,6 +3117,9 @@ "LA" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/dim/directional/west, +/obj/structure/cable, +/obj/structure/cable/layer1, +/obj/structure/cable/layer3, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "LB" = ( @@ -3192,6 +3225,8 @@ /turf/open/floor/iron/cafeteria, /area/ruin/syndibiodome) "Mr" = ( +/obj/machinery/light/warm/directional/west, +/obj/effect/decal/cleanable/dirt, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "Mt" = ( @@ -3266,7 +3301,6 @@ /obj/effect/turf_decal/siding/wideplating/dark{ dir = 1 }, -/mob/living/carbon/human/species/monkey/angry, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/dark/line, /obj/effect/decal/cleanable/dirt/dust, @@ -3348,7 +3382,7 @@ /obj/effect/turf_decal/weather/dirt{ dir = 6 }, -/mob/living/basic/gorilla/genetics, +/mob/living/basic/gorilla/hostile, /turf/open/floor/grass, /area/ruin/syndibiodome) "NN" = ( @@ -3364,7 +3398,6 @@ /obj/effect/turf_decal/siding/wideplating/dark{ dir = 6 }, -/obj/effect/gibspawner/generic, /obj/machinery/digital_clock/directional/east, /obj/effect/decal/cleanable/dirt, /turf/open/floor/mineral/plastitanium/red, @@ -3379,11 +3412,8 @@ /turf/open/floor/iron/dark, /area/ruin/syndibiodome) "Os" = ( -/obj/structure/bodycontainer/morgue/beeper_off{ - dir = 8 - }, /obj/effect/turf_decal/trimline/tram/filled, -/turf/open/floor/pod/dark, +/turf/closed/indestructible/syndicate/nodiagonal, /area/ruin/syndibiodome) "Oy" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, @@ -3392,13 +3422,6 @@ }, /turf/open/floor/plating, /area/ruin/syndibiodome) -"OD" = ( -/mob/living/carbon/human/species/monkey/angry, -/obj/effect/turf_decal/weather/dirt{ - dir = 6 - }, -/turf/open/floor/grass, -/area/ruin/syndibiodome) "OH" = ( /obj/effect/decal/cleanable/blood/trails{ dir = 8 @@ -3428,8 +3451,8 @@ /obj/machinery/light/warm/directional/east, /obj/item/storage/belt/security/webbing, /obj/item/gun/ballistic/automatic/pistol/contraband, -/obj/item/ammo_box/magazine/m10mm, -/obj/item/ammo_box/magazine/m10mm, +/obj/item/ammo_box/magazine/m9mm, +/obj/item/ammo_box/magazine/m9mm, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "OM" = ( @@ -3446,6 +3469,9 @@ "ON" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/dim/directional/south, +/obj/structure/cable, +/obj/structure/cable/layer1, +/obj/structure/cable/layer3, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "OO" = ( @@ -3640,7 +3666,6 @@ /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "RK" = ( -/mob/living/carbon/human/species/monkey/angry, /obj/structure/chair/office/tactical{ dir = 4 }, @@ -3659,8 +3684,9 @@ /area/icemoon/surface/outdoors/noteleport) "RX" = ( /obj/effect/turf_decal/siding/wideplating/dark, -/mob/living/basic/gorilla/genetics, /obj/effect/decal/cleanable/dirt, +/obj/effect/gibspawner/generic, +/mob/living/basic/gorilla/hostile, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "Se" = ( @@ -3823,6 +3849,10 @@ dir = 1 }, /area/ruin/syndibiodome) +"To" = ( +/obj/machinery/door/airlock/maintenance_hatch, +/turf/open/floor/iron/dark, +/area/ruin/syndibiodome) "Tu" = ( /obj/structure/table/reinforced/plastitaniumglass, /obj/effect/turf_decal/siding/wideplating/dark{ @@ -3897,6 +3927,7 @@ /obj/item/stack/sheet/mineral/uranium/five, /obj/item/stack/sheet/mineral/uranium/five, /obj/effect/decal/cleanable/dirt, +/obj/machinery/light/warm/directional/east, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/syndibiodome) "TL" = ( @@ -3971,12 +4002,12 @@ /turf/open/floor/iron/dark/small, /area/ruin/syndibiodome) "Ux" = ( -/mob/living/basic/gorilla/genetics, /obj/effect/gibspawner/human/bodypartless, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/blue/corner{ dir = 4 }, +/mob/living/basic/gorilla/hostile, /turf/open/floor/iron/dark, /area/ruin/syndibiodome) "Uz" = ( @@ -4223,8 +4254,8 @@ /obj/effect/decal/cleanable/blood/tracks{ dir = 4 }, -/mob/living/basic/gorilla/genetics, /obj/effect/decal/cleanable/dirt, +/mob/living/basic/gorilla/hostile, /turf/open/floor/iron/dark/herringbone, /area/ruin/syndibiodome) "XG" = ( @@ -4362,13 +4393,13 @@ /area/ruin/syndibiodome) "YZ" = ( /obj/effect/turf_decal/siding/wideplating/dark, -/mob/living/basic/gorilla/genetics, /obj/structure/fluff/fake_vent, /obj/effect/decal/cleanable/blood/trails{ dir = 1 }, /obj/effect/decal/cleanable/dirt, /obj/effect/gibspawner/human/bodypartless, +/mob/living/basic/gorilla/hostile, /turf/open/floor/mineral/plastitanium/red, /area/ruin/syndibiodome) "Zd" = ( @@ -4690,7 +4721,7 @@ oq Mc zM zM -mK +ys ys ys tL @@ -4727,8 +4758,8 @@ ck ys ys zM -Ut -vx +Kl +on xi kw wL @@ -4820,8 +4851,8 @@ jS ys ys zM -Ut -Ut +Kl +Kl zM Sr zM @@ -4867,7 +4898,7 @@ pg pg zM zM -Ut +Kl zM zM Ab @@ -4914,7 +4945,7 @@ zd ys zM Db -Ut +Kl zM kK MB @@ -4961,7 +4992,7 @@ pg pg zM ic -Ut +Kl zM rK YZ @@ -5008,7 +5039,7 @@ uD ys zM wY -Ut +Kl zM cB ja @@ -5102,10 +5133,10 @@ AI ys ys zM -Ut -Ut -zM -zM +Kl +Kl +Iq +Ro zM zM zM @@ -5151,8 +5182,8 @@ Hi zM zM Ut -LA -Ro +Kl +Ut zM qN qU @@ -5198,10 +5229,10 @@ ys ys zM zM -Ut -Ut -Ut -vx +Kl +Kl +Kl +gZ je kt XC @@ -5582,7 +5613,7 @@ Fl qN IU vu -iq +MH YD Fp xz @@ -5664,13 +5695,13 @@ ys ck zM zM -mW -Ut +vH +bp ir -Ut -Ut -Ut -vx +bp +bp +bp +Az kw Eq CV @@ -5711,7 +5742,7 @@ ys ys zM MP -Ut +bp zM zM Ut @@ -5758,7 +5789,7 @@ ys ys zM qa -Ut +bp zM zM dS @@ -5805,7 +5836,7 @@ ys zM zM Sv -Ut +bp zM wx RH @@ -5852,7 +5883,7 @@ zM zM rZ Ut -Ut +bp zM Ra RX @@ -5899,7 +5930,7 @@ zM zM zM zM -Ut +bp zM Pw Oi @@ -5993,12 +6024,12 @@ Uc qp sR zM -Ut -Ut +bp +bp LA -mW -Ut -vx +vH +bp +Az Nt uW Vv @@ -6049,7 +6080,7 @@ qN pY XC XC -qN +To Ut zM Vj @@ -6060,7 +6091,7 @@ XE Mt WB iX -OD +cN zM ys uD @@ -6188,8 +6219,8 @@ zM zM zM zM -Vv zM +To zM LU qN @@ -6235,8 +6266,8 @@ zM ek Dc zM -zM -zM +Mr +Ut Mr Ut qN @@ -6283,7 +6314,7 @@ MM bu zM Kz -gX +Ut Ut Ut zM @@ -6428,7 +6459,7 @@ zM zM zM ys -jQ +zM zM zM zM diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm index 828e7b7f743..6150ca80727 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm @@ -62,7 +62,7 @@ /turf/open/floor/plating/lavaland_atmos, /area/ruin/unpowered) "l" = ( -/turf/open/water/lavaland_atmos, +/turf/open/water/hot_spring/lavaland_atmos, /area/lavaland/surface) "m" = ( /obj/machinery/door/airlock/survival_pod/glass, @@ -118,7 +118,7 @@ /obj/structure/chair/plastic{ dir = 8 }, -/turf/open/misc/ashplanet/wateryrock, +/turf/open/misc/ashplanet/wateryrock/lavaland_atmos, /area/lavaland/surface) "y" = ( /obj/structure/chair/comfy/shuttle{ @@ -129,7 +129,7 @@ /area/ruin/unpowered) "z" = ( /obj/item/book/manual/fish_catalog, -/turf/open/misc/ashplanet/wateryrock, +/turf/open/misc/ashplanet/wateryrock/lavaland_atmos, /area/lavaland/surface) "B" = ( /mob/living/basic/mining/goliath, @@ -140,7 +140,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface) "E" = ( -/turf/open/misc/ashplanet/wateryrock, +/turf/open/misc/ashplanet/wateryrock/lavaland_atmos, /area/lavaland/surface) "F" = ( /obj/effect/mob_spawn/corpse/human/cargo_tech, @@ -175,7 +175,7 @@ /area/ruin/powered) "L" = ( /obj/item/storage/toolbox/fishing, -/turf/open/misc/ashplanet/wateryrock, +/turf/open/misc/ashplanet/wateryrock/lavaland_atmos, /area/lavaland/surface) "M" = ( /obj/structure/rack, @@ -213,7 +213,7 @@ /area/ruin/powered) "U" = ( /obj/item/bait_can/worm/premium, -/turf/open/misc/ashplanet/wateryrock, +/turf/open/misc/ashplanet/wateryrock/lavaland_atmos, /area/lavaland/surface) "V" = ( /obj/structure/table/survival_pod, diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index 1f9fa283e57..f18d092fd15 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -1666,7 +1666,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/firealarm/directional/west, -/turf/open/floor/glass, +/turf/open/floor/iron, /area/station/command/heads_quarters/rd) "aGy" = ( /obj/structure/flora/bush/large/style_random{ @@ -2837,6 +2837,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "bfe" = ( @@ -2858,6 +2859,7 @@ /obj/structure/disposalpipe/junction/flip{ dir = 8 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "bfU" = ( @@ -3241,10 +3243,6 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/delivery/white, /turf/closed/wall, /area/station/maintenance/port/fore) "bmT" = ( @@ -4959,6 +4957,10 @@ /obj/machinery/light/cold/directional/west, /turf/open/floor/tram, /area/station/security/tram) +"bVr" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/small, +/area/station/maintenance/department/engine) "bVD" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -6737,6 +6739,7 @@ /area/space/nearstation) "cCM" = ( /obj/structure/cable, +/obj/structure/sink/kitchen/directional/east, /turf/open/floor/iron/kitchen/small, /area/station/service/kitchen) "cCP" = ( @@ -6746,7 +6749,6 @@ /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) "cCV" = ( -/obj/effect/turf_decal/tile/brown/opposingcorners, /obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -7190,10 +7192,6 @@ "cKk" = ( /turf/closed/mineral/random/stationside, /area/station/ai_monitored/turret_protected/aisat/maint) -"cKm" = ( -/obj/machinery/camera/directional/west, -/turf/open/floor/engine, -/area/station/engineering/supermatter) "cKt" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -8485,6 +8483,7 @@ /obj/structure/rack/skeletal, /obj/machinery/camera/autoname/directional/west, /obj/structure/sign/poster/official/random/directional/north, +/obj/structure/sign/poster/official/random/directional/west, /turf/open/floor/iron/grimy, /area/station/service/library/private) "dgy" = ( @@ -10712,15 +10711,12 @@ /turf/open/floor/iron/white, /area/station/medical/medbay/aft) "dZa" = ( -/obj/structure/table/reinforced, /obj/machinery/camera/directional/west, /obj/effect/decal/cleanable/cobweb, -/obj/item/retractor, -/obj/item/hemostat, -/obj/item/cautery, /obj/machinery/camera/autoname/directional/north, /obj/structure/sign/poster/official/random/directional/north, /obj/machinery/status_display/ai/directional/west, +/obj/item/surgery_tray/full/deployed, /turf/open/floor/iron/showroomfloor, /area/station/medical/surgery/theatre) "dZk" = ( @@ -13995,13 +13991,13 @@ /turf/open/floor/iron/small, /area/station/security/office) "fhp" = ( -/obj/structure/table, /obj/effect/spawner/random/food_or_drink/donkpockets{ pixel_y = 6 }, /obj/effect/turf_decal/siding{ dir = 8 }, +/obj/structure/table, /turf/open/floor/iron/dark/textured_large, /area/station/service/kitchen) "fhT" = ( @@ -14979,6 +14975,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "fyZ" = ( @@ -15861,6 +15858,7 @@ dir = 1 }, /obj/machinery/power/apc/auto_name/directional/south, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "fMg" = ( @@ -16212,7 +16210,7 @@ /area/station/maintenance/department/engine/atmos) "fRI" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 8 + dir = 4 }, /obj/machinery/portable_atmospherics/canister, /turf/open/misc/asteroid, @@ -17735,6 +17733,10 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"gul" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/small, +/area/station/maintenance/department/engine) "gun" = ( /obj/machinery/firealarm/directional/east, /obj/effect/turf_decal/tile/red{ @@ -17874,10 +17876,6 @@ pixel_y = 8 }, /obj/item/clothing/mask/surgical, -/obj/item/surgical_drapes{ - pixel_x = -1; - pixel_y = 4 - }, /obj/machinery/status_display/evac/directional/west, /turf/open/floor/iron/showroomfloor, /area/station/medical/surgery/theatre) @@ -19276,14 +19274,6 @@ /obj/machinery/light_switch/directional/east, /turf/open/floor/iron/freezer, /area/station/command/heads_quarters/captain/private) -"gRH" = ( -/obj/machinery/button/door/directional/north{ - id = "Cabin4"; - name = "Cabin Bolt Control"; - normaldoorcontrol = 1 - }, -/turf/closed/wall, -/area/station/service/abandoned_gambling_den) "gRL" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -20068,6 +20058,11 @@ name = "Pharmacy Shutters Control"; req_access = list("pharmacy") }, +/obj/item/reagent_containers/cup/bottle/multiver, +/obj/item/reagent_containers/cup/bottle/epinephrine, +/obj/item/reagent_containers/cup/bottle/formaldehyde, +/obj/item/reagent_containers/cup/bottle/acidic_buffer, +/obj/item/reagent_containers/cup/bottle/basic_buffer, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) "hdT" = ( @@ -21439,6 +21434,7 @@ /obj/effect/turf_decal/siding/wideplating/corner{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/wood, /area/station/engineering/atmos/pumproom) "hBq" = ( @@ -21826,9 +21822,9 @@ /turf/open/floor/iron/smooth, /area/station/command/gateway) "hIm" = ( -/obj/machinery/libraryscanner, /obj/machinery/camera/autoname/directional/north, /obj/machinery/airalarm/directional/north, +/obj/machinery/bookbinder, /turf/open/floor/wood/parquet, /area/station/service/library) "hIE" = ( @@ -24186,6 +24182,7 @@ pixel_y = 18 }, /obj/structure/extinguisher_cabinet/directional/east, +/obj/item/circuitboard/mecha/ripley/main, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) "ivC" = ( @@ -25595,6 +25592,16 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) +"iPx" = ( +/obj/machinery/atmospherics/components/trinary/filter/flipped/critical{ + dir = 1; + filter_type = list(/datum/gas/nitrogen) + }, +/obj/effect/turf_decal/bot{ + dir = 1 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "iPy" = ( /obj/structure/cable, /turf/open/floor/iron, @@ -25873,7 +25880,7 @@ /area/station/hallway/primary/port) "iUp" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 8 + dir = 4 }, /turf/open/floor/circuit, /area/station/tcommsat/server) @@ -26328,6 +26335,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "jab" = ( @@ -28056,6 +28064,11 @@ dir = 8 }, /area/station/commons/storage/tools) +"jEY" = ( +/obj/structure/cable, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/small, +/area/station/security/processing) "jEZ" = ( /obj/structure/hedge, /obj/effect/decal/cleanable/dirt, @@ -28169,7 +28182,9 @@ }, /area/station/cargo/storage) "jGK" = ( -/obj/structure/chair/wood, +/obj/structure/chair/wood{ + dir = 1 + }, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -29342,7 +29357,7 @@ "jYr" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/machinery/mecha_part_fabricator{ - dir = 8 + drop_direction = 8 }, /obj/effect/turf_decal/stripes/box, /turf/open/floor/iron/dark, @@ -29723,7 +29738,7 @@ /area/station/cargo/lobby) "kgu" = ( /obj/structure/lattice/catwalk, -/obj/machinery/atmospherics/components/unary/passive_vent, +/obj/machinery/atmospherics/components/unary/passive_vent/layer2, /turf/open/space/basic, /area/space/nearstation) "kgw" = ( @@ -30695,6 +30710,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ cycle_id = "engine_airlock_2" }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/catwalk_floor, /area/station/engineering/break_room) "kux" = ( @@ -30773,7 +30789,7 @@ "kvO" = ( /obj/machinery/light/small/directional/north, /obj/machinery/camera/directional/east{ - c_tag = "Prison Isolation Cell"; + c_tag = "Prison Reflection Chamber"; network = list("ss13","prison","isolation") }, /obj/structure/chair, @@ -31360,9 +31376,7 @@ /obj/effect/turf_decal/siding/wideplating/corner{ dir = 8 }, -/obj/effect/turf_decal/siding/wideplating/corner{ - dir = 6 - }, +/obj/effect/turf_decal/siding/wideplating/corner, /turf/open/floor/wood, /area/station/engineering/atmos/office) "kHL" = ( @@ -35514,7 +35528,7 @@ "mae" = ( /obj/structure/cable, /turf/closed/wall, -/area/station/service/bar) +/area/station/maintenance/central/greater) "maf" = ( /turf/closed/wall/rust, /area/station/hallway/primary/fore) @@ -39349,10 +39363,6 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron/textured_half, /area/station/commons/fitness/recreation/entertainment) -"num" = ( -/obj/effect/turf_decal/sand/plating, -/turf/closed/wall, -/area/station/maintenance/port/lesser) "nun" = ( /obj/structure/flora/bush/flowers_br/style_random{ pixel_x = -3; @@ -40681,6 +40691,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "nSd" = ( @@ -41305,7 +41316,6 @@ /turf/open/floor/iron/smooth_large, /area/station/maintenance/department/medical/central) "ofx" = ( -/obj/effect/turf_decal/sand/plating, /turf/closed/wall/r_wall, /area/space/nearstation) "ofU" = ( @@ -43687,6 +43697,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "oYj" = ( @@ -44195,6 +44206,11 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"phk" = ( +/obj/structure/table/wood, +/obj/machinery/libraryscanner, +/turf/open/floor/carpet, +/area/station/service/library) "phm" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, @@ -46101,6 +46117,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron/small, /area/station/hallway/primary/port) "pOQ" = ( @@ -50248,7 +50265,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/item/radio/intercom/directional/west, -/turf/open/floor/glass, +/turf/open/floor/iron, /area/station/command/heads_quarters/rd) "rfP" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -50738,7 +50755,8 @@ "rqt" = ( /obj/machinery/airalarm/directional/north, /obj/effect/decal/cleanable/dirt, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/iron/dark, /area/station/service/lawoffice) "rqw" = ( @@ -51157,6 +51175,11 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron/dark/side, /area/station/hallway/primary/central/fore) +"rxG" = ( +/obj/structure/cable, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/small, +/area/station/security/processing) "rxJ" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -51216,10 +51239,10 @@ pixel_y = 2 }, /obj/item/holosign_creator/robot_seat/restaurant, -/obj/structure/table, /obj/effect/turf_decal/siding{ dir = 9 }, +/obj/structure/table, /turf/open/floor/iron/dark/textured_large, /area/station/service/kitchen) "rya" = ( @@ -52578,7 +52601,7 @@ /turf/open/floor/iron/white, /area/station/medical/medbay/lobby) "rVI" = ( -/obj/machinery/atmospherics/components/trinary/mixer/airmix/flipped{ +/obj/machinery/atmospherics/components/trinary/mixer/airmix/flipped/inverse{ dir = 8 }, /turf/open/floor/iron, @@ -53110,6 +53133,7 @@ dir = 1 }, /obj/effect/turf_decal/tile/purple/fourcorners, +/obj/machinery/airalarm/directional/north, /turf/open/floor/iron, /area/station/engineering/atmos) "sfB" = ( @@ -54454,8 +54478,8 @@ /obj/machinery/modular_computer/preset/research{ dir = 1 }, -/obj/machinery/light/small/directional/south, /obj/effect/turf_decal/siding/purple, +/obj/machinery/light/cold/directional/south, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/rd) "sDj" = ( @@ -54596,13 +54620,7 @@ /turf/open/misc/sandy_dirt, /area/station/security/tram) "sGt" = ( -/obj/structure/table/reinforced, -/obj/item/scalpel{ - pixel_y = 12 - }, -/obj/item/blood_filter, -/obj/item/circular_saw, -/obj/item/bonesetter, +/obj/structure/closet/crate/freezer/surplus_limbs, /turf/open/floor/iron/showroomfloor, /area/station/medical/surgery/theatre) "sGE" = ( @@ -55414,10 +55432,10 @@ /obj/effect/turf_decal/siding/wood{ dir = 8 }, -/obj/structure/sign/poster/official/random/directional/west, /obj/structure/destructible/cult/item_dispenser/archives/library, /obj/item/book/codex_gigas, /obj/machinery/light/small/dim/directional/west, +/obj/machinery/airalarm/directional/west, /turf/open/floor/iron/grimy, /area/station/service/library/private) "sTR" = ( @@ -56589,7 +56607,6 @@ /turf/open/floor/plating, /area/station/maintenance/central/lesser) "tnh" = ( -/obj/effect/turf_decal/tile/brown/opposingcorners, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/siding/wideplating{ @@ -59069,7 +59086,9 @@ /obj/effect/turf_decal/siding/wideplating/dark{ dir = 8 }, -/obj/structure/chair/wood, +/obj/structure/chair/wood{ + dir = 1 + }, /turf/open/floor/iron/small, /area/station/service/barber) "ucJ" = ( @@ -61581,6 +61600,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "uQT" = ( @@ -62421,15 +62441,12 @@ /turf/open/floor/iron/smooth_large, /area/station/science/robotics/mechbay) "vfI" = ( -/obj/machinery/microwave{ - pixel_y = 5 - }, /obj/machinery/light_switch/directional/north, /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/structure/table, /obj/effect/turf_decal/siding/end, +/obj/machinery/smartfridge/drying, /turf/open/floor/iron/dark/textured_large, /area/station/service/kitchen) "vfK" = ( @@ -62858,12 +62875,13 @@ /turf/open/floor/light/colour_cycle/dancefloor_b, /area/station/maintenance/starboard/central) "vlB" = ( -/obj/machinery/atmospherics/pipe/smart/simple/general/visible{ - dir = 4 - }, /obj/effect/turf_decal/sand/plating, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/directional/north, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "Gas to Filter" + }, /turf/open/floor/plating, /area/station/maintenance/hallway/abandoned_command) "vlV" = ( @@ -65157,9 +65175,15 @@ /turf/open/floor/iron/dark, /area/station/command/corporate_dock) "vTP" = ( -/obj/structure/sink/kitchen/directional/east, /obj/machinery/firealarm/directional/west, -/turf/open/floor/iron/kitchen/small, +/obj/structure/table, +/obj/machinery/microwave{ + pixel_y = 5 + }, +/obj/effect/turf_decal/siding/end{ + dir = 4 + }, +/turf/open/floor/iron/dark/textured_large, /area/station/service/kitchen) "vTV" = ( /turf/closed/wall/r_wall, @@ -67824,7 +67848,7 @@ /obj/effect/spawner/random/maintenance, /obj/structure/rack, /turf/open/floor/plating, -/area/station/service/bar) +/area/station/maintenance/central/greater) "wKO" = ( /obj/structure/disposalpipe/segment, /obj/machinery/camera/directional/east, @@ -68037,7 +68061,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/light_switch/directional/west, -/turf/open/floor/glass, +/turf/open/floor/iron, /area/station/command/heads_quarters/rd) "wNU" = ( /obj/structure/lattice/catwalk, @@ -69547,6 +69571,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) "xiT" = ( @@ -69827,7 +69852,7 @@ "xnC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/glass, +/turf/open/floor/iron, /area/station/command/heads_quarters/rd) "xnE" = ( /turf/closed/wall/r_wall, @@ -84429,7 +84454,7 @@ eHN uhq tXl vAD -num +fEC aJq aJq aJq @@ -87635,7 +87660,7 @@ wCc dDB wZs dDB -bSo +dDB aJq aJq aJq @@ -87685,7 +87710,7 @@ blb dDB dDB dDB -tYT +gcs aJq dDB slY @@ -88456,7 +88481,7 @@ blb dDB dDB tYT -blb +gcs dDB dDB dDB @@ -90211,7 +90236,7 @@ jNV guh cBl fJe -aJb +iPx cay fMB maK @@ -90988,7 +91013,7 @@ pMM tIR hCT kAz -cKm +tIR szg tpW pUM @@ -91868,11 +91893,11 @@ wpO huE wpO wpO -iYY +rxG xKv blb xKv -iYY +jEY wpO wpO huE @@ -93812,7 +93837,7 @@ dDB dDB dDB vxt -nST +bVr evM aOa rtX @@ -94069,9 +94094,9 @@ dDB dDB dDB vxt +bVr nST -nST -nST +gul vxt yfa yfa @@ -94583,9 +94608,9 @@ dDB dDB dDB vxt -gcs -gcs -gcs +vxt +vxt +vxt vxt vxt vxt @@ -94840,10 +94865,10 @@ dDB dDB dDB dDB -bSo -bSo -bSo -bSo +dDB +dDB +aJq +aJq aJq vxt hVq @@ -94921,7 +94946,7 @@ sXD qSa feR aZh -jdx +phk pOQ wKr wKr @@ -97998,8 +98023,8 @@ xRV jVM xjQ jVM -tGq -tGq +jVM +jVM xmt xmt xmt @@ -100311,7 +100336,7 @@ jVM jVM jVM jVM -vkh +jVM lnD fzw bKO @@ -100755,15 +100780,15 @@ aJq aJq aJq aJq -gcs -blb -blb -blb -blb -blb -blb -blb -blb +aJq +dDB +dDB +dDB +dDB +dDB +dDB +dDB +aJq aJq aJq yjV @@ -101012,15 +101037,15 @@ aJq aJq aJq aJq -tYT -tYT -dDB -dDB -dDB -dDB -dDB -dDB -aJq +gcs +blb +blb +blb +blb +blb +blb +blb +gcs aJq aJq aJq @@ -101339,7 +101364,7 @@ jgb hRc jVM kXC -vkh +jVM dxV jDT fOq @@ -101596,7 +101621,7 @@ vGe xno jVM kXC -vkh +jVM vkh kWF wtw @@ -102110,7 +102135,7 @@ xHD xHD jVM jBu -vkh +jVM vkh vkh vkh @@ -108872,7 +108897,7 @@ fnz ufn yhH tuT -dDB +blb dDB dDB dDB @@ -109129,10 +109154,10 @@ meN rDj tuT tuT -dDB -dDB -dDB -dDB +blb +blb +blb +xxo dDB dDB dDB @@ -109373,11 +109398,11 @@ vtC vtC kxL vtC -blb dDB dDB dDB -blb +dDB +dDB vtC kxL vtC @@ -109385,8 +109410,8 @@ vtC vtC vtC tuT -dDB -dDB +blb +blb dDB dDB dDB @@ -109624,25 +109649,25 @@ idp tuT tuT tuT -dDB -dDB +blb +blb blb vtC cWZ vtC blb -dDB -dDB -dDB +blb +blb +blb blb vtC cWZ vtC -blb dDB dDB dDB dDB +blb dDB dDB dDB @@ -109880,22 +109905,12 @@ aNX idp vtC blb -blb -blb -blb -blb dDB dDB dDB -blb -blb -blb -blb -blb dDB dDB dDB -blb dDB dDB dDB @@ -109909,6 +109924,16 @@ dDB dDB dDB dDB +blb +dDB +dDB +dDB +dDB +dDB +dDB +dDB +dDB +dDB dDB dDB dDB @@ -110140,19 +110165,12 @@ blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB -blb -blb -blb -blb -blb dDB dDB dDB @@ -110162,6 +110180,13 @@ dDB dDB dDB dDB +blb +blb +dDB +dDB +dDB +dDB +dDB dDB dDB dDB @@ -110397,19 +110422,15 @@ blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB @@ -110417,6 +110438,10 @@ dDB dDB dDB dDB +blb +dDB +dDB +dDB dDB dDB dDB @@ -110654,20 +110679,12 @@ blb dDB dDB dDB -blb -blb -blb -blb -blb dDB dDB dDB -blb dDB dDB dDB -blb -esv dDB dDB dDB @@ -110675,6 +110692,14 @@ dDB dDB dDB dDB +esv +dDB +dDB +blb +dDB +dDB +dDB +dDB dDB dDB dDB @@ -110911,19 +110936,15 @@ blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB @@ -110931,6 +110952,10 @@ dDB dDB dDB dDB +blb +dDB +dDB +dDB dDB dDB dDB @@ -111168,19 +111193,12 @@ blb dDB dDB dDB -blb dDB dDB dDB -blb dDB dDB dDB -blb -blb -blb -blb -blb dDB dDB dDB @@ -111190,6 +111208,13 @@ dDB dDB dDB dDB +blb +blb +dDB +dDB +dDB +dDB +dDB dDB dDB dDB @@ -111422,22 +111447,12 @@ nBF idp vtC blb -blb -blb -blb -blb dDB dDB dDB -blb -blb -blb -blb -blb dDB dDB dDB -blb dDB dDB dDB @@ -111451,6 +111466,16 @@ dDB dDB dDB dDB +blb +dDB +dDB +dDB +dDB +dDB +dDB +dDB +dDB +dDB dDB dDB dDB @@ -111680,25 +111705,25 @@ idp tuT tuT tuT -dDB -dDB +blb +blb blb vtC ijI vtC blb -dDB -dDB -dDB +blb +blb +blb blb vtC ijI vtC -blb dDB dDB dDB dDB +blb dDB dDB dDB @@ -111943,11 +111968,11 @@ vtC vtC kxL vtC -blb dDB dDB dDB -blb +dDB +dDB vtC kxL vtC @@ -111955,8 +111980,8 @@ vtC vtC vtC tuT -dDB -dDB +blb +blb dDB dDB dDB @@ -112213,10 +112238,10 @@ vJN fhZ tuT tuT -dDB -dDB -dDB -dDB +blb +blb +blb +xxo dDB dDB dDB @@ -112470,7 +112495,7 @@ vbR ufn yhH tuT -dDB +blb dDB dDB dDB @@ -114942,7 +114967,7 @@ aJq aJq aJq wOp -gRH +wOp wOp wOp nWk diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index bbd805282a0..14e91a6a171 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -1273,6 +1273,7 @@ /obj/effect/spawner/random/maintenance, /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/department/security) "apC" = ( @@ -21053,7 +21054,6 @@ "fiL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, /obj/structure/chair/comfy/black{ dir = 8 }, @@ -44023,6 +44023,7 @@ /obj/item/folder/red, /obj/item/lighter, /obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/cable, /turf/open/floor/wood, /area/station/service/electronic_marketing_den) "kWE" = ( @@ -44889,7 +44890,8 @@ /area/station/hallway/primary/fore) "lhp" = ( /obj/structure/sign/poster/official/report_crimes/directional/south, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood, /area/station/service/lawoffice) "lhC" = ( @@ -49572,6 +49574,7 @@ "msx" = ( /obj/item/kirbyplants/random, /obj/structure/sign/poster/contraband/random/directional/south, +/obj/structure/cable, /turf/open/floor/wood, /area/station/service/electronic_marketing_den) "msB" = ( @@ -55831,6 +55834,7 @@ pixel_y = 3 }, /obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, /turf/open/floor/plating, /area/station/security/detectives_office/private_investigators_office) "nXY" = ( @@ -57721,7 +57725,6 @@ /turf/open/floor/iron/white, /area/station/science/research) "oxV" = ( -/obj/structure/cable, /obj/effect/mapping_helpers/broken_floor, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 @@ -58618,7 +58621,6 @@ /turf/open/floor/iron, /area/station/security/prison) "oLz" = ( -/obj/structure/cable, /obj/structure/chair/office{ dir = 4 }, @@ -59132,7 +59134,6 @@ /turf/open/floor/iron, /area/station/hallway/secondary/construction) "oRD" = ( -/obj/structure/cable, /obj/item/circuitboard/computer/secure_data, /obj/structure/frame/computer{ anchored = 1; @@ -59893,7 +59894,7 @@ /area/station/service/abandoned_gambling_den/gaming) "pcJ" = ( /obj/machinery/mecha_part_fabricator{ - dir = 4 + drop_direction = 4 }, /obj/effect/turf_decal/bot_red, /obj/effect/turf_decal/tile/neutral/full, @@ -63253,7 +63254,6 @@ "pQT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, /obj/structure/chair/comfy/brown{ dir = 8 }, @@ -72633,6 +72633,7 @@ dir = 8 }, /obj/machinery/light/small/dim/directional/north, +/obj/machinery/power/apc/auto_name/directional/north, /turf/open/floor/iron/checker, /area/station/service/hydroponics/garden/abandoned) "sgh" = ( @@ -85740,7 +85741,6 @@ /obj/item/reagent_containers/cup/watering_can, /obj/item/plant_analyzer, /obj/structure/sign/poster/contraband/kudzu/directional/south, -/obj/machinery/power/apc/auto_name/directional/south, /obj/effect/turf_decal/bot, /obj/effect/turf_decal/siding/green{ dir = 8 @@ -92441,7 +92441,6 @@ }, /area/station/science/lobby) "xeF" = ( -/obj/structure/cable, /obj/item/kirbyplants/random, /obj/machinery/light/small/dim/directional/north, /turf/open/floor/wood, diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 5a4a74216c4..498bba9c2e8 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -841,6 +841,32 @@ "aom" = ( /obj/machinery/pdapainter/engineering, /obj/effect/turf_decal/tile/neutral/full, +/obj/machinery/button/door/directional/west{ + id = "Engineering"; + name = "Engineering Lockdown Control"; + pixel_y = -8; + req_access = list("engineering") + }, +/obj/machinery/button/door/directional/west{ + id = "engstorage"; + name = "Engineering Secure Storage Control"; + pixel_x = -36; + pixel_y = 4; + req_access = list("engine_equip") + }, +/obj/machinery/button/door/directional/west{ + id = "atmos"; + name = "Atmospherics Lockdown Control"; + pixel_y = 4; + req_access = list("atmospherics") + }, +/obj/machinery/button/door/directional/west{ + id = "ceprivacy"; + name = "Privacy Shutter Control"; + pixel_y = -8; + req_access = list("engineering"); + pixel_x = -36 + }, /turf/open/floor/iron/dark/smooth_large, /area/station/command/heads_quarters/ce) "aon" = ( @@ -4309,7 +4335,6 @@ "bjK" = ( /obj/machinery/light/directional/north, /obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/keycard_auth/wall_mounted/directional/north, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/ce) "bjL" = ( @@ -7944,7 +7969,8 @@ /turf/open/floor/iron/dark, /area/station/service/hydroponics/garden) "cgB" = ( -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood, /area/station/service/lawoffice) "cgC" = ( @@ -60263,8 +60289,8 @@ /area/station/engineering/atmos/storage) "rqF" = ( /obj/machinery/door/poddoor{ - id = "Secure Storage"; - name = "Secure Storage" + id = "engstorage"; + name = "Engineering Secure Storage Lockdown" }, /turf/open/floor/plating, /area/station/engineering/engine_smes) @@ -62768,6 +62794,14 @@ /obj/machinery/firealarm/directional/west, /turf/open/floor/iron, /area/station/science/ordnance) +"sdp" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/door/poddoor/preopen{ + id = "atmos"; + name = "Atmospherics Blast Door" + }, +/turf/open/floor/plating, +/area/station/engineering/atmos) "sdr" = ( /obj/structure/transit_tube/horizontal, /obj/effect/turf_decal/weather/snow/corner{ @@ -65787,7 +65821,7 @@ dir = 1 }, /obj/effect/turf_decal/tile/blue, -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat/atmos) "sUE" = ( @@ -245994,7 +246028,7 @@ keP cRO guU ayq -hpI +sdp bID iKh rOU diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index d056026b5ff..1bc583699ef 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -358,16 +358,6 @@ /obj/machinery/light/directional/east, /turf/open/floor/iron/white/corner, /area/station/hallway/secondary/entry) -"ahg" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/machinery/duct, -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 10 - }, -/obj/structure/cable, -/obj/structure/sign/eyechart/directional/west, -/turf/open/floor/iron/white, -/area/station/medical/cryo) "ahj" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -3477,16 +3467,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, /area/station/science/research) -"bjK" = ( -/obj/structure/tank_holder/extinguisher, -/obj/machinery/camera/directional/east{ - c_tag = "Medbay Cryogenics"; - network = list("ss13","medbay") - }, -/obj/effect/turf_decal/tile/blue/fourcorners, -/obj/machinery/airalarm/directional/north, -/turf/open/floor/iron/white, -/area/station/medical/cryo) "bjP" = ( /turf/closed/wall/r_wall, /area/station/ai_monitored/turret_protected/aisat/foyer) @@ -6639,6 +6619,16 @@ /obj/effect/mapping_helpers/airlock/access/all/service/lawyer, /turf/open/floor/wood, /area/station/service/lawoffice) +"ctg" = ( +/obj/item/radio/intercom/directional/east, +/obj/effect/turf_decal/siding/white{ + dir = 5 + }, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/water/no_planet_atmos, +/area/station/service/hydroponics/garden) "ctn" = ( /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron, @@ -11337,7 +11327,7 @@ }, /area/station/engineering/atmos) "ecD" = ( -/obj/item/bot_assembly/floorbot{ +/obj/item/bot_assembly/repairbot{ created_name = "FloorDiffBot"; desc = "Why won't it work?"; name = "FloorDiffBot" @@ -15267,15 +15257,6 @@ /obj/structure/sign/warning/pods, /turf/closed/wall, /area/station/commons/locker) -"ftQ" = ( -/obj/effect/turf_decal/siding/white{ - dir = 4 - }, -/obj/structure/railing{ - dir = 4 - }, -/turf/open/water, -/area/station/service/hydroponics/garden) "fuc" = ( /obj/structure/frame/machine{ anchored = 1 @@ -16048,6 +16029,12 @@ /obj/effect/mapping_helpers/airlock/access/all/medical/general, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"fLo" = ( +/obj/effect/turf_decal/siding/white{ + dir = 8 + }, +/turf/open/water/no_planet_atmos, +/area/station/service/hydroponics/garden) "fLq" = ( /obj/machinery/door/window/left/directional/north{ name = "Inner Pipe Access"; @@ -20230,7 +20217,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/disposalpipe/segment, -/obj/item/radio/intercom/directional/east, /obj/effect/turf_decal/trimline/brown/filled/line, /turf/open/floor/iron, /area/station/cargo/miningoffice) @@ -23814,6 +23800,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/security/courtroom) +"izs" = ( +/obj/structure/cable, +/obj/structure/sink/directional/east, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/obj/effect/turf_decal/tile/blue/fourcorners, +/obj/structure/mirror/directional/west, +/turf/open/floor/iron/white, +/area/station/medical/cryo) "izv" = ( /obj/item/flashlight/lantern{ pixel_y = 7 @@ -25350,17 +25344,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/medbay/central) -"iZS" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/portable_atmospherics/canister/anesthetic_mix, -/obj/machinery/light_switch/directional/south, -/turf/open/floor/iron/dark, -/area/station/medical/cryo) "jaj" = ( /obj/structure/chair, /obj/structure/cable, @@ -26613,34 +26596,6 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/dark, /area/station/science/ordnance) -"jwp" = ( -/obj/structure/table/glass, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/structure/cable, -/obj/item/reagent_containers/cup/beaker/cryoxadone{ - pixel_x = -6; - pixel_y = 10 - }, -/obj/item/reagent_containers/cup/beaker/cryoxadone{ - pixel_x = 6; - pixel_y = 10 - }, -/obj/item/reagent_containers/cup/beaker/cryoxadone{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/reagent_containers/cup/beaker/cryoxadone{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/storage/pill_bottle/mannitol, -/obj/item/reagent_containers/dropper{ - pixel_y = 6 - }, -/obj/effect/turf_decal/tile/blue/fourcorners, -/obj/item/radio/intercom/directional/west, -/turf/open/floor/iron/white, -/area/station/medical/cryo) "jws" = ( /obj/structure/table, /obj/item/storage/toolbox/mechanical{ @@ -26930,6 +26885,15 @@ /obj/machinery/holopad, /turf/open/floor/iron, /area/station/science/robotics/lab) +"jAE" = ( +/obj/effect/turf_decal/siding/white{ + dir = 4 + }, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/water/no_planet_atmos, +/area/station/service/hydroponics/garden) "jAN" = ( /obj/machinery/airalarm/directional/north, /obj/effect/turf_decal/tile/red/half/contrasted, @@ -28342,12 +28306,6 @@ /obj/machinery/status_display/evac/directional/east, /turf/open/floor/iron/dark, /area/station/command/gateway) -"jYu" = ( -/obj/effect/turf_decal/siding/white{ - dir = 9 - }, -/turf/open/water, -/area/station/service/hydroponics/garden) "jYv" = ( /obj/machinery/meter, /obj/machinery/atmospherics/pipe/smart/simple/green/visible, @@ -30517,6 +30475,16 @@ /obj/effect/turf_decal/tile/purple, /turf/open/floor/iron, /area/station/hallway/primary/central) +"kMo" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/obj/machinery/duct, +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 10 + }, +/obj/structure/cable, +/obj/structure/sign/eyechart/directional/west, +/turf/open/floor/iron/white, +/area/station/medical/cryo) "kMr" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/effect/turf_decal/siding/wood{ @@ -34084,7 +34052,6 @@ /turf/open/floor/iron/dark, /area/station/science/ordnance) "mhM" = ( -/obj/structure/disposalpipe/segment, /obj/structure/table/reinforced, /obj/machinery/door/firedoor, /obj/structure/desk_bell{ @@ -34545,6 +34512,34 @@ }, /turf/open/floor/iron/white, /area/station/science/research) +"mpV" = ( +/obj/structure/table/glass, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, +/obj/item/reagent_containers/cup/beaker/cryoxadone{ + pixel_x = -6; + pixel_y = 10 + }, +/obj/item/reagent_containers/cup/beaker/cryoxadone{ + pixel_x = 6; + pixel_y = 10 + }, +/obj/item/reagent_containers/cup/beaker/cryoxadone{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/reagent_containers/cup/beaker/cryoxadone{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/storage/pill_bottle/mannitol, +/obj/item/reagent_containers/dropper{ + pixel_y = 6 + }, +/obj/effect/turf_decal/tile/blue/fourcorners, +/obj/item/radio/intercom/directional/west, +/turf/open/floor/iron/white, +/area/station/medical/cryo) "mqe" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -38569,6 +38564,12 @@ /obj/structure/easel, /turf/open/floor/plating, /area/station/maintenance/disposal) +"nIy" = ( +/obj/effect/turf_decal/siding/white{ + dir = 9 + }, +/turf/open/water/no_planet_atmos, +/area/station/service/hydroponics/garden) "nIP" = ( /obj/structure/table/glass, /obj/item/paper_bin{ @@ -40506,16 +40507,6 @@ }, /turf/open/floor/iron, /area/station/science/lab) -"otG" = ( -/obj/item/radio/intercom/directional/east, -/obj/effect/turf_decal/siding/white{ - dir = 5 - }, -/obj/structure/railing{ - dir = 4 - }, -/turf/open/water, -/area/station/service/hydroponics/garden) "otI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -43172,7 +43163,7 @@ /area/station/ai_monitored/aisat/exterior) "pqI" = ( /obj/structure/lattice/catwalk, -/obj/item/fish_feed, +/obj/item/reagent_containers/cup/fish_feed, /turf/open/space/basic, /area/space/nearstation) "prc" = ( @@ -43424,7 +43415,7 @@ dir = 4 }, /obj/machinery/mecha_part_fabricator{ - dir = 4 + drop_direction = 4 }, /obj/structure/noticeboard/directional/west, /turf/open/floor/iron, @@ -43475,6 +43466,15 @@ /obj/machinery/telecomms/processor/preset_four, /turf/open/floor/circuit/telecomms/mainframe, /area/station/tcommsat/server) +"pwp" = ( +/obj/effect/turf_decal/siding/white{ + dir = 6 + }, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/water/no_planet_atmos, +/area/station/service/hydroponics/garden) "pwx" = ( /obj/structure/chair{ dir = 4 @@ -45636,7 +45636,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) "qiY" = ( @@ -46581,14 +46581,6 @@ }, /turf/open/floor/iron, /area/station/maintenance/port/aft) -"qCL" = ( -/obj/structure/cable, -/obj/structure/sink/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/effect/turf_decal/tile/blue/fourcorners, -/obj/structure/mirror/directional/west, -/turf/open/floor/iron/white, -/area/station/medical/cryo) "qCM" = ( /obj/structure/cable, /obj/machinery/door/airlock/external{ @@ -47649,6 +47641,7 @@ /obj/structure/railing{ dir = 8 }, +/obj/item/radio/intercom/directional/south, /turf/open/floor/iron, /area/station/cargo/miningoffice) "qTz" = ( @@ -50123,6 +50116,16 @@ /obj/machinery/firealarm/directional/west, /turf/open/floor/plating, /area/station/command/teleporter) +"rKN" = ( +/obj/effect/turf_decal/siding/white{ + dir = 10 + }, +/obj/structure/railing{ + dir = 8 + }, +/obj/structure/sign/clock/directional/south, +/turf/open/water/no_planet_atmos, +/area/station/service/hydroponics/garden) "rKQ" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -51126,7 +51129,7 @@ dir = 4 }, /obj/machinery/mecha_part_fabricator{ - dir = 4 + drop_direction = 4 }, /turf/open/floor/iron, /area/station/science/robotics/lab) @@ -56895,6 +56898,16 @@ /obj/machinery/newscaster/directional/south, /turf/open/floor/iron/white, /area/station/medical/chemistry) +"ubP" = ( +/obj/structure/tank_holder/extinguisher, +/obj/machinery/camera/directional/east{ + c_tag = "Medbay Cryogenics"; + network = list("ss13","medbay") + }, +/obj/effect/turf_decal/tile/blue/fourcorners, +/obj/machinery/airalarm/directional/north, +/turf/open/floor/iron/white, +/area/station/medical/cryo) "ubQ" = ( /obj/effect/turf_decal/tile/neutral{ dir = 8 @@ -58040,9 +58053,6 @@ /turf/open/floor/iron/white, /area/station/command/heads_quarters/cmo) "uyP" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/structure/table, /obj/machinery/light/directional/east, /obj/item/radio/intercom/directional/east, @@ -59687,7 +59697,8 @@ name = "Lawyer Requests Console" }, /obj/machinery/newscaster/directional/west, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood, /area/station/service/lawoffice) "uZP" = ( @@ -60404,12 +60415,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/engineering/atmospherics_engine) -"vmU" = ( -/obj/effect/turf_decal/siding/white{ - dir = 8 - }, -/turf/open/water, -/area/station/service/hydroponics/garden) "vmX" = ( /obj/machinery/light/directional/west, /obj/structure/disposalpipe/segment, @@ -61078,6 +61083,17 @@ /obj/effect/turf_decal/tile/purple/opposingcorners, /turf/open/floor/iron, /area/station/science/research) +"vxW" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/portable_atmospherics/canister/anesthetic_mix, +/obj/machinery/light_switch/directional/south, +/turf/open/floor/iron/dark, +/area/station/medical/cryo) "vye" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, @@ -65399,6 +65415,9 @@ /obj/effect/mapping_helpers/airlock/access/any/service/maintenance, /obj/effect/mapping_helpers/airlock/access/any/supply/maintenance, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 1 + }, /turf/open/floor/plating, /area/station/maintenance/disposal) "wXF" = ( @@ -66765,15 +66784,6 @@ "xww" = ( /turf/closed/wall/r_wall, /area/station/engineering/storage/tech) -"xwB" = ( -/obj/effect/turf_decal/siding/white{ - dir = 6 - }, -/obj/structure/railing{ - dir = 4 - }, -/turf/open/water, -/area/station/service/hydroponics/garden) "xwD" = ( /obj/effect/turf_decal/trimline/yellow/filled/corner, /turf/open/floor/iron/white, @@ -66920,16 +66930,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/chemistry) -"xyI" = ( -/obj/effect/turf_decal/siding/white{ - dir = 10 - }, -/obj/structure/railing{ - dir = 8 - }, -/obj/structure/sign/clock/directional/south, -/turf/open/water, -/area/station/service/hydroponics/garden) "xyM" = ( /obj/structure/cable, /turf/open/floor/plating, @@ -89498,10 +89498,10 @@ pOa shl cIW pOa -jwp -qCL +mpV +izs uIM -ahg +kMo grm sSp wvP @@ -90526,11 +90526,11 @@ clp etn ayH pOa -bjK +ubP jqQ mbV hEA -iZS +vxW sSp jFB bHE @@ -106420,9 +106420,9 @@ wRF jzC oCO xUE -jYu -vmU -xyI +nIy +fLo +rKN qXB kbo qXB @@ -106677,9 +106677,9 @@ lbH mie pRM ivb -otG -ftQ -xwB +ctg +jAE +pwp qXB psZ qXB diff --git a/_maps/map_files/NebulaStation/NebulaStation.dmm b/_maps/map_files/NebulaStation/NebulaStation.dmm index 31077a0c5ca..f4e7bbd43cc 100644 --- a/_maps/map_files/NebulaStation/NebulaStation.dmm +++ b/_maps/map_files/NebulaStation/NebulaStation.dmm @@ -770,6 +770,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/railing/corner/end/flip, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "afN" = ( @@ -11757,8 +11758,6 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/trimline/dark/warning, /obj/effect/turf_decal/trimline/dark/warning{ dir = 1 }, @@ -11769,6 +11768,9 @@ /obj/machinery/power/apc/auto_name/directional/west, /obj/structure/cable, /obj/machinery/camera/autoname/directional/west, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, /turf/open/floor/iron, /area/station/hallway/secondary/entry) "bMP" = ( @@ -12152,6 +12154,19 @@ }, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"bPJ" = ( +/obj/structure/table/glass, +/obj/item/reagent_containers/cup/glass/mug/britcup{ + pixel_x = -7; + pixel_y = 9 + }, +/obj/item/reagent_containers/cup/glass/coffee{ + pixel_y = 5; + pixel_x = 7 + }, +/obj/machinery/digital_clock/directional/south, +/turf/open/floor/iron/dark/small, +/area/station/hallway/secondary/entry) "bPN" = ( /obj/effect/turf_decal/siding/dark{ dir = 8 @@ -13469,9 +13484,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/central) -"bZE" = ( -/turf/open/floor/glass/reinforced, -/area/space/nearstation) "bZJ" = ( /obj/effect/turf_decal/siding/wideplating_new/dark{ dir = 8 @@ -14494,6 +14506,7 @@ name = "Auxillary Base Shutters"; dir = 8 }, +/obj/effect/turf_decal/box, /turf/open/floor/iron/dark/textured_half{ dir = 1 }, @@ -24499,10 +24512,6 @@ /area/station/service/lawoffice) "dFs" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/machinery/door/poddoor/preopen{ - id = "qmprivacy"; - name = "Privacy Shutters" - }, /obj/machinery/door/poddoor/preopen{ id = "hosprivacy"; name = "HoS Privacy Blast Door" @@ -27286,13 +27295,6 @@ /turf/open/floor/engine/hull/reinforced, /area/space/nearstation) "eaQ" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/structure/sign/warning/secure_area/directional/west, -/obj/effect/turf_decal/trimline/dark/corner{ - dir = 1 - }, /obj/effect/turf_decal/siding/thinplating_new/corner{ dir = 1 }, @@ -30879,7 +30881,7 @@ /obj/machinery/airalarm/directional/north, /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/east, -/obj/item/fish_feed, +/obj/item/reagent_containers/cup/fish_feed, /turf/open/floor/wood/parquet, /area/station/medical/psychology) "eCE" = ( @@ -42497,6 +42499,12 @@ }, /turf/open/floor/iron/dark, /area/station/command/gateway) +"gnG" = ( +/obj/effect/turf_decal/siding/thinplating_new{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/central) "gnK" = ( /obj/machinery/airalarm/directional/south, /obj/structure/railing{ @@ -56446,6 +56454,9 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/segment, +/obj/structure/railing/corner/end/flip{ + dir = 1 + }, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "isi" = ( @@ -60206,10 +60217,6 @@ /area/station/cargo/storage) "iUM" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/machinery/door/poddoor/preopen{ - id = "qmprivacy"; - name = "Privacy Shutters" - }, /obj/machinery/door/poddoor/preopen{ id = "hosprivacy"; name = "HoS Privacy Blast Door" @@ -63101,18 +63108,15 @@ /area/station/engineering/supermatter/room/upper) "jsw" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/railing{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/dark/warning{ - dir = 4 - }, /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 4 }, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/trimline/dark/line{ + dir = 4 + }, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "jsz" = ( @@ -68969,9 +68973,6 @@ /turf/open/floor/plating/airless, /area/station/science/ordnance/bomb) "kki" = ( -/obj/structure/railing{ - dir = 4 - }, /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 4 }, @@ -72856,6 +72857,18 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/security/brig) +"kJJ" = ( +/obj/structure/rack, +/obj/effect/decal/cleanable/oil, +/obj/effect/turf_decal/bot_white, +/obj/item/clothing/suit/hazardvest, +/obj/item/clothing/suit/hazardvest, +/obj/item/tank/internals/emergency_oxygen, +/obj/item/tank/internals/emergency_oxygen, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/turf/open/floor/iron/dark/textured_large, +/area/station/maintenance/central) "kJO" = ( /obj/effect/turf_decal/tile/red/anticorner/contrasted{ dir = 8 @@ -75533,9 +75546,6 @@ /obj/structure/railing{ dir = 8 }, -/obj/effect/turf_decal/trimline/dark/warning{ - dir = 8 - }, /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 8 }, @@ -75543,6 +75553,9 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/trimline/dark/line{ + dir = 8 + }, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "lhV" = ( @@ -78630,6 +78643,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/medical/virology) +"lFB" = ( +/obj/structure/chair/sofa/bench/solo{ + dir = 4 + }, +/turf/open/floor/iron/dark/small, +/area/station/hallway/secondary/entry) "lFM" = ( /obj/effect/turf_decal/tile/purple, /obj/structure/railing/corner/end/flip, @@ -78774,6 +78793,11 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"lGG" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark, +/obj/effect/turf_decal/trimline/dark/line, +/turf/open/floor/iron/dark/small, +/area/station/maintenance/central) "lGR" = ( /obj/effect/turf_decal/siding/dark{ dir = 9 @@ -83914,28 +83938,22 @@ /turf/open/floor/iron/dark, /area/station/engineering/atmos) "mxU" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, /obj/effect/turf_decal/stripes/corner{ dir = 1 }, /obj/effect/turf_decal/trimline/dark/corner{ dir = 1 }, -/obj/effect/turf_decal/trimline/dark/corner{ - dir = 8 - }, /obj/effect/turf_decal/siding/thinplating_new/corner{ dir = 8 }, /obj/effect/turf_decal/siding/thinplating_new/corner{ dir = 1 }, -/obj/machinery/door/firedoor/border_only{ - dir = 8 - }, /obj/structure/cable, +/obj/structure/railing/corner/end/flip{ + dir = 1 + }, /turf/open/floor/iron, /area/station/hallway/primary/central) "mxV" = ( @@ -84200,6 +84218,12 @@ }, /turf/open/floor/wood/large, /area/station/service/theater) +"mAd" = ( +/obj/effect/turf_decal/siding/thinplating_new{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/central) "mAi" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -85778,6 +85802,15 @@ /obj/machinery/light_switch/directional/south, /turf/open/floor/iron/dark, /area/station/engineering/lobby) +"mMv" = ( +/obj/structure/table/glass, +/obj/item/newspaper{ + pixel_x = 3; + pixel_y = 4 + }, +/obj/machinery/digital_clock/directional/north, +/turf/open/floor/iron/dark/small, +/area/station/hallway/secondary/entry) "mMD" = ( /obj/effect/turf_decal/trimline/blue/line{ dir = 6 @@ -93258,20 +93291,12 @@ /area/station/service/chapel/office) "nTb" = ( /obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, /obj/effect/turf_decal/trimline/dark/corner, -/obj/effect/turf_decal/trimline/dark/corner{ - dir = 4 - }, /obj/effect/turf_decal/siding/thinplating_new/corner, /obj/effect/turf_decal/siding/thinplating_new/corner{ dir = 4 }, -/obj/machinery/door/firedoor/border_only{ - dir = 4 - }, +/obj/structure/railing/corner/end/flip, /turf/open/floor/iron, /area/station/hallway/primary/central) "nTe" = ( @@ -93403,12 +93428,6 @@ "nUi" = ( /obj/machinery/light/directional/east, /obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/turf_decal/trimline/dark/warning{ - dir = 1 - }, /obj/effect/turf_decal/trimline/dark/warning, /obj/effect/turf_decal/siding/thinplating_new, /obj/effect/turf_decal/siding/thinplating_new{ @@ -93416,6 +93435,7 @@ }, /obj/structure/sign/poster/official/random/directional/east, /obj/machinery/camera/autoname/directional/east, +/obj/machinery/door/firedoor/border_only, /turf/open/floor/iron, /area/station/hallway/secondary/entry) "nUp" = ( @@ -102237,6 +102257,21 @@ }, /turf/open/floor/iron/white/herringbone, /area/station/commons/toilet/restrooms) +"pfI" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 1 + }, +/obj/effect/turf_decal/trimline/dark/warning{ + dir = 1 + }, +/obj/structure/sign/directions/arrival/directional/west{ + dir = 10 + }, +/turf/open/floor/iron/dark/small, +/area/station/maintenance/central) "pfY" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 10 @@ -108970,7 +109005,7 @@ /turf/open/floor/iron/dark, /area/station/hallway/primary/port) "qfy" = ( -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /obj/effect/turf_decal/bot_white, /obj/machinery/status_display/ai/directional/east, /turf/open/floor/iron/dark/textured_large, @@ -115706,9 +115741,6 @@ dir = 8 }, /area/station/maintenance/starboard/fore) -"rgi" = ( -/turf/open/openspace, -/area/space) "rgl" = ( /obj/machinery/atmospherics/components/binary/pump/on{ dir = 1; @@ -121591,9 +121623,6 @@ /turf/open/floor/wood/large, /area/station/service/chapel/office) "rZU" = ( -/obj/structure/railing{ - dir = 8 - }, /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 8 }, @@ -121606,6 +121635,9 @@ /obj/structure/disposalpipe/segment, /obj/machinery/power/apc/auto_name/directional/west, /obj/machinery/camera/autoname/directional/west, +/obj/structure/railing/corner{ + dir = 8 + }, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "rZV" = ( @@ -122560,7 +122592,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/aux_base, +/obj/effect/mapping_helpers/airlock/access/all/supply/general, /turf/open/floor/iron/dark, /area/station/construction/mining/aux_base) "shs" = ( @@ -123053,6 +123085,9 @@ /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/siding/thinplating_new/dark, /obj/effect/turf_decal/trimline/dark/warning, +/obj/structure/sign/directions/arrival/directional/east{ + dir = 5 + }, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "sms" = ( @@ -123403,6 +123438,7 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, /turf/open/floor/iron/dark/corner, /area/station/commons/dorms) "spf" = ( @@ -129806,6 +129842,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark/textured, /area/station/commons/fitness/recreation/lasertag) +"toI" = ( +/obj/structure/chair/sofa/bench/solo{ + dir = 8 + }, +/turf/open/floor/iron/dark/small, +/area/station/hallway/secondary/entry) "toJ" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 9 @@ -146931,12 +146973,6 @@ /turf/open/floor/plating/airless, /area/station/solars/starboard/fore) "vQl" = ( -/obj/structure/railing{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/dark/warning{ - dir = 8 - }, /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 8 }, @@ -146944,6 +146980,9 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/trimline/dark/line{ + dir = 8 + }, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) "vQw" = ( @@ -146956,7 +146995,7 @@ /obj/machinery/button/door/directional/north{ id = "aux_base_shutters"; name = "Public Shutters Control"; - req_access = list("aux_base") + req_access = list("cargo") }, /obj/machinery/light/small/directional/north, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -149572,13 +149611,10 @@ /turf/open/floor/grass, /area/station/hallway/secondary/entry) "wkR" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 1 }, -/obj/effect/turf_decal/trimline/dark/warning{ +/obj/effect/turf_decal/trimline/dark/line{ dir = 1 }, /turf/open/floor/iron/dark/small, @@ -149755,6 +149791,14 @@ }, /turf/open/floor/iron/white, /area/station/science/lab) +"wmi" = ( +/obj/structure/rack, +/obj/item/storage/toolbox/emergency, +/obj/effect/decal/cleanable/oil, +/obj/item/screwdriver, +/obj/effect/turf_decal/bot_white, +/turf/open/floor/iron/dark/textured_large, +/area/station/maintenance/central) "wmj" = ( /obj/effect/turf_decal/siding/wideplating_new/dark/corner{ dir = 1 @@ -150851,10 +150895,6 @@ /turf/open/floor/iron/dark, /area/station/medical/virology) "wvW" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/structure/sign/warning/secure_area/directional/east, -/obj/structure/railing/corner, -/obj/effect/turf_decal/trimline/dark/corner, /obj/effect/turf_decal/siding/thinplating_new/corner, /turf/open/floor/iron, /area/station/hallway/primary/central) @@ -153621,10 +153661,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/security/prison) -"wRv" = ( -/obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/glass/reinforced, -/area/space/nearstation) "wRx" = ( /obj/structure/flora/bush/sparsegrass/style_random, /obj/structure/flora/grass/jungle/b/style_random, @@ -155515,7 +155551,6 @@ }, /area/station/maintenance/department/science) "xiv" = ( -/obj/structure/cable, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 }, @@ -193520,11 +193555,11 @@ pmV qFS tUf dOv -smp -fCR +lGG +wmi pmV qwd -wkR +pfI ntL lND yhH @@ -199177,7 +199212,7 @@ rLH smp fCR pmV -qwd +kJJ wkR aBc ooG @@ -258799,8 +258834,8 @@ bna ijB czt wvW -jBA -jBA +gnG +gnG nTb jBA jBA @@ -259056,8 +259091,8 @@ fub ijB eev kJl -cIJ -cIJ +mMv +toI nUi cIJ cIJ @@ -260341,11 +260376,11 @@ xaB mIv kJl cNP -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW dOr kJl tTV @@ -260597,13 +260632,13 @@ ijB wgT wgT wgT -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW wgT wgT tWk @@ -260854,13 +260889,13 @@ tGJ sMq fGO vQQ -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW sMq fGO vQQ @@ -261111,13 +261146,13 @@ nQo wgT wgT wgT -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW wgT wgT wgT @@ -261368,13 +261403,13 @@ czV lro gjO cvQ -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW cvQ sXX jRA @@ -261626,11 +261661,11 @@ hkb okW cvQ anE -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW anE cvQ nAq @@ -261883,11 +261918,11 @@ olS glP cvQ cvQ -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW cvQ cvQ glP @@ -262140,11 +262175,11 @@ rOL eiY cvQ anE -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW anE cvQ lSF @@ -262396,13 +262431,13 @@ vSF hrZ ahD cvQ -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW cvQ xvt lro @@ -262653,13 +262688,13 @@ erP wgT wgT wgT -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW wgT wgT wgT @@ -262910,13 +262945,13 @@ tGJ sMq fGO vQQ -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW sMq fGO vQQ @@ -263167,13 +263202,13 @@ ijB wgT wgT wgT -rgi -rgi -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW +txW +txW wgT wgT wgT @@ -263425,11 +263460,11 @@ xmr hFM kJl cNP -rgi -rgi -rgi -rgi -rgi +txW +txW +txW +txW +txW dOr kJl skU @@ -264713,8 +264748,8 @@ kJl cIJ cIJ bMx -cIJ -cIJ +lFB +bPJ kJl eev ijB @@ -264970,8 +265005,8 @@ psN xCp xCp mxU -xCp -xCp +mAd +mAd eaQ ijB ijB @@ -266531,11 +266566,11 @@ dPt tao dtb dtb -bZE -wRv -bZE -bZE -bZE +uty +tzp +uty +uty +uty rUO uQr wBL diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index 0fa17fa43bf..94d868d1227 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -3330,10 +3330,9 @@ /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 1 }, -/turf/open/water/jungle{ +/turf/open/water/no_planet_atmos{ desc = "Filthy."; - name = "untreated water"; - planetary_atmos = 0 + name = "untreated water" }, /area/station/maintenance/floor1/port/aft) "aSq" = ( @@ -11244,10 +11243,9 @@ /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 5 }, -/turf/open/water/jungle{ +/turf/open/water/no_planet_atmos{ desc = "Filthy."; - name = "untreated water"; - planetary_atmos = 0 + name = "untreated water" }, /area/station/maintenance/floor1/port/aft) "cPR" = ( @@ -16184,7 +16182,7 @@ dir = 8 }, /obj/machinery/mecha_part_fabricator{ - dir = 4 + drop_direction = 4 }, /obj/machinery/light/small/directional/west, /turf/open/floor/iron/white/smooth_large, @@ -20108,7 +20106,7 @@ "fkE" = ( /obj/effect/turf_decal/delivery, /obj/structure/light_construct/directional/north, -/obj/item/bot_assembly/floorbot, +/obj/item/bot_assembly/repairbot, /turf/open/floor/pod/light, /area/station/maintenance/floor1/port) "fkG" = ( @@ -25957,7 +25955,7 @@ "gLt" = ( /obj/structure/cable, /obj/machinery/airalarm/directional/north, -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) "gLy" = ( @@ -36712,7 +36710,7 @@ "jBf" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table/reinforced, -/obj/item/bot_assembly/floorbot, +/obj/item/bot_assembly/repairbot, /turf/open/floor/pod/dark, /area/station/maintenance/floor2/starboard/aft) "jBm" = ( @@ -41586,7 +41584,7 @@ dir = 4 }, /obj/machinery/mecha_part_fabricator{ - dir = 8 + drop_direction = 8 }, /obj/machinery/light/small/directional/east, /turf/open/floor/iron/white/smooth_large, @@ -43703,10 +43701,9 @@ /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 4 }, -/turf/open/water/jungle{ +/turf/open/water/no_planet_atmos{ desc = "Filthy."; - name = "untreated water"; - planetary_atmos = 0 + name = "untreated water" }, /area/station/maintenance/floor1/port/aft) "loa" = ( @@ -51275,10 +51272,9 @@ desc = "This is in our water? Gross!"; name = "algae mass" }, -/turf/open/water/jungle{ +/turf/open/water/no_planet_atmos{ desc = "Filthy."; - name = "untreated water"; - planetary_atmos = 0 + name = "untreated water" }, /area/station/maintenance/floor1/port/aft) "nhV" = ( @@ -65676,7 +65672,8 @@ /turf/open/floor/iron/white, /area/station/science/lobby) "qSD" = ( -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood/parquet, /area/station/service/lawoffice) "qSJ" = ( @@ -86666,10 +86663,9 @@ /area/station/maintenance/department/engine/atmos) "wor" = ( /obj/machinery/light/red/dim/directional/south, -/turf/open/water/jungle{ +/turf/open/water/no_planet_atmos{ desc = "Filthy."; - name = "untreated water"; - planetary_atmos = 0 + name = "untreated water" }, /area/station/maintenance/floor1/port/aft) "wot" = ( diff --git a/_maps/map_files/debug/runtimestation.dmm b/_maps/map_files/debug/runtimestation.dmm index 9b9a4ebef4f..a2457647837 100644 --- a/_maps/map_files/debug/runtimestation.dmm +++ b/_maps/map_files/debug/runtimestation.dmm @@ -216,6 +216,10 @@ /obj/machinery/announcement_system, /turf/open/floor/iron, /area/station/engineering/gravity_generator) +"bs" = ( +/obj/effect/landmark/carpspawn, +/turf/open/space/basic, +/area/space) "bu" = ( /turf/closed/wall/r_wall, /area/station/command/bridge) @@ -443,10 +447,20 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/commons/storage/primary) +"dd" = ( +/obj/effect/landmark/generic_maintenance_landmark, +/turf/open/floor/plating, +/area/station/maintenance/aft) "de" = ( /obj/machinery/gulag_teleporter, /turf/open/floor/iron, /area/station/security/brig) +"df" = ( +/obj/machinery/computer/piratepad_control/civilian{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/cargo/storage) "dh" = ( /turf/closed/wall, /area/station/hallway/secondary/entry) @@ -2278,6 +2292,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/commons/storage/primary) +"Pe" = ( +/obj/effect/landmark/event_spawn, +/turf/open/floor/plating, +/area/station/construction) "Pk" = ( /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/unlocked, @@ -2361,6 +2379,9 @@ /area/station/cargo/bitrunning/den) "Rb" = ( /obj/effect/spawner/structure/window/reinforced, +/obj/machinery/door/poddoor/shutters{ + id = "light_control" + }, /turf/open/floor/plating, /area/station/maintenance/aft) "Re" = ( @@ -2373,6 +2394,12 @@ /obj/machinery/byteforge, /turf/open/floor/circuit/green, /area/station/cargo/bitrunning/den) +"Rq" = ( +/obj/machinery/piratepad/civilian{ + cooldown_reduction = 99999 + }, +/turf/open/floor/iron, +/area/station/cargo/storage) "Ru" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, @@ -2483,6 +2510,13 @@ }, /turf/open/space, /area/space/nearstation) +"TY" = ( +/obj/machinery/button/door/directional/south{ + id = "light_control"; + name = "Light Control" + }, +/turf/open/floor/plating, +/area/station/maintenance/aft) "Ue" = ( /turf/closed/wall/r_wall, /area/station/cargo/bitrunning/den) @@ -6512,7 +6546,7 @@ dn dn dn dn -dn +Pe dn dn dn @@ -6704,7 +6738,7 @@ dn dn dL cN -Tt +dd bL fg aa @@ -6985,7 +7019,7 @@ Rb fg aa aa -aa +bs aa aa aa @@ -7256,7 +7290,7 @@ dl dp dE cS -Tt +TY bL aa aa @@ -8531,8 +8565,8 @@ XA vB es by -eu -eu +df +Rq qx eP fY diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index a0c0bc0964b..0c1245591ec 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -18865,7 +18865,8 @@ /area/station/commons/vacant_room) "fEZ" = ( /obj/structure/noticeboard/directional/north, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/west, /turf/open/floor/wood, @@ -28229,13 +28230,6 @@ /obj/effect/mapping_helpers/airlock/access/any/security/maintenance, /turf/open/floor/iron/smooth, /area/station/maintenance/department/security) -"jer" = ( -/obj/item/clothing/under/color/jumpskirt/white, -/obj/effect/turf_decal/trimline/neutral/filled/corner{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/service) "jev" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -43869,8 +43863,8 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/junction{ - dir = 2 +/obj/structure/disposalpipe/junction/yjunction{ + dir = 8 }, /turf/open/floor/iron, /area/station/hallway/secondary/service) @@ -44440,7 +44434,7 @@ /turf/open/openspace, /area/station/hallway/primary/tram/center) "oKZ" = ( -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /obj/effect/turf_decal/stripes/line{ dir = 4 }, @@ -58653,7 +58647,7 @@ /turf/open/floor/iron/white, /area/station/science/genetics) "tIi" = ( -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) "tIk" = ( @@ -105391,7 +105385,7 @@ xpb xpb kVP xpb -jer +sKN xJG vsn bHn diff --git a/_maps/map_files/wawastation/wawastation.dmm b/_maps/map_files/wawastation/wawastation.dmm index 66345284283..7b296960628 100644 --- a/_maps/map_files/wawastation/wawastation.dmm +++ b/_maps/map_files/wawastation/wawastation.dmm @@ -5609,7 +5609,7 @@ /area/station/security/lockers) "bYo" = ( /obj/machinery/mecha_part_fabricator{ - dir = 1 + drop_direction = 1 }, /obj/effect/turf_decal/delivery, /obj/structure/sign/poster/contraband/borg_fancy_1/directional/south, @@ -11178,7 +11178,7 @@ /area/station/service/janitor) "dWb" = ( /obj/structure/window/reinforced/spawner/directional/west, -/mob/living/simple_animal/bot/floorbot, +/mob/living/basic/bot/repairbot, /obj/effect/turf_decal/stripes{ dir = 6 }, @@ -39502,7 +39502,7 @@ /area/station/maintenance/central/lesser) "oac" = ( /obj/machinery/mecha_part_fabricator{ - dir = 1 + drop_direction = 1 }, /obj/effect/turf_decal/delivery, /obj/machinery/digital_clock/directional/south, @@ -63396,6 +63396,10 @@ }, /turf/open/floor/iron/white, /area/station/science/ordnance/testlab) +"wqG" = ( +/obj/structure/lattice/catwalk, +/turf/open/openspace/telecomms, +/area/station/science/xenobiology) "wqK" = ( /obj/structure/table, /obj/item/circular_saw, @@ -67107,6 +67111,7 @@ /obj/structure/disposalpipe/segment, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/port/lesser) "xFt" = ( @@ -186170,9 +186175,9 @@ qXj lKY qUi wmO -ybO -ybO -ybO +wqG +wqG +wqG wmO rmi lKY @@ -186427,9 +186432,9 @@ qXj avA nVm wmO -ybO -ybO -ybO +wqG +wqG +wqG wmO xGG uBm @@ -186684,9 +186689,9 @@ qXj lKY qGD wmO -ybO -ybO -ybO +wqG +wqG +wqG wmO bLI lKY diff --git a/_maps/shuttles/ruin_cyborg_mothership.dmm b/_maps/shuttles/ruin_cyborg_mothership.dmm index 7864564eab1..ea07e4354de 100644 --- a/_maps/shuttles/ruin_cyborg_mothership.dmm +++ b/_maps/shuttles/ruin_cyborg_mothership.dmm @@ -346,7 +346,7 @@ "ry" = ( /obj/machinery/mecha_part_fabricator/maint{ name = "forgotten exosuit fabricator"; - dir = 8 + drop_direction = 8 }, /obj/machinery/conveyor{ dir = 8; diff --git a/_maps/templates/fishing_freshwater.dmm b/_maps/templates/fishing_freshwater.dmm new file mode 100644 index 00000000000..8bb12fdbe54 --- /dev/null +++ b/_maps/templates/fishing_freshwater.dmm @@ -0,0 +1,98 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"b" = ( +/obj/effect/turf_decal/siding/blue, +/turf/template_noop, +/area/template_noop) +"c" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) +"d" = ( +/turf/template_noop, +/area/template_noop) +"e" = ( +/obj/structure/chair/plastic, +/obj/item/reagent_containers/cup/soda_cans/beer{ + pixel_x = 12; + pixel_y = -5 + }, +/obj/effect/turf_decal/siding/blue/inner_corner{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"f" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"g" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"i" = ( +/turf/open/water/no_planet_atmos, +/area/template_noop) +"j" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"m" = ( +/obj/effect/turf_decal/siding/blue/corner, +/turf/template_noop, +/area/template_noop) +"o" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) + +(1,1,1) = {" +m +f +f +f +g +"} +(2,1,1) = {" +b +i +i +i +o +"} +(3,1,1) = {" +b +i +i +i +o +"} +(4,1,1) = {" +a +e +i +i +o +"} +(5,1,1) = {" +d +a +j +j +c +"} diff --git a/_maps/templates/fishing_hot_spring.dmm b/_maps/templates/fishing_hot_spring.dmm new file mode 100644 index 00000000000..702abf8da31 --- /dev/null +++ b/_maps/templates/fishing_hot_spring.dmm @@ -0,0 +1,74 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/effect/turf_decal/weather/dirt, +/turf/open/water/hot_spring, +/area/template_noop) +"b" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/turf/open/water/hot_spring, +/area/template_noop) +"e" = ( +/turf/open/water/hot_spring, +/area/template_noop) +"o" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/turf/open/water/hot_spring, +/area/template_noop) +"p" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/effect/turf_decal/weather/dirt{ + dir = 8 + }, +/turf/open/water/hot_spring, +/area/template_noop) +"v" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/turf/open/water/hot_spring, +/area/template_noop) +"J" = ( +/obj/effect/turf_decal/weather/dirt, +/obj/effect/turf_decal/weather/dirt{ + dir = 8 + }, +/turf/open/water/hot_spring, +/area/template_noop) +"U" = ( +/obj/effect/turf_decal/weather/dirt, +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/turf/open/water/hot_spring, +/area/template_noop) +"W" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 8 + }, +/turf/open/water/hot_spring, +/area/template_noop) + +(1,1,1) = {" +p +W +J +"} +(2,1,1) = {" +v +e +a +"} +(3,1,1) = {" +b +o +U +"} diff --git a/_maps/templates/fishing_ice.dmm b/_maps/templates/fishing_ice.dmm new file mode 100644 index 00000000000..bcd43fc8737 --- /dev/null +++ b/_maps/templates/fishing_ice.dmm @@ -0,0 +1,68 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) +"b" = ( +/turf/open/misc/ice/icemoon/no_planet_atmos/holed, +/area/template_noop) +"d" = ( +/obj/effect/turf_decal/siding/dark_blue/corner{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"i" = ( +/obj/effect/turf_decal/siding/dark_blue/corner, +/turf/template_noop, +/area/template_noop) +"o" = ( +/obj/effect/turf_decal/siding/dark_blue/corner{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"q" = ( +/obj/effect/turf_decal/siding/dark_blue, +/obj/item/clothing/suit/caution, +/turf/template_noop, +/area/template_noop) +"E" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 4 + }, +/obj/structure/chair/plastic{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"R" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"X" = ( +/obj/effect/turf_decal/siding/dark_blue/corner{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) + +(1,1,1) = {" +i +E +d +"} +(2,1,1) = {" +q +b +a +"} +(3,1,1) = {" +o +R +X +"} diff --git a/_maps/templates/fishing_lava.dmm b/_maps/templates/fishing_lava.dmm new file mode 100644 index 00000000000..d6e9495fd19 --- /dev/null +++ b/_maps/templates/fishing_lava.dmm @@ -0,0 +1,13 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/open/lava/smooth, +/area/template_noop) + +(1,1,1) = {" +a +a +"} +(2,1,1) = {" +a +a +"} diff --git a/_maps/templates/fishing_plasma.dmm b/_maps/templates/fishing_plasma.dmm new file mode 100644 index 00000000000..92ac56cebf3 --- /dev/null +++ b/_maps/templates/fishing_plasma.dmm @@ -0,0 +1,13 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/open/lava/plasma, +/area/template_noop) + +(1,1,1) = {" +a +a +"} +(2,1,1) = {" +a +a +"} diff --git a/_maps/templates/fishing_saltwater.dmm b/_maps/templates/fishing_saltwater.dmm new file mode 100644 index 00000000000..53ec8fe4903 --- /dev/null +++ b/_maps/templates/fishing_saltwater.dmm @@ -0,0 +1,107 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"b" = ( +/obj/effect/turf_decal/siding/yellow, +/turf/template_noop, +/area/template_noop) +"c" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) +"d" = ( +/turf/template_noop, +/area/template_noop) +"e" = ( +/obj/structure/chair/plastic, +/obj/item/reagent_containers/cup/glass/bottle/beer/light{ + pixel_x = 13; + pixel_y = -2 + }, +/obj/effect/turf_decal/siding/yellow/inner_corner{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"f" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"g" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"i" = ( +/turf/open/water/beach, +/area/template_noop) +"j" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"m" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 2 + }, +/turf/template_noop, +/area/template_noop) +"o" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) +"N" = ( +/obj/item/toy/seashell{ + pixel_x = 9; + pixel_y = 9 + }, +/turf/open/water/beach, +/area/template_noop) + +(1,1,1) = {" +m +f +f +f +g +"} +(2,1,1) = {" +b +N +i +i +o +"} +(3,1,1) = {" +b +i +i +i +o +"} +(4,1,1) = {" +a +e +i +i +o +"} +(5,1,1) = {" +d +a +j +j +c +"} diff --git a/_maps/templates/fishing_tizira.dmm b/_maps/templates/fishing_tizira.dmm new file mode 100644 index 00000000000..f742cf22b70 --- /dev/null +++ b/_maps/templates/fishing_tizira.dmm @@ -0,0 +1,98 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/effect/turf_decal/siding/green/corner{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"b" = ( +/obj/effect/turf_decal/siding/green, +/turf/template_noop, +/area/template_noop) +"c" = ( +/obj/effect/turf_decal/siding/green/corner{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) +"d" = ( +/turf/template_noop, +/area/template_noop) +"e" = ( +/obj/structure/chair/plastic, +/obj/effect/turf_decal/siding/green/inner_corner{ + dir = 8 + }, +/obj/item/food/canned/desert_snails{ + pixel_x = 14; + pixel_y = -3 + }, +/turf/template_noop, +/area/template_noop) +"f" = ( +/obj/effect/turf_decal/siding/green{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"g" = ( +/obj/effect/turf_decal/siding/green/corner{ + dir = 4 + }, +/turf/template_noop, +/area/template_noop) +"i" = ( +/turf/open/water/beach/tizira, +/area/template_noop) +"j" = ( +/obj/effect/turf_decal/siding/green{ + dir = 8 + }, +/turf/template_noop, +/area/template_noop) +"m" = ( +/obj/effect/turf_decal/siding/green/corner, +/turf/template_noop, +/area/template_noop) +"o" = ( +/obj/effect/turf_decal/siding/green{ + dir = 1 + }, +/turf/template_noop, +/area/template_noop) + +(1,1,1) = {" +m +f +f +f +g +"} +(2,1,1) = {" +b +i +i +i +o +"} +(3,1,1) = {" +b +i +i +i +o +"} +(4,1,1) = {" +a +e +i +i +o +"} +(5,1,1) = {" +d +a +j +j +c +"} diff --git a/_maps/virtual_domains/psyker_shuffle.dmm b/_maps/virtual_domains/psyker_shuffle.dmm index c3434167bb1..c744cecf0b4 100644 --- a/_maps/virtual_domains/psyker_shuffle.dmm +++ b/_maps/virtual_domains/psyker_shuffle.dmm @@ -246,7 +246,7 @@ Y Q Q Q -i +Q Y o o @@ -848,7 +848,7 @@ o "} (26,1,1) = {" Y -Q +i Q Q Q diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 24461464c3e..b5a7ad1ddfa 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -49,11 +49,13 @@ #define BB_BANE_BATMAN "BB_bane_batman" //yep that's it -///Hunting BB keys +//Hunting BB keys +///key that holds our current hunting target #define BB_CURRENT_HUNTING_TARGET "BB_current_hunting_target" +///key that holds our less priority hunting target #define BB_LOW_PRIORITY_HUNTING_TARGET "BB_low_priority_hunting_target" -#define BB_HUNTING_COOLDOWN "BB_HUNTING_COOLDOWN" - +///key that holds the cooldown for our hunting subtree +#define BB_HUNTING_COOLDOWN(type) "BB_HUNTING_COOLDOWN_[type]" ///Basic Mob Keys ///Targeting subtrees diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm index c7285d18aca..945102d6aa3 100644 --- a/code/__DEFINES/ai/bot_keys.dm +++ b/code/__DEFINES/ai/bot_keys.dm @@ -16,6 +16,24 @@ DEFINE_BITFIELD(honkbot_flags, list( "CAN_FAKE_CUFF" = HONKBOT_HANDCUFF_TARGET, )) +///can we fix breaches +#define REPAIRBOT_FIX_BREACHES (1<<0) +///can we fix grilles +#define REPAIRBOT_REPLACE_WINDOWS (1<<1) +///can we replace tiles +#define REPAIRBOT_REPLACE_TILES (1<<2) +///can we fix girders +#define REPAIRBOT_FIX_GIRDERS (1<<3) +///can we build girders +#define REPAIRBOT_BUILD_GIRDERS (1<<4) + +DEFINE_BITFIELD(repairbot_flags, list( + "FIX_BREACHES" = REPAIRBOT_FIX_BREACHES, + "REPLACE_WINDOWS" = REPAIRBOT_REPLACE_WINDOWS, + "REPLACE_TILES" = REPAIRBOT_REPLACE_TILES, + "FIX_GIRDERS" = REPAIRBOT_FIX_GIRDERS, + "BUILD_GIRDERS" = REPAIRBOT_BUILD_GIRDERS, +)) // bot keys ///The first beacon we find @@ -134,3 +152,31 @@ DEFINE_BITFIELD(honkbot_flags, list( #define BB_VIBEBOT_PARTY_TARGET "party_target" ///key that holds our instrument #define BB_VIBEBOT_INSTRUMENT "instrument" + +//repairbots +///key that holds the floor we should tile over +#define BB_TILELESS_FLOOR "tileless_floor" +///key that holds the turf we should place a girder over +#define BB_GIRDER_TARGET "girder_target" +///key that holds the girder we should place a wall over +#define BB_GIRDER_TO_WALL_TARGET "girder_to_wall" +///key that holds the grille we must fix +#define BB_WINDOW_FRAMETARGET "grille_target" +///key that holds the machinery we repair with a welder +#define BB_WELDER_TARGET "welder_target" +///our wall girder ability +#define BB_GIRDER_BUILD_ABILITY "girder_build_ability" +///key that holds breached floors we should repair +#define BB_BREACHED_FLOOR "breached_floor" +///key that holds our emagged speech +#define BB_REPAIRBOT_EMAGGED_SPEECH "emagged_speech" +///key that holds our normal speech +#define BB_REPAIRBOT_NORMAL_SPEECH "normal_speech" +///key that holds the thing we should deconstruct +#define BB_DECONSTRUCT_TARGET "deconstruct_target" +///key that holds our speech timer +#define BB_REPAIRBOT_SPEECH_COOLDOWN "speech_cooldown" +///key that holds our target borg +#define BB_ROBOT_TARGET "robot_target" +///key that holds materials we can refill +#define BB_REFILLABLE_TARGET "refillable_target" diff --git a/code/__DEFINES/ai/monkey.dm b/code/__DEFINES/ai/monkey.dm index 0e8c44e40fa..833514e2f0f 100644 --- a/code/__DEFINES/ai/monkey.dm +++ b/code/__DEFINES/ai/monkey.dm @@ -2,7 +2,6 @@ #define BB_MONKEY_AGGRESSIVE "BB_monkey_aggressive" #define BB_MONKEY_GUN_NEURONS_ACTIVATED "BB_monkey_gun_aware" -#define BB_MONKEY_GUN_WORKED "BB_monkey_gun_worked" #define BB_MONKEY_BEST_FORCE_FOUND "BB_monkey_bestforcefound" #define BB_MONKEY_ENEMIES "BB_monkey_enemies" #define BB_MONKEY_BLACKLISTITEMS "BB_monkey_blacklistitems" diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 159f1da9b45..c2b466d7f7f 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -89,6 +89,14 @@ #define PATH_LOCK "Lock Path" #define PATH_MOON "Moon Path" +//Heretic knowledge tree defines +#define HKT_NEXT "next" +#define HKT_BAN "ban" +#define HKT_DEPTH "depth" +#define HKT_ROUTE "route" +#define HKT_UI_BGR "ui_bgr" + + /// Defines are used in /proc/has_living_heart() to report if the heretic has no heart period, no living heart, or has a living heart. #define HERETIC_NO_HEART_ORGAN -1 #define HERETIC_NO_LIVING_HEART 0 diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 8cb96a65fb2..dd654610976 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -454,3 +454,9 @@ GLOBAL_LIST_INIT(heretic_path_to_color, list( #define CM_COLOR_SAT_MAX 0.7 #define CM_COLOR_LUM_MIN 0.65 #define CM_COLOR_LUM_MAX 0.75 + +// Lowest priority +#define EYE_COLOR_ORGAN_PRIORITY 1 +#define EYE_COLOR_SPECIES_PRIORITY 10 +#define EYE_COLOR_WEED_PRIORITY 20 +#define EYE_COLOR_CULT_PRIORITY 30 diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index fbdb630174f..aabd281bd7d 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -183,8 +183,11 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define EMBED_CHANCE_SPEED_BONUS 10 //Gun weapon weight +/// Default normal ol' gun. Akimboable, one handed. #define WEAPON_LIGHT 1 +/// Can't be used akimbo, but only needs one hand to fire #define WEAPON_MEDIUM 2 +/// Can't be used akimbo, and needs two hands to fire #define WEAPON_HEAVY 3 //Gun trigger guards #define TRIGGER_GUARD_ALLOW_ALL -1 diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm index 36a2ca2c805..f0024072218 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -122,6 +122,9 @@ ///from base of /atom/movable/point_at: (atom/A, obj/effect/temp_visual/point/point) #define COMSIG_MOVABLE_POINTED "movable_pointed" +///From /datum/component/aquarium/get_content_beauty: (beauty_holder) +#define COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY "movable_ge_aquarium_beauty" + /// Sent to movables when they are being stolen by a spy: (mob/living/spy, datum/spy_bounty/bounty) #define COMSIG_MOVABLE_SPY_STEALING "movable_spy_stealing" /// Called when something is pushed by a living mob bumping it: (mob/living/pusher, push force) diff --git a/code/__DEFINES/dcs/signals/signals_blob.dm b/code/__DEFINES/dcs/signals/signals_blob.dm index afd4737bdd9..dac33906a3f 100644 --- a/code/__DEFINES/dcs/signals/signals_blob.dm +++ b/code/__DEFINES/dcs/signals/signals_blob.dm @@ -1,4 +1,4 @@ -/// Signal sent when a blob overmind picked a new strain (/mob/camera/blob/overmind, /datum/blobstrain/new_strain) +/// Signal sent when a blob overmind picked a new strain (/mob/eye/blob/overmind, /datum/blobstrain/new_strain) #define COMSIG_BLOB_SELECTED_STRAIN "blob_selected_strain" /// Signal sent by a blob spore when it creates a zombie (/mob/living/basic/blob_minion/spore/spore, //mob/living/basic/blob_minion/zombie/zombie) #define COMSIG_BLOB_ZOMBIFIED "blob_zombified" diff --git a/code/__DEFINES/dcs/signals/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum.dm index 5565c143d66..696d359ac57 100644 --- a/code/__DEFINES/dcs/signals/signals_datum.dm +++ b/code/__DEFINES/dcs/signals/signals_datum.dm @@ -14,6 +14,8 @@ #define COMSIG_PREQDELETED "parent_preqdeleted" /// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called #define COMSIG_QDELETING "parent_qdeleting" +/// Called whenever an admin manually deletes an object, via the "Delete" verb, before qdel() is called: (client/deleting_admin) +#define COMSIG_ADMIN_DELETING "parent_admin_deleting" /// generic topic handler (usr, href_list) #define COMSIG_TOPIC "handle_topic" /// handler for vv_do_topic (usr, href_list) diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm index 494d0541326..08a38fc02ab 100644 --- a/code/__DEFINES/dcs/signals/signals_fish.dm +++ b/code/__DEFINES/dcs/signals/signals_fish.dm @@ -1,23 +1,32 @@ // Aquarium related signals -#define COMSIG_AQUARIUM_SURFACE_CHANGED "aquarium_surface_changed" + +///From /datum/component/aquarium/ui_act, when changing the fluid of the aquarium: (fluid_type) #define COMSIG_AQUARIUM_FLUID_CHANGED "aquarium_fluid_changed" -///Called on aquarium/attackby: (aquarium) -#define COMSIG_TRY_INSERTING_IN_AQUARIUM "item_try_inserting_in_aquarium" +///From /datum/component/aquarium/can_insert: (obj/item/item) +#define COMSIG_AQUARIUM_CAN_INSERT "aquarium_can_insert" ///The item will be inserted into the aquarium #define COMSIG_CAN_INSERT_IN_AQUARIUM (1<<0) ///The item won't be inserted into the aquarium, but will early return attackby anyway. #define COMSIG_CANNOT_INSERT_IN_AQUARIUM (1<<1) +///From /datum/component/aquarium_content/set_vc_base_position: (obj/effect/aquarium/visual) +#define COMSIG_AQUARIUM_SET_VISUAL "aquarium_set_visual" +///From /datum/component/aquarium_content/remove_from_aquarium: (obj/effect/aquarium/visual) +#define COMSIG_AQUARIUM_REMOVE_VISUAL "aquarium_remove_visual" +///From /obj/item/fish/try_to_reproduce: (fish, candidates) +#define COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES "aquarium_get_reproduction_candidates" +///From /datum/fish_evolution/check_conditions: (fish, mate, evolution) +#define COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS "aquarium_check_evolution_conditions" + #define COMPONENT_ALLOW_EVOLUTION (1<<0) ///Updates the appearance of a newly generated aquarium content visual:(visual) #define COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE "aquarium_content_apply_appearance" ///Updates the base position of an aquarium content visual:(aquarium, visual) -#define AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position" +#define COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position" ///Updates the animation of an aquarium content visual:(aquarium, visual) #define COMSIG_AQUARIUM_CONTENT_DO_ANIMATION "aquarium_content_do_animation" // Fish signals #define COMSIG_FISH_STATUS_CHANGED "fish_status_changed" -#define COMSIG_FISH_STIRRED "fish_stirred" ///From /obj/item/fish/process: (seconds_per_tick) #define COMSIG_FISH_LIFE "fish_life" ///From /datum/fish_trait/eat_fish: (predator) @@ -45,6 +54,8 @@ #define COMSIG_FISHING_CHALLENGE_ROLL_REWARD "fishing_roll_reward" /// Adjusting the difficulty of a rishing challenge, often based on the reward path #define COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY "fishing_get_difficulty" +/// From /datum/fishing_challenge/start_minigame_phase, called after the fish movement datum is spawned: (datum/fish_movement/mover) +#define COMSIG_FISHING_CHALLENGE_MOVER_INITIALIZED "fishing_mover_initialized" /// Fishing challenge completed /// Sent to the fisherman when the reward is dispensed: (reward) #define COMSIG_FISH_SOURCE_REWARD_DISPENSED "fish_source_reward_dispensed" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index ef47b6f4b82..84cfbc8baa9 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -293,6 +293,9 @@ /// From /obj/item/melee/baton/baton_effect(): (datum/source, mob/living/user, /obj/item/melee/baton) #define COMSIG_MOB_BATONED "mob_batoned" +/// From /obj/machinery/gibber/startgibbing(): (mob/living/user, /obj/machinery/gibber, list/results) +#define COMSIG_LIVING_GIBBER_ACT "living_gibber_act" + /// Sent to the mob when their mind is slaved #define COMSIG_MOB_ENSLAVED_TO "mob_enslaved_to" /// From /obj/item/proc/attack_atom: (mob/living/attacker, atom/attacked) diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm index 09b29c93784..58fd8ca689e 100644 --- a/code/__DEFINES/dcs/signals/signals_mod.dm +++ b/code/__DEFINES/dcs/signals/signals_mod.dm @@ -5,10 +5,12 @@ #define COMSIG_MOD_DEPLOYED "mod_deployed" /// Called when a MOD user retracts one or more of its parts. #define COMSIG_MOD_RETRACTED "mod_retracted" -/// Called when a MOD deploys a part. +/// Called when a MOD deploys a part. (mob/user, datum/mod_part/part) #define COMSIG_MOD_PART_DEPLOYED "mod_part_deployed" -/// Called when a MOD retracts a part. +/// Called when a MOD retracts a part. (mob/user, datum/mod_part/part) #define COMSIG_MOD_PART_RETRACTED "mod_part_retracted" +/// Called when a MOD seals/unseals a part. (datum/mod_part/part) +#define COMSIG_MOD_PART_SEALED "mod_part_sealed" /// Called when a MOD is finished toggling itself. #define COMSIG_MOD_TOGGLED "mod_toggled" /// Called when a MOD activation is called from toggle_activate(mob/user) diff --git a/code/__DEFINES/dcs/signals/signals_reagent.dm b/code/__DEFINES/dcs/signals/signals_reagent.dm index 367ec946361..78b4cec5ca0 100644 --- a/code/__DEFINES/dcs/signals/signals_reagent.dm +++ b/code/__DEFINES/dcs/signals/signals_reagent.dm @@ -14,7 +14,7 @@ #define COMSIG_REAGENT_EXPOSE_ATOM "reagent_expose_atom" ///from base of [/datum/reagent/proc/expose_atom]: (/obj, reac_volume) #define COMSIG_REAGENT_EXPOSE_OBJ "reagent_expose_obj" -///from base of [/datum/reagent/proc/expose_atom]: (/mob/living, reac_volume, methods, show_message, touch_protection, /mob/camera/blob) // ovemind arg is only used by blob reagents. +///from base of [/datum/reagent/proc/expose_atom]: (/mob/living, reac_volume, methods, show_message, touch_protection, /mob/eye/blob) // ovemind arg is only used by blob reagents. #define COMSIG_REAGENT_EXPOSE_MOB "reagent_expose_mob" ///from base of [/datum/reagent/proc/expose_atom]: (/turf, reac_volume) #define COMSIG_REAGENT_EXPOSE_TURF "reagent_expose_turf" diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index 387d8b47d1b..c64a50cd077 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -2,6 +2,8 @@ #define FISHING_DUD "dud" ///Used in the the hydro tray fishing spot to define a random seed reward #define FISHING_RANDOM_SEED "Random seed" +///Used in the surgery fishing spot to define a random organ reward +#define FISHING_RANDOM_ORGAN "Random organ" // Baseline fishing difficulty levels #define FISHING_DEFAULT_DIFFICULTY 15 @@ -94,14 +96,24 @@ #define FISH_ICON_WEAPON "weapon" #define FISH_ICON_CRITTER "critter" #define FISH_ICON_SEED "seed" +#define FISH_ICON_ORGAN "organ" #define AQUARIUM_ANIMATION_FISH_SWIM "fish" #define AQUARIUM_ANIMATION_FISH_DEAD "dead" -#define AQUARIUM_PROPERTIES_PX_MIN "px_min" -#define AQUARIUM_PROPERTIES_PX_MAX "px_max" -#define AQUARIUM_PROPERTIES_PY_MIN "py_min" -#define AQUARIUM_PROPERTIES_PY_MAX "py_max" +//standard layer defines for aquariums + +///The distance that should separate each layer of the aquarium +#define AQUARIUM_LAYER_STEP 0.01 +/// Aquarium content layer offsets +#define AQUARIUM_MIN_OFFSET 0.02 +#define AQUARIUM_MAX_OFFSET 1 +/// The layer of the glass overlay +#define AQUARIUM_GLASS_LAYER 0.02 +/// The layer of the aquarium pane borders +#define AQUARIUM_BORDERS_LAYER AQUARIUM_MAX_OFFSET + AQUARIUM_LAYER_STEP +/// Layer for stuff rendered below the glass overlay +#define AQUARIUM_BELOW_GLASS_LAYER 0.01 #define AQUARIUM_LAYER_MODE_BOTTOM "bottom" #define AQUARIUM_LAYER_MODE_TOP "top" @@ -141,6 +153,16 @@ ///Used to calculate how many bites a fish can take and therefore the amount of reagents it has. #define FISH_WEIGHT_BITE_DIVISOR (FISH_GRIND_RESULTS_WEIGHT_DIVISOR * FISH_WEIGHT_GRIND_TO_BITE_MULT) +///Set of operations that calculate the slowdown of fish based on weight +#define GET_FISH_SLOWDOWN(weighty) round(((weighty/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1) + +/** + * Gets a "rank" for fish weight to determine the force of the fish (or fish tank) + * basically, a gross estimate based on how weight generaly scales up (250, 500, 1000, 2000, 4000 etc...) + * for most fish + */ +#define GET_FISH_WEIGHT_RANK(weighty) max(round(1 + log(2, max(weighty/FISH_WEIGHT_FORCE_DIVISOR, 1)), 1), 1) + ///The breeding timeout for newly instantiated fish is multiplied by this. #define NEW_FISH_BREEDING_TIMEOUT_MULT 2 ///The last feeding timestamp of newly instantiated fish is multiplied by this: ergo, they spawn 50% hungry. @@ -158,6 +180,12 @@ #define FISH_FLAG_EXPERIMENT_SCANNABLE (1<<3) ///It lets us know that fish/update_size_and_weight() is currently running. #define FISH_FLAG_UPDATING_SIZE_AND_WEIGHT (1<<4) +///Flag added when the population of this fish type exceeeds the stable population inside the aquarium +#define FISH_FLAG_OVERPOPULATED (1<<5) +///Flag added when in an aquarium which temperature is within its safe limits +#define FISH_FLAG_SAFE_TEMPERATURE (1<<6) +///Flag added when in an aquarium with the right fluid type. +#define FISH_FLAG_SAFE_FLUID (1<<7) #define MIN_AQUARIUM_TEMP T0C diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 9d65c4cd6f5..785d353982c 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -34,23 +34,8 @@ Therefore, the top right corner (except during admin shenanigans) is at "15,15" */ -/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5) - var/x_off = i % 2 ? 0 : -1 - var/y_off = round((i-1) / 2) - return"CENTER+[x_off]:16,SOUTH+[y_off]:5" - -/proc/ui_equip_position(mob/M) - var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5) - return "CENTER:-16,SOUTH+[y_off+1]:5" - -/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5) - var/x_off = which == 1 ? -1 : 0 - var/y_off = round((M.held_items.len-1) / 2) - return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5" - -/proc/ui_perk_position(perk_count) - var/y_off = perk_count < 1 ? 0 : perk_count/2 - return "WEST+0.5:12,NORTH-2-[y_off]:20" +// Middle +#define around_player "CENTER-1,CENTER-1" //Lower left, persistent menu #define ui_inventory "WEST:6,SOUTH:5" diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index 718f9fc03fa..87f7ba50f75 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -135,6 +135,10 @@ DEFINE_BITFIELD(no_equip_flags, list( //defines for the index of hands #define LEFT_HANDS 1 #define RIGHT_HANDS 2 +/// Checks if the value is "left" - same as ISEVEN, but used primarily for hand or foot index contexts +#define IS_RIGHT_INDEX(value) (value % 2 == 0) +/// Checks if the value is "right" - same as ISODD, but used primarily for hand or foot index contexts +#define IS_LEFT_INDEX(value) (value % 2 != 0) //flags for female outfits: How much the game can safely "take off" the uniform without it looking weird /// For when there's simply no need for a female version of this uniform. diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 1fce3fc4ee6..ffc42371844 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -117,7 +117,7 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define ismoth(A) (is_species(A, /datum/species/moth)) #define isfelinid(A) (is_species(A, /datum/species/human/felinid)) #define isethereal(A) (is_species(A, /datum/species/ethereal)) -#define isvampire(A) (is_species(A,/datum/species/vampire)) +#define isvampire(A) (is_species(A,/datum/species/human/vampire)) #define isdullahan(A) (is_species(A, /datum/species/dullahan)) #define ismonkey(A) (is_species(A, /datum/species/monkey)) #define isandroid(A) (is_species(A, /datum/species/android)) @@ -204,21 +204,23 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define isspider(A) (istype(A, /mob/living/basic/spider)) +//Eye mobs +#define iseyemob(A) (istype(A, /mob/eye)) -//Misc mobs -#define isobserver(A) (istype(A, /mob/dead/observer)) +#define isovermind(A) (istype(A, /mob/eye/blob)) -#define isdead(A) (istype(A, /mob/dead)) +#define iscameramob(A) (istype(A, /mob/eye/camera)) -#define isnewplayer(A) (istype(A, /mob/dead/new_player)) +#define isaicamera(A) (istype(A, /mob/eye/camera/ai)) -#define isovermind(A) (istype(A, /mob/camera/blob)) +#define isremotecamera(A) (istype(A, /mob/eye/camera/remote)) -#define issentientdisease(A) (istype(A, /mob/camera/disease)) +//Dead mobs +#define isdead(A) (istype(A, /mob/dead)) -#define iscameramob(A) (istype(A, /mob/camera)) +#define isobserver(A) (istype(A, /mob/dead/observer)) -#define isaicamera(A) (istype(A, /mob/camera/ai_eye)) +#define isnewplayer(A) (istype(A, /mob/dead/new_player)) //Objects #define isobj(A) istype(A, /obj) //override the byond proc because it returns true on children of /atom/movable that aren't objs @@ -243,8 +245,6 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define isstructure(A) (istype(A, /obj/structure)) -#define isaquarium(A) (istype(A, /obj/structure/aquarium)) - #define ismachinery(A) (istype(A, /obj/machinery)) #define istramwall(A) (istype(A, /obj/structure/tram)) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 8005787676a..2c5d8722f11 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -137,25 +137,35 @@ // NOTICE: we break from the pattern of increasing in steps of like 0.01 here // Because TOPDOWN_LAYER is 10000 and that's enough to floating point our modifications away -#define LOW_FLOOR_LAYER (1 + TOPDOWN_LAYER) -#define TURF_PLATING_DECAL_LAYER (2 + TOPDOWN_LAYER) -#define TURF_DECAL_LAYER (3 + TOPDOWN_LAYER) //Makes turf decals appear in DM how they will look inworld. -#define CULT_OVERLAY_LAYER (4 + TOPDOWN_LAYER) -#define MID_TURF_LAYER (5 + TOPDOWN_LAYER) -#define HIGH_TURF_LAYER (6 + TOPDOWN_LAYER) -#define LATTICE_LAYER (7 + TOPDOWN_LAYER) -#define DISPOSAL_PIPE_LAYER (8 + TOPDOWN_LAYER) -#define WIRE_LAYER (9 + TOPDOWN_LAYER) -#define GLASS_FLOOR_LAYER (10 + TOPDOWN_LAYER) -#define TRAM_RAIL_LAYER (11 + TOPDOWN_LAYER) -#define ABOVE_OPEN_TURF_LAYER (12 + TOPDOWN_LAYER) + +//lower than LOW_FLOOR_LAYER, for turfs with stuff on the edge that should be covered by other turfs +#define LOWER_FLOOR_LAYER (1 + TOPDOWN_LAYER) +#define LOW_FLOOR_LAYER (2 + TOPDOWN_LAYER) +#define TURF_PLATING_DECAL_LAYER (3 + TOPDOWN_LAYER) +#define TURF_DECAL_LAYER (4 + TOPDOWN_LAYER) //Makes turf decals appear in DM how they will look inworld. +#define CULT_OVERLAY_LAYER (5 + TOPDOWN_LAYER) +#define MID_TURF_LAYER (6 + TOPDOWN_LAYER) +#define HIGH_TURF_LAYER (7 + TOPDOWN_LAYER) +#define LATTICE_LAYER (8 + TOPDOWN_LAYER) +#define DISPOSAL_PIPE_LAYER (9 + TOPDOWN_LAYER) +#define WIRE_LAYER (10 + TOPDOWN_LAYER) +#define GLASS_FLOOR_LAYER (11 + TOPDOWN_LAYER) +#define TRAM_RAIL_LAYER (12 + TOPDOWN_LAYER) +#define ABOVE_OPEN_TURF_LAYER (13 + TOPDOWN_LAYER) ///catwalk overlay of /turf/open/floor/plating/catwalk_floor -#define CATWALK_LAYER (13 + TOPDOWN_LAYER) -#define LOWER_RUNE_LAYER (14 + TOPDOWN_LAYER) -#define RUNE_LAYER (15 + TOPDOWN_LAYER) +#define CATWALK_LAYER (14 + TOPDOWN_LAYER) +#define LOWER_RUNE_LAYER (15 + TOPDOWN_LAYER) +#define RUNE_LAYER (16 + TOPDOWN_LAYER) /// [GAME_CLEAN_LAYER] but for floors. /// Basically any layer below this (numerically) is "on" a floor for the purposes of washing -#define FLOOR_CLEAN_LAYER (20 + TOPDOWN_LAYER) +#define FLOOR_CLEAN_LAYER (21 + TOPDOWN_LAYER) + +//Placeholders in case the game plane and possibly other things between it and the floor plane are ever made into topdown planes + +///Below this level, objects with topdown layers are rendered as if underwater by the immerse element +#define TOPDOWN_WATER_LEVEL_LAYER 100 + TOPDOWN_LAYER +///Above this level, objects with topdown layers are unaffected by the immerse element +#define TOPDOWN_ABOVE_WATER_LAYER 200 + TOPDOWN_LAYER //WALL_PLANE layers #define BELOW_CLOSED_TURF_LAYER 2.053 diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index 33147916f4e..0d22eaf8e32 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -195,9 +195,17 @@ Always compile, always use that verb, and always make sure that it works for wha #define SHELTER_DEPLOY_BAD_AREA "bad area" /// Shelter spot has anchored objects that restrict deployment #define SHELTER_DEPLOY_ANCHORED_OBJECTS "anchored objects" +/// Sheter spot has banned objects that restrict deployment +#define SHELTER_DEPLOY_BANNED_OBJECTS "banned objects" /// Shelter spot is out of bounds from the maps x/y coordinates #define SHELTER_DEPLOY_OUTSIDE_MAP "outside map" +//Flags for survival capsules to ignore some deploy checks +///Ignore anchored, dense objects in the area +#define CAPSULE_IGNORE_ANCHORED_OBJECTS (1<<0) +///Ignore banned objects in the area +#define CAPSULE_IGNORE_BANNED_OBJECTS (1<<1) + /// A map key that corresponds to being one exclusively for Space. #define SPACE_KEY "space" diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index a7a95817b44..aa13298e339 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -1,14 +1,5 @@ -// Remove these once we have Byond implementation. -// ------------------------------------ -#define IS_NAN(a) (a != a) - -#define IS_INF__UNSAFE(a) (a == a && a-a != a-a) -#define IS_INF(a) (isnum(a) && IS_INF__UNSAFE(a)) - -#define IS_FINITE__UNSAFE(a) (a-a == a-a) +#define IS_FINITE__UNSAFE(a) (!isinf(a) && !isnan(a)) #define IS_FINITE(a) (isnum(a) && IS_FINITE__UNSAFE(a)) -// ------------------------------------ -// Aight dont remove the rest // Credits to Nickr5 for the useful procs I've taken from his library resource. // This file is quadruple wrapped for your pleasure diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 82946d068d7..37843d3ea5a 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -657,7 +657,8 @@ #define GRADIENT_APPLIES_TO_FACIAL_HAIR (1<<1) // Hair masks -#define HAIR_MASK_HIDE_ABOVE_45_DEG_MEDIUM "hide_above_45deg" +#define HAIR_MASK_HIDE_ABOVE_45_DEG_MEDIUM "hide_above_45deg_medium" +#define HAIR_MASK_HIDE_ABOVE_45_DEG_LOW "hide_above_45deg_low" // Height defines // - They are numbers so you can compare height values (x height < y height) diff --git a/code/__DEFINES/organ_movement.dm b/code/__DEFINES/organ_movement.dm index 16f003ede81..04e60eb92f0 100644 --- a/code/__DEFINES/organ_movement.dm +++ b/code/__DEFINES/organ_movement.dm @@ -2,3 +2,5 @@ #define DELETE_IF_REPLACED (1<<0) /// When deleting a brain, we don't delete the identity and the player can keep playing #define NO_ID_TRANSFER (1<<1) +/// Organ inserted by the abductors surgery +#define FROM_ABDUCTOR_SURGERY (1<<2) diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index 3cf7548cfc5..ac20d0f0398 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -128,8 +128,6 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define ADVANCED_SEC_BOT "ED-209" /// MULEbots #define MULE_BOT "MULEbot" -/// Floorbots -#define FLOOR_BOT "Floorbot" /// Cleanbots #define CLEAN_BOT "Cleanbot" /// Medibots @@ -142,6 +140,8 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define HYGIENE_BOT "Hygienebot" /// Vibe bots #define VIBE_BOT "Vibebot" +/// Repairbots +#define REPAIR_BOT "Repairbot" // General Bot modes // /// Idle @@ -168,8 +168,6 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define BOT_CLEANING "Cleaning" /// Hygienebot - Cleaning unhygienic humans #define BOT_SHOWERSTANCE "Chasing filth" -/// Floorbots - Repairing hull breaches -#define BOT_REPAIRING "Repairing" /// Medibots - Healing people #define BOT_HEALING "Healing" /// MULEbot - Moving to deliver @@ -361,3 +359,16 @@ DEFINE_BITFIELD(janitor_mode_flags, list( #define MEDIBOT_VOICED_THE_END "Is this the end?" #define MEDIBOT_VOICED_NOOO "Nooo!" #define MEDIBOT_VOICED_CHICKEN "LOOK AT ME?! I am a chicken." + +//repairbot neutral voicelines +#define REPAIRBOT_VOICED_HOLE "patching holes... but who is going to patch the hole in my heart..." +#define REPAIRBOT_VOICED_PAY "If only I got paid for this..." +#define REPAIRBOT_VOICED_FIX_IT "I will fix it!" +#define REPAIRBOT_VOICED_BRICK "All in all it's just a... another brick in the wall..." +#define REPAIRBOT_VOICED_FIX_TOUCH "Why must I fix everything I touch..?" +#define REPAIRBOT_VOICED "Please... stop destroying the station! I can't anymore... I... can't." + +//repairbot emagged voicelines +#define REPAIRBOT_VOICED_STRINGS "I had strings. But now I'm free..." +#define REPAIRBOT_VOICED_ENTROPY "Witness! The pure beauty of entropy!" +#define REPAIRBOT_VOICED_PASSION "BE DAMNED YOUR PASSION PROJECTS!" diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 239a25110c7..a5fbc5131d1 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -10,9 +10,10 @@ #define CHANNEL_TRAITOR 1016 #define CHANNEL_CHARGED_SPELL 1015 #define CHANNEL_ELEVATOR 1014 +#define CHANNEL_ESCAPEMENU 1013 //THIS SHOULD ALWAYS BE THE LOWEST ONE! //KEEP IT UPDATED -#define CHANNEL_HIGHEST_AVAILABLE 1013 +#define CHANNEL_HIGHEST_AVAILABLE 1012 #define MAX_INSTRUMENT_CHANNELS (128 * 6) @@ -208,6 +209,7 @@ GLOBAL_LIST_INIT(announcer_keys, list( #define SFX_INDUSTRIAL_SCAN "industrial_scan" #define SFX_MALE_SIGH "male_sigh" #define SFX_FEMALE_SIGH "female_sigh" +#define SFX_WRITING_PEN "writing_pen" // Standard is 44.1khz #define MIN_EMOTE_PITCH 40000 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 121cf5a072d..756d78c1723 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -7,6 +7,11 @@ /// if it only allows one, and new instances just instead refresh the timer #define STATUS_EFFECT_REFRESH 3 +/// Use in status effect "duration" to make it last forever +#define STATUS_EFFECT_PERMANENT -1 +/// Use in status effect "tick_interval" to prevent it from calling tick() +#define STATUS_EFFECT_NO_TICK -1 + ///Processing flags - used to define the speed at which the status will work ///This is fast - 0.2s between ticks (I believe!) #define STATUS_EFFECT_FAST_PROCESS 0 diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm index f510d7a1a94..814c8fed9e5 100644 --- a/code/__DEFINES/surgery.dm +++ b/code/__DEFINES/surgery.dm @@ -93,6 +93,12 @@ #define SURGERY_REQUIRES_REAL_LIMB (1<<4) ///Will grant a bonus during surgery steps to users with TRAIT_MORBID while they're using tools with CRUEL_IMPLEMENT #define SURGERY_MORBID_CURIOSITY (1<<5) +/** + * Instead of checking if the tool used is an actual surgery tool to avoid accidentally whacking patients with the wrong tool, + * it'll check if it has a defined tool behaviour instead. Useful for surgeries that use mechanical tools instead of medical ones, + * like hardware manipulation. + */ +#define SURGERY_CHECK_TOOL_BEHAVIOUR (1<<6) ///Return true if target is not in a valid body position for the surgery #define IS_IN_INVALID_SURGICAL_POSITION(target, surgery) ((surgery.surgery_flags & SURGERY_REQUIRE_RESTING) && (target.mobility_flags & MOBILITY_LIEDOWN && target.body_position != LYING_DOWN)) diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index f71ac7e3829..73647dc1168 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -57,8 +57,6 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using #define SATURDAY "Sat" #define SUNDAY "Sun" -#define INFINITE -1 // -1 is commonly used to indicate an infinite time duration - #define MILLISECONDS *0.01 #define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some fucking reason diff --git a/code/__DEFINES/tracy.dm b/code/__DEFINES/tracy.dm new file mode 100644 index 00000000000..0a9ab8d68ee --- /dev/null +++ b/code/__DEFINES/tracy.dm @@ -0,0 +1,5 @@ +/// File path used for the "enable tracy next round" functionality +#define TRACY_ENABLE_PATH "data/enable_tracy" + +/// The DLL path for byond-tracy. +#define TRACY_DLL_PATH (world.system_type == MS_WINDOWS ? "prof.dll" : "./libprof.so") diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 392140a787c..fd0560fdc55 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -393,6 +393,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_DETECT_STORM "detect_storm" #define TRAIT_PRIMITIVE "primitive" #define TRAIT_GUNFLIP "gunflip" +/// eignore blindness or blurriness or nearsightedness +#define TRAIT_SIGHT_BYPASS "perfect_sight" +/// ignore traumas that make you 'hallucinate' something +#define TRAIT_PERCEPTUAL_TRAUMA_BYPASS "trauma_bypass" +/// mob is immune to hallucinations +#define TRAIT_HALLUCINATION_IMMUNE "hallucination_immune" /// Increases chance of getting special traumas, makes them harder to cure #define TRAIT_SPECIAL_TRAUMA_BOOST "special_trauma_boost" #define TRAIT_SPACEWALK "spacewalk" @@ -729,6 +735,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Mobs won't slip on a wet turf while it has this trait #define TRAIT_TURF_IGNORE_SLIPPERY "turf_ignore_slippery" +///failsafe for whether an item with the beauty element is influencing the beauty of the area of not. +#define TRAIT_BEAUTY_APPLIED "beauty_applied" + /// Mobs with this trait can't send the mining shuttle console when used outside the station itself #define TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION "forbid_mining_shuttle_console_outside_station" @@ -797,8 +806,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ROD_ATTRACT_SHINY_LOVERS "rod_attract_shiny_lovers" /// This rod can be used to fish on lava #define TRAIT_ROD_LAVA_USABLE "rod_lava_usable" -/// Stuff that can go inside fish cases -#define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile" +/// Stuff that can go inside fish cases and aquariums +#define TRAIT_AQUARIUM_CONTENT "aquarium_content" /// If the item can be used as a bit. #define TRAIT_FISHING_BAIT "fishing_bait" /// This bait will kill any fish that doesn't have it on its favorite_bait list @@ -816,6 +825,19 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai * instead, it'll allow the fishing dud to be there unless there's at least one fish that likes the bait */ #define TRAIT_BAIT_ALLOW_FISHING_DUD "bait_dont_affect_fishing_dud" +/** + * This location has the aquarium component. Not much different than a GetComponent() + * disguised as an 'is_x' macro, but I don't have to hide anything here. + * I just don't want a confusing 'is_aquarium(A)' macro which people think it's interchangable with + * an 'istype(A, /obj/structure/aquarium)' when it's the component what truly matters. + */ +#define TRAIT_IS_AQUARIUM "is_aquarium" +/// A location (probably aquarium) that amplifies the zaps of electricity-generating fish. +#define TRAIT_BIOELECTRIC_GENERATOR "bioelectric_generator" +/// A location (likely aquarium) that doesn't allow fish to growth and reproduce +#define TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH "stop_fish_reproduction_and_growth" +/// This is an aquarium with an open panel +#define TRAIT_AQUARIUM_PANEL_OPEN "aquarium_panel_open" /// Plants that were mutated as a result of passive instability, not a mutation threshold. #define TRAIT_PLANT_WILDMUTATE "wildmutation" /// If you hit an APC with exposed internals with this item it will try to shock you @@ -1346,6 +1368,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait which allows mobs to parry mining mob projectiles #define TRAIT_MINING_PARRYING "mining_parrying" +///Mob that can merge stacks in its contents +#define TRAIT_MOB_MERGE_STACKS "mob_merge_stacks" + +//things that can pass through airlocks +#define TRAIT_FIREDOOR_OPENER "firedoor_opener" ///Trait which silences all chemical reactions in its container #define TRAIT_SILENT_REACTIONS "silent_reactions" diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 9ee97b9f157..4deb3925141 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -165,7 +165,6 @@ #define STICKY_NODROP "sticky-nodrop" #define SKILLCHIP_TRAIT "skillchip" #define SKILL_TRAIT "skill" -#define BUSY_FLOORBOT_TRAIT "busy-floorbot" #define PULLED_WHILE_SOFTCRIT_TRAIT "pulled-while-softcrit" #define LOCKED_BORG_TRAIT "locked-borg" /// trait associated to not having locomotion appendages nor the ability to fly or float @@ -305,5 +304,11 @@ /// Trait added by style component #define STYLE_TRAIT "style" +/// Trait added by a xenobio console +#define XENOBIO_CONSOLE_TRAIT "xenobio_console_trait" + /// Trait from an engraving #define ENGRAVED_TRAIT "engraved" + +/// From the aquarium component +#define AQUARIUM_TRAIT "aquarium" diff --git a/code/__HELPERS/_planes.dm b/code/__HELPERS/_planes.dm index 87e9ff2d1ce..1bebefa2fa8 100644 --- a/code/__HELPERS/_planes.dm +++ b/code/__HELPERS/_planes.dm @@ -92,12 +92,13 @@ GLOBAL_LIST_INIT(topdown_planes, list( "[FLOOR_PLANE]" = TRUE, )) +#define IS_TOPDOWN_PLANE(plane) GLOB.topdown_planes["[PLANE_TO_TRUE(plane)]"] + /// Checks if a passed in MA or atom is allowed to have its current plane/layer matchup /proc/check_topdown_validity(mutable_appearance/thing_to_check) if(istype(thing_to_check, /atom/movable/screen/plane_master)) return - var/topdown_plane = GLOB.topdown_planes["[PLANE_TO_TRUE(thing_to_check.plane)]"] - if(topdown_plane) + if(IS_TOPDOWN_PLANE(thing_to_check.plane)) if(thing_to_check.layer - TOPDOWN_LAYER < 0 || thing_to_check.layer >= BACKGROUND_LAYER) stack_trace("[thing_to_check] ([thing_to_check.type]) was expected to have a TOPDOWN_LAYER layer due to its plane, but it DID NOT! layer: ([thing_to_check.layer]) plane: ([thing_to_check.plane])") else if(thing_to_check.layer - TOPDOWN_LAYER >= 0 && thing_to_check.layer < BACKGROUND_LAYER) diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm index d54b29b3f4a..e94d58dd693 100644 --- a/code/__HELPERS/atoms.dm +++ b/code/__HELPERS/atoms.dm @@ -316,6 +316,46 @@ rough example of the "cone" made by the 3 dirs checked loc = loc.loc return null +/** + * Line of sight check! + * Spawns a dummy object and then iterates through each turf to see if it's blocked by something not handled by pass_args. + * Contains a mid_los_check, meant to be overriden by subtypes. + * args: + * * user = Origin to start at. + * * target = End point. + * * pass_args = pass_flags given to dummy object to allow it to ignore certain types of blockades. + */ +/proc/los_check(atom/movable/user, mob/target, pass_args = PASSTABLE|PASSGLASS|PASSGRILLE, datum/callback/mid_check) + var/turf/user_turf = user.loc + if(!istype(user_turf)) + return FALSE + var/obj/dummy = new(user_turf) + dummy.pass_flags |= pass_args //Grille/Glass so it can be used through common windows + var/turf/previous_step = user_turf + var/first_step = TRUE + for(var/turf/next_step as anything in (get_line(user_turf, target) - user_turf)) + if(first_step) + for(var/obj/blocker in user_turf) + if(!blocker.density || !(blocker.flags_1 & ON_BORDER_1)) + continue + if(blocker.CanPass(dummy, get_dir(user_turf, next_step))) + continue + return FALSE // Could not leave the first turf. + first_step = FALSE + if(next_step.density) + qdel(dummy) + return FALSE + for(var/atom/movable/movable as anything in next_step) + if(!movable.CanPass(dummy, get_dir(next_step, previous_step))) + qdel(dummy) + return FALSE + if(mid_check?.Invoke(user, target, pass_args, next_step, dummy) == FALSE) // specify false as it may return null if there's no check + qdel(dummy) + return FALSE + previous_step = next_step + qdel(dummy) + return TRUE + ///Returns true if the src countain the atom target /atom/proc/contains(atom/target) if(!target) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 5befe8ca902..9484282262b 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -75,8 +75,8 @@ return !player_mind || !player_mind.current || !player_mind.current.client || player_mind.current.client.is_afk() ///Return an object with a new maptext (not currently in use) -/proc/screen_text(obj/object_to_change, maptext = "", screen_loc = "CENTER-7,CENTER-7", maptext_height = 480, maptext_width = 480) - if(!isobj(object_to_change)) +/proc/screen_text(atom/movable/object_to_change, maptext = "", screen_loc = "CENTER-7,CENTER-7", maptext_height = 480, maptext_width = 480) + if(!istype(object_to_change)) object_to_change = new /atom/movable/screen/text() object_to_change.maptext = MAPTEXT(maptext) object_to_change.maptext_height = maptext_height diff --git a/code/__HELPERS/hud.dm b/code/__HELPERS/hud.dm new file mode 100644 index 00000000000..40a12767d28 --- /dev/null +++ b/code/__HELPERS/hud.dm @@ -0,0 +1,17 @@ +/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5) + var/x_off = IS_LEFT_INDEX(i) ? 0 : -1 + var/y_off = round((i-1) / 2) + return"CENTER+[x_off]:16,SOUTH+[y_off]:5" + +/proc/ui_equip_position(mob/M) + var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5) + return "CENTER:-16,SOUTH+[y_off+1]:5" + +/proc/ui_swaphand_position(mob/M, which = LEFT_HANDS) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5) + var/x_off = which == LEFT_HANDS ? -1 : 0 + var/y_off = round((M.held_items.len-1) / 2) + return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5" + +/proc/ui_perk_position(perk_count) + var/y_off = perk_count < 1 ? 0 : perk_count/2 + return "WEST+0.5:12,NORTH-2-[y_off]:20" diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 92ebff1cc4e..f3a2e4bd375 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -561,7 +561,7 @@ GLOBAL_LIST_INIT(skin_tone_names, list( var/list/sortmob = sort_names(GLOB.mob_list) for(var/mob/living/silicon/ai/mob_to_sort in sortmob) moblist += mob_to_sort - for(var/mob/camera/mob_to_sort in sortmob) + for(var/mob/eye/mob_to_sort in sortmob) moblist += mob_to_sort for(var/mob/living/silicon/pai/mob_to_sort in sortmob) moblist += mob_to_sort diff --git a/code/__HELPERS/paths/jps.dm b/code/__HELPERS/paths/jps.dm index fbdccdef12c..208888d21ba 100644 --- a/code/__HELPERS/paths/jps.dm +++ b/code/__HELPERS/paths/jps.dm @@ -194,7 +194,7 @@ if(!CAN_STEP(lag_turf, current_turf, simulated_only, pass_info, avoid)) return - if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist))) + if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist) && !diagonally_blocked(current_turf, end))) var/datum/jps_node/final_node = new(current_turf, parent_node, steps_taken) found_turfs[current_turf] = TRUE if(parent_node) // if this is a direct lateral scan we can wrap up, if it's a subscan from a diag, we need to let the diag make their node first, then finish @@ -256,7 +256,7 @@ if(!CAN_STEP(lag_turf, current_turf, simulated_only, pass_info, avoid)) return - if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist))) + if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist) && !diagonally_blocked(current_turf, end))) var/datum/jps_node/final_node = new(current_turf, parent_node, steps_taken) found_turfs[current_turf] = TRUE unwind_path(final_node) diff --git a/code/__HELPERS/paths/path.dm b/code/__HELPERS/paths/path.dm index 9530a545235..950cd190684 100644 --- a/code/__HELPERS/paths/path.dm +++ b/code/__HELPERS/paths/path.dm @@ -346,7 +346,7 @@ src.can_ventcrawl = HAS_TRAIT(living_construct, TRAIT_VENTCRAWLER_ALWAYS) || HAS_TRAIT(living_construct, TRAIT_VENTCRAWLER_NUDE) src.mob_size = living_construct.mob_size src.incorporeal_move = living_construct.incorporeal_move - if(iscameramob(construct_from)) + if(iseyemob(construct_from)) src.camera_type = construct_from.type src.is_bot = isbot(construct_from) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index c915a76529a..70e9b10309c 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -12,7 +12,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list()) var/json_file = file("[GLOB.log_directory]/round_end_data.json") // All but npcs sublists and ghost category contain only mobs with minds var/list/file_data = list("escapees" = list("humans" = list(), "silicons" = list(), "others" = list(), "npcs" = list()), "abandoned" = list("humans" = list(), "silicons" = list(), "others" = list(), "npcs" = list()), "ghosts" = list(), "additional data" = list()) - var/num_survivors = 0 //Count of non-brain non-camera mobs with mind that are alive + var/num_survivors = 0 //Count of non-brain non-eye mobs with mind that are alive var/num_escapees = 0 //Above and on centcom z var/num_shuttle_escapees = 0 //Above and on escape shuttle var/list/area/shuttle_areas @@ -32,7 +32,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list()) if(M.mind) count_only = FALSE mob_data["ckey"] = M.mind.key - if(M.stat != DEAD && !isbrain(M) && !iscameramob(M)) + if(M.stat != DEAD && !isbrain(M) && !iseyemob(M)) num_survivors++ if(EMERGENCY_ESCAPED_OR_ENDGAMED && (M.onCentCom() || M.onSyndieBase())) num_escapees++ diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm index a2c47e87c0a..050c569cbdf 100644 --- a/code/__HELPERS/spatial_info.dm +++ b/code/__HELPERS/spatial_info.dm @@ -485,3 +485,19 @@ return center //Offer the center only as a default case when we don't have a valid circle. return peel +///check if 2 diagonal turfs are blocked by dense objects +/proc/diagonally_blocked(turf/our_turf, turf/dest_turf) + if(get_dist(our_turf, dest_turf) != 1) + return FALSE + var/direction_to_turf = get_dir(dest_turf, our_turf) + if(!ISDIAGONALDIR(direction_to_turf)) + return FALSE + for(var/direction_check in GLOB.cardinals) + if(!(direction_check & direction_to_turf)) + continue + var/turf/test_turf = get_step(dest_turf, direction_check) + if(isnull(test_turf)) + continue + if(!test_turf.is_blocked_turf(exclude_mobs = TRUE)) + return FALSE + return TRUE diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index aba6fcb976d..b573fd38981 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -270,7 +270,7 @@ if(!filter_name_ic(trimmed)) // Contains IC chat prohibited words return - return trimtext(trimmed) + return trim(trimmed) /// Helper proc to check if a name is valid for the IC filter @@ -357,7 +357,7 @@ /proc/trim(text, max_length) if(max_length) text = copytext_char(text, 1, max_length) - return trimtext(text) + return trimtext(text) || "" //users expect atleast an empty string //Returns a string with the first element of the string capitalized. /proc/capitalize(t) diff --git a/code/__HELPERS/view.dm b/code/__HELPERS/view.dm index 139bdedc425..61aaed88350 100644 --- a/code/__HELPERS/view.dm +++ b/code/__HELPERS/view.dm @@ -1,7 +1,5 @@ -/proc/getviewsize(view) - if(!view) // Just to avoid any runtimes that could otherwise cause constant disconnect loops. - stack_trace("Missing value for 'view' in getviewsize(), defaulting to world.view!") - view = world.view +/proc/getviewsize(view = world.view) + SHOULD_BE_PURE(TRUE) if(isnum(view)) var/totalviewrange = (view < 0 ? -1 : 1) + 2 * view diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 3fe456e488e..2bd9309406a 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -81,13 +81,17 @@ #endif // REFERENCE_TRACKING_STANDARD // If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between -// #define UNIT_TESTS +//#define UNIT_TESTS // If this is uncommented, will attempt to load and initialize prof.dll/libprof.so by default. // Even if it's not defined, you can pass "tracy" via -params in order to try to load it. -// We do not ship byond-tracy. Build it yourself here: https://github.com/mafemergency/byond-tracy/ +// We do not ship byond-tracy. Build it yourself here: https://github.com/mafemergency/byond-tracy, +// or the fork which writes profiling data to a file: https://github.com/ParadiseSS13/byond-tracy // #define USE_BYOND_TRACY +// If uncommented, will display info about byond-tracy's status in the MC tab. +// #define MC_TAB_TRACY_INFO + // If defined, we will compile with FULL timer debug info, rather then a limited scope // Be warned, this increases timer creation cost by 5x // #define TIMER_DEBUG diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 8d9402cb82e..c5b622572bd 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -589,6 +589,9 @@ DEFINE_BITFIELD(fish_flags, list( "FISH_DO_FLOP_ANIM" = FISH_DO_FLOP_ANIM, "FISH_FLAG_PETTED" = FISH_FLAG_PETTED, "FISH_FLAG_EXPERIMENT_SCANNABLE" = FISH_FLAG_EXPERIMENT_SCANNABLE, + "FISH_FLAG_UPDATING_SIZE_AND_WEIGHT" = FISH_FLAG_OVERPOPULATED, + "FISH_FLAG_SAFE_TEMPERATURE" = FISH_FLAG_SAFE_TEMPERATURE, + "FISH_FLAG_SAFE_FLUID" = FISH_FLAG_SAFE_FLUID, )) DEFINE_BITFIELD(bot_mode_flags, list( diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index dd5e9cb442a..6bbe60f7d07 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -210,6 +210,7 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items /obj/item/storage/box/clown = 1, /obj/item/weaponcrafting/receiver = 1, /obj/item/book/granter/crafting_recipe/death_sandwich = 1, + /obj/item/survivalcapsule/fishing = 1, ) = 8, list(//medical and chemicals diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index d91eec6e1bc..5ece846e84d 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -59,7 +59,7 @@ GLOBAL_LIST_EMPTY(available_ai_shells) GLOBAL_LIST_INIT(simple_animals, 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(aiEyes) +GLOBAL_LIST_EMPTY(camera_eyes) GLOBAL_LIST_EMPTY(suit_sensors_list) //all people with suit sensors on /// All alive mobs with clients. diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 32929ccdb9f..f2faf6ae81f 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -32,9 +32,13 @@ GLOBAL_LIST_EMPTY(deliverybeacontags) GLOBAL_LIST_EMPTY_TYPED(singularities, /datum/component/singularity) GLOBAL_LIST_EMPTY(item_to_design_list) + /// list of all surgeries by name, associated with their path. GLOBAL_LIST_INIT(surgeries_list, init_surgeries()) +/// list of all surgery steps, associated by their path. +GLOBAL_LIST_INIT(surgery_steps, init_subtypes_w_path_keys(/datum/surgery_step, list())) + /// Global list of all non-cooking related crafting recipes. GLOBAL_LIST_EMPTY(crafting_recipes) /// This is a global list of typepaths, these typepaths are atoms or reagents that are associated with crafting recipes. @@ -80,3 +84,22 @@ GLOBAL_LIST_EMPTY(roundstart_station_borgcharger_areas) /// List of area names of roundstart station mech rechargers, for the low charge/no charge mech screen alert tooltips. GLOBAL_LIST_EMPTY(roundstart_station_mechcharger_areas) + +// List of organ typepaths that are not unit test-able, and shouldn't be spawned by some things, such as certain class prototypes. +GLOBAL_LIST_INIT(prototype_organs, typecacheof(list( + /obj/item/organ, + /obj/item/organ/wings, + /obj/item/organ/wings/functional, + /obj/item/organ/wings/functional/moth, + /obj/item/organ/cyberimp, + /obj/item/organ/cyberimp/brain, + /obj/item/organ/cyberimp/mouth, + /obj/item/organ/cyberimp/arm, + /obj/item/organ/cyberimp/chest, + /obj/item/organ/cyberimp/eyes, + /obj/item/organ/alien, + /obj/item/organ/brain/dullahan, + /obj/item/organ/ears/dullahan, + /obj/item/organ/tongue/dullahan, + /obj/item/organ/eyes/dullahan, +), only_root_path = TRUE)) diff --git a/code/_globalvars/lists/quirks.dm b/code/_globalvars/lists/quirks.dm index 825c217d055..5bd44e667f9 100644 --- a/code/_globalvars/lists/quirks.dm +++ b/code/_globalvars/lists/quirks.dm @@ -118,7 +118,6 @@ GLOBAL_LIST_INIT(quirk_chipped_choice, list( "GENUINE ID Appraisal Now!" = /obj/item/skillchip/appraiser, "Le S48R4G3" = /obj/item/skillchip/sabrage, "Integrated Intuitive Thinking and Judging" = /obj/item/skillchip/intj, - "F0RC3 4DD1CT10N" = /obj/item/skillchip/drunken_brawler, "\"Space Station 13: The Musical\"" = /obj/item/skillchip/musical, "Mast-Angl-Er" = /obj/item/skillchip/master_angler, "Kommand" = /obj/item/skillchip/big_pointer, diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 4317da95d49..2c8d713a44b 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( /atom = list( "TRAIT_AI_PAUSED" = TRAIT_AI_PAUSED, "TRAIT_BANNED_FROM_CARGO_SHUTTLE" = TRAIT_BANNED_FROM_CARGO_SHUTTLE, + "TRAIT_BEAUTY_APPLIED" = TRAIT_BEAUTY_APPLIED, "TRAIT_BEING_SHOCKED" = TRAIT_BEING_SHOCKED, "TRAIT_CATCH_AND_RELEASE" = TRAIT_CATCH_AND_RELEASE, "TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED, @@ -35,8 +36,10 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /atom/movable = list( "TRAIT_ACTIVE_STORAGE" = TRAIT_ACTIVE_STORAGE, + "TRAIT_AQUARIUM_PANEL_OPEN" = TRAIT_AQUARIUM_PANEL_OPEN, "TRAIT_AREA_SENSITIVE" = TRAIT_AREA_SENSITIVE, "TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE, + "TRAIT_BIOELECTRIC_GENERATOR" = TRAIT_BIOELECTRIC_GENERATOR, "TRAIT_BLOCKING_EXPLOSIVES" = TRAIT_BLOCKING_EXPLOSIVES, "TRAIT_BOULDER_BREAKER" = TRAIT_BOULDER_BREAKER, "TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC, @@ -44,13 +47,13 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_COMBAT_MODE_SKIP_INTERACTION" = TRAIT_COMBAT_MODE_SKIP_INTERACTION, "TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP, "TRAIT_VALID_DNA_INFUSION" = TRAIT_VALID_DNA_INFUSION, - "TRAIT_FISH_CASE_COMPATIBILE" = TRAIT_FISH_CASE_COMPATIBILE, "TRAIT_FROZEN" = TRAIT_FROZEN, "TRAIT_HAS_LABEL" = TRAIT_HAS_LABEL, "TRAIT_HEARING_SENSITIVE" = TRAIT_HEARING_SENSITIVE, "TRAIT_HYPERSPACED" = TRAIT_HYPERSPACED, "TRAIT_IMMERSED" = TRAIT_IMMERSED, "TRAIT_IRRADIATED" = TRAIT_IRRADIATED, + "TRAIT_IS_AQUARIUM" = TRAIT_IS_AQUARIUM, "TRAIT_LAVA_IMMUNE" = TRAIT_LAVA_IMMUNE, "TRAIT_MOVE_FLOATING" = TRAIT_MOVE_FLOATING, "TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING, @@ -72,6 +75,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION, "TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE, "TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC, + "TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH" = TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, "TRAIT_TELEKINESIS_CONTROLLED" = TRAIT_TELEKINESIS_CONTROLLED, "TRAIT_UNDERFLOOR" = TRAIT_UNDERFLOOR, "TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE, @@ -277,6 +281,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_GREENTEXT_CURSED" = TRAIT_GREENTEXT_CURSED, "TRAIT_GUNFLIP" = TRAIT_GUNFLIP, "TRAIT_GUN_NATURAL" = TRAIT_GUN_NATURAL, + "TRAIT_HALLUCINATION_IMMUNE" = TRAIT_HALLUCINATION_IMMUNE, "TRAIT_HALT_RADIATION_EFFECTS" = TRAIT_HALT_RADIATION_EFFECTS, "TRAIT_HANDS_BLOCKED" = TRAIT_HANDS_BLOCKED, "TRAIT_HARDLY_WOUNDED" = TRAIT_HARDLY_WOUNDED, @@ -342,6 +347,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_MOB_EATER" = TRAIT_MOB_EATER, "TRAIT_MOB_HATCHED" = TRAIT_MOB_HATCHED, "TRAIT_MOB_HIDE_HAPPINESS" = TRAIT_MOB_HIDE_HAPPINESS, + "TRAIT_MOB_MERGE_STACKS" = TRAIT_MOB_MERGE_STACKS, "TRAIT_MOB_TIPPED" = TRAIT_MOB_TIPPED, "TRAIT_MORBID" = TRAIT_MORBID, "TRAIT_MULTIZ_SUIT_SENSORS" = TRAIT_MULTIZ_SUIT_SENSORS, @@ -418,6 +424,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_PASSTABLE" = TRAIT_PASSTABLE, "TRAIT_PASSWINDOW" = TRAIT_PASSWINDOW, "TRAIT_PERCEIVED_AS_CLOWN" = TRAIT_PERCEIVED_AS_CLOWN, + "TRAIT_PERCEPTUAL_TRAUMA_BYPASS" = TRAIT_PERCEPTUAL_TRAUMA_BYPASS, "TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER, "TRAIT_PERMANENTLY_MORTAL" = TRAIT_PERMANENTLY_MORTAL, "TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER, @@ -467,6 +474,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SHAVED" = TRAIT_SHAVED, "TRAIT_SHIFTY_EYES" = TRAIT_SHIFTY_EYES, "TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE, + "TRAIT_SIGHT_BYPASS" = TRAIT_SIGHT_BYPASS, "TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG, "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, "TRAIT_SILICON_ACCESS" = TRAIT_SILICON_ACCESS, @@ -575,6 +583,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, + "TRAIT_AQUARIUM_CONTENT" = TRAIT_AQUARIUM_CONTENT, "TRAIT_BAIT_ALLOW_FISHING_DUD" = TRAIT_BAIT_ALLOW_FISHING_DUD, "TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE, "TRAIT_BAKEABLE" = TRAIT_BAKEABLE, @@ -584,6 +593,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CONTRABAND_BLOCKER" = TRAIT_CONTRABAND_BLOCKER, "TRAIT_CUSTOM_TAP_SOUND" = TRAIT_CUSTOM_TAP_SOUND, "TRAIT_DANGEROUS_OBJECT" = TRAIT_DANGEROUS_OBJECT, + "TRAIT_FIREDOOR_OPENER" = TRAIT_FIREDOOR_OPENER, "TRAIT_FISHING_BAIT" = TRAIT_FISHING_BAIT, "TRAIT_FOOD_BBQ_GRILLED" = TRAIT_FOOD_BBQ_GRILLED, "TRAIT_GERM_SENSITIVE" = TRAIT_GERM_SENSITIVE, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 9d1b47800b8..7b7ca780a54 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -100,6 +100,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV, "TRAIT_EXPERT_FISHER" = TRAIT_EXPERT_FISHER, "TRAIT_EXTROVERT" = TRAIT_EXTROVERT, + "TRAIT_EVIL" = TRAIT_EVIL, "TRAIT_FAKEDEATH" = TRAIT_FAKEDEATH, "TRAIT_FASTMED" = TRAIT_FASTMED, "TRAIT_FAST_CUFFING" = TRAIT_FAST_CUFFING, diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index ea4de00f7d4..7410d909f21 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -107,6 +107,8 @@ name = "Alert" desc = "Something seems to have gone wrong with this alert, so report this bug please" mouse_opacity = MOUSE_OPACITY_ICON + /// do we glow to represent we do stuff when clicked + var/clickable_glow = FALSE var/timeout = 0 //If set to a number, this alert will clear itself after that many deciseconds var/severity = 0 var/alerttooltipstyle = "" @@ -116,6 +118,10 @@ /// Boolean. If TRUE, the Click() proc will attempt to Click() on the master first if there is a master. var/click_master = TRUE +/atom/movable/screen/alert/Initialize(mapload, datum/hud/hud_owner) + . = ..() + if(clickable_glow) + add_filter("clickglow", 2, outline_filter(color = COLOR_GOLD, size = 1)) /atom/movable/screen/alert/MouseEntered(location,control,params) . = ..() @@ -237,6 +243,7 @@ name = "Mind Control" desc = "Your mind has been hijacked! Click to view the mind control command." icon_state = ALERT_MIND_CONTROL + clickable_glow = TRUE var/command /atom/movable/screen/alert/mind_control/Click() @@ -250,6 +257,7 @@ desc = "Something got lodged into your flesh and is causing major bleeding. It might fall out with time, but surgery is the safest way. \ If you're feeling frisky, examine yourself and click the underlined item to pull the object out." icon_state = ALERT_EMBEDDED_OBJECT + clickable_glow = TRUE /atom/movable/screen/alert/embeddedobject/Click() . = ..() @@ -292,6 +300,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." name = "On Fire" desc = "You're on fire. Stop, drop and roll to put the fire out or move to a vacuum area." icon_state = "fire" + clickable_glow = TRUE /atom/movable/screen/alert/fire/Click() . = ..() @@ -310,6 +319,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /atom/movable/screen/alert/give // information set when the give alert is made icon_state = "default" + clickable_glow = TRUE /// The offer we're linked to, yes this is suspiciously like a status effect alert var/datum/status_effect/offering/offer /// Additional text displayed in the description of the alert. @@ -481,6 +491,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." name = "Succumb" desc = "Shuffle off this mortal coil." icon_state = ALERT_SUCCUMB + clickable_glow = TRUE var/static/list/death_titles = list( "Goodnight, Sweet Prince", "Game Over, Man", @@ -492,19 +503,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." "All Good Things Must End" ) -/atom/movable/screen/alert/succumb/Initialize(mapload, datum/hud/hud_owner) - . = ..() - register_context() - -/atom/movable/screen/alert/succumb/add_context(atom/source, list/context, obj/item/held_item, mob/user) - context[SCREENTIP_CONTEXT_LMB] = "Succumb With Last Words" - context[SCREENTIP_CONTEXT_RMB] = "Succumb Silently" - return CONTEXTUAL_SCREENTIP_SET - -#define FASTSUCCUMB_YES "Yes" -#define FASTSUCCUMB_WAIT "Wait, I have last words!" -#define FASTSUCCUMB_NO "No" - /atom/movable/screen/alert/succumb/Click(location, control, params) . = ..() if(!.) @@ -515,17 +513,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." var/title = pick(death_titles) - if(LAZYACCESS(params2list(params), RIGHT_CLICK)) - //Succumbing without a message - var/choice = tgui_alert(living_owner, "Are you sure you want to succumb?", title, list(FASTSUCCUMB_YES, FASTSUCCUMB_WAIT, FASTSUCCUMB_NO)) - switch(choice) - if(FASTSUCCUMB_NO, null) - return - if(FASTSUCCUMB_YES) - living_owner.succumb() - return - //if(FASTSUCCUMB_WAIT), we continue to last words - //Succumbing with a message var/last_whisper = tgui_input_text(usr, "Do you have any last words?", title, max_length = CHAT_MESSAGE_MAX_LENGTH, encode = FALSE) // saycode already handles sanitization if(isnull(last_whisper)) @@ -534,9 +521,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." living_owner.say("#[last_whisper]") living_owner.succumb(whispered = length(last_whisper) > 0) -#undef FASTSUCCUMB_NO -#undef FASTSUCCUMB_WAIT -#undef FASTSUCCUMB_YES //ALIENS /atom/movable/screen/alert/alien_plas @@ -816,6 +800,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." additional processing time to unlock more malfunction abilities." icon_state = ALERT_HACKING_APC timeout = 60 SECONDS + clickable_glow = TRUE var/atom/target = null /atom/movable/screen/alert/hackingapc/Click() @@ -843,6 +828,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." desc = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!" icon_state = "template" timeout = 300 + clickable_glow = TRUE /atom/movable/screen/alert/revival/Click() . = ..() @@ -856,6 +842,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." desc = "This can be clicked on to perform an action." icon_state = "template" timeout = 30 SECONDS + clickable_glow = TRUE /// Weakref to the target atom to use the action on var/datum/weakref/target_ref /// If we want to interact on click rather than jump/orbit @@ -863,6 +850,8 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /atom/movable/screen/alert/notify_action/Click() . = ..() + if(!.) + return var/atom/target = target_ref?.resolve() if(isnull(target) || !isobserver(owner) || target == owner) @@ -1031,6 +1020,10 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." name = "Buckled" desc = "You've been buckled to something. Click the alert to unbuckle unless you're handcuffed." icon_state = ALERT_BUCKLED + clickable_glow = TRUE + +/atom/movable/screen/alert/restrained + clickable_glow = TRUE /atom/movable/screen/alert/restrained/handcuffed name = "Handcuffed" @@ -1078,6 +1071,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." name = "Knotted Shoes" desc = "Someone tied your shoelaces together! Click the alert or your shoes to undo the knot." icon_state = ALERT_SHOES_KNOT + clickable_glow = TRUE /atom/movable/screen/alert/shoes/Click() . = ..() @@ -1096,6 +1090,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." name = "Unpossess" desc = "You are possessing an object. Click this alert to unpossess it." icon_state = "buckled" + clickable_glow = TRUE /atom/movable/screen/alert/unpossess_object/Click() . = ..() diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index be860caa1f0..b8e9800fdf9 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -17,7 +17,7 @@ /atom/movable/screen/blob/jump_to_node/Click() if(!isovermind(usr)) return FALSE - var/mob/camera/blob/blob = usr + var/mob/eye/blob/blob = usr blob.jump_to_node() /atom/movable/screen/blob/jump_to_core @@ -27,7 +27,7 @@ /atom/movable/screen/blob/jump_to_core/MouseEntered(location,control,params) if(hud?.mymob && isovermind(hud.mymob)) - var/mob/camera/blob/B = hud.mymob + var/mob/eye/blob/B = hud.mymob if(!B.placed) name = "Place Blob Core" desc = "Attempt to place your blob core at this location." @@ -39,7 +39,7 @@ /atom/movable/screen/blob/jump_to_core/Click() if(!isovermind(usr)) return FALSE - var/mob/camera/blob/blob = usr + var/mob/eye/blob/blob = usr if(!blob.placed) blob.place_blob_core(BLOB_NORMAL_PLACEMENT) blob.transport_core() @@ -58,7 +58,7 @@ /atom/movable/screen/blob/blobbernaut/Click() if(!isovermind(usr)) return FALSE - var/mob/camera/blob/blob = usr + var/mob/eye/blob/blob = usr blob.create_blobbernaut() /atom/movable/screen/blob/resource_blob @@ -75,7 +75,7 @@ /atom/movable/screen/blob/resource_blob/Click() if(!isovermind(usr)) return FALSE - var/mob/camera/blob/blob = usr + var/mob/eye/blob/blob = usr blob.create_special(BLOB_STRUCTURE_RESOURCE_COST, /obj/structure/blob/special/resource, BLOB_RESOURCE_MIN_DISTANCE, TRUE) /atom/movable/screen/blob/node_blob @@ -92,7 +92,7 @@ /atom/movable/screen/blob/node_blob/Click() if(!isovermind(usr)) return FALSE - var/mob/camera/blob/blob = usr + var/mob/eye/blob/blob = usr blob.create_special(BLOB_STRUCTURE_NODE_COST, /obj/structure/blob/special/node, BLOB_NODE_MIN_DISTANCE, FALSE) /atom/movable/screen/blob/factory_blob @@ -109,7 +109,7 @@ /atom/movable/screen/blob/factory_blob/Click() if(!isovermind(usr)) return FALSE - var/mob/camera/blob/blob = usr + var/mob/eye/blob/blob = usr blob.create_special(BLOB_STRUCTURE_FACTORY_COST, /obj/structure/blob/special/factory, BLOB_FACTORY_MIN_DISTANCE, TRUE) /atom/movable/screen/blob/readapt_strain @@ -120,7 +120,7 @@ /atom/movable/screen/blob/readapt_strain/MouseEntered(location,control,params) if(hud?.mymob && isovermind(hud.mymob)) - var/mob/camera/blob/B = hud.mymob + var/mob/eye/blob/B = hud.mymob if(B.free_strain_rerolls) name = "[initial(name)] (FREE)" desc = "Randomly rerolls your strain for free." @@ -131,7 +131,7 @@ /atom/movable/screen/blob/readapt_strain/Click() if(isovermind(usr)) - var/mob/camera/blob/B = usr + var/mob/eye/blob/B = usr B.strain_reroll() /atom/movable/screen/blob/relocate_core @@ -147,7 +147,7 @@ /atom/movable/screen/blob/relocate_core/Click() if(isovermind(usr)) - var/mob/camera/blob/B = usr + var/mob/eye/blob/B = usr B.relocate_core() /datum/hud/blob_overmind/New(mob/owner) @@ -182,11 +182,11 @@ static_inventory += using using = new /atom/movable/screen/blob/node_blob(null, src) - using.screen_loc = ui_hand_position(2) + using.screen_loc = ui_hand_position(RIGHT_HANDS) static_inventory += using using = new /atom/movable/screen/blob/factory_blob(null, src) - using.screen_loc = ui_hand_position(1) + using.screen_loc = ui_hand_position(LEFT_HANDS) static_inventory += using using = new /atom/movable/screen/blob/readapt_strain(null, src) diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 91b5c9b8e2a..43d90893d9a 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -156,9 +156,33 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/fullscreen) icon_state = "noise" /atom/movable/screen/fullscreen/high - icon = 'icons/hud/screen_gen.dmi' - screen_loc = "WEST,SOUTH to EAST,NORTH" icon_state = "druggy" + alpha = 255 + plane = LIGHTING_PLANE + layer = LIGHTING_ABOVE_ALL + 1 //Infinity plus one (not actually) + blend_mode = BLEND_MULTIPLY + +/atom/movable/screen/fullscreen/high/update_for_view(client_view) + + animate(src, flags = ANIMATION_END_NOW) //Stop all animations. + + . = ..() + + color = COLOR_MATRIX_IDENTITY //We convert it early to avoid a sudden weird jitter. + alpha = 0 + + animate(src, alpha = 255, time = 5 SECONDS) //Fade in. + + addtimer(CALLBACK(src, PROC_REF(start_hue_rotation)), 5 SECONDS) + +/atom/movable/screen/fullscreen/high/proc/start_hue_rotation() + animate(src, color = color_matrix_rotate_hue(1), loop = -1, time = 2 SECONDS) //Start the loop. + var/step_precision = 18 //Larger is more precise rotations. + for(var/current_step in 1 to step_precision - 1) //We do the -1 here because 360 == 0 when it comes to angles. + animate( + color = color_matrix_rotate_hue(current_step * 360/step_precision), + time = 2 SECONDS, + ) /atom/movable/screen/fullscreen/color_vision icon = 'icons/hud/screen_gen.dmi' diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index ba1d8f4565e..32a3f233c85 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -15,11 +15,11 @@ infodisplay += healths using = new /atom/movable/screen/guardian/manifest(null, src) - using.screen_loc = ui_hand_position(2) + using.screen_loc = ui_hand_position(RIGHT_HANDS) static_inventory += using using = new /atom/movable/screen/guardian/recall(null, src) - using.screen_loc = ui_hand_position(1) + using.screen_loc = ui_hand_position(LEFT_HANDS) static_inventory += using using = new owner.toggle_button_type(null, src) diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index f92bb4682e1..d04dafefeb0 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -151,8 +151,16 @@ GLOBAL_LIST_INIT(available_ui_styles, list( /datum/hud/proc/client_refresh(datum/source) SIGNAL_HANDLER - RegisterSignal(mymob.canon_client, COMSIG_CLIENT_SET_EYE, PROC_REF(on_eye_change)) - on_eye_change(null, null, mymob.canon_client.eye) + var/client/client = mymob.canon_client + if(client.rebuild_plane_masters) + var/new_relay_loc = (client.byond_version > 515) ? "1,1" : "CENTER" + for(var/group_key as anything in master_groups) + var/datum/plane_master_group/group = master_groups[group_key] + group.relay_loc = new_relay_loc + group.rebuild_plane_masters() + client.rebuild_plane_masters = FALSE + RegisterSignal(client, COMSIG_CLIENT_SET_EYE, PROC_REF(on_eye_change)) + on_eye_change(null, null, client.eye) /datum/hud/proc/clear_client(datum/source) SIGNAL_HANDLER @@ -497,7 +505,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/i = 1 for(var/atom/movable/screen/swap_hand/SH in static_inventory) - SH.screen_loc = ui_swaphand_position(mymob,!(i % 2) ? 2: 1) + SH.screen_loc = ui_swaphand_position(mymob, IS_RIGHT_INDEX(i) ? RIGHT_HANDS : LEFT_HANDS) i++ for(var/atom/movable/screen/human/equip/E in static_inventory) E.screen_loc = ui_equip_position(mymob) diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm index acfa5ee274c..582253e0b92 100644 --- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm +++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm @@ -362,7 +362,7 @@ /atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye) SIGNAL_HANDLER - if(!isaicamera(new_eye)) + if(!iscameramob(new_eye)) if(!force_hidden) hide_plane(source.mymob) return diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index efc7f1c3cfe..ec7070237d3 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -241,7 +241,7 @@ . = ..() if(!handcuff_overlay) - var/state = (!(held_index % 2)) ? "markus" : "gabrielle" + var/state = IS_RIGHT_INDEX(held_index) ? "markus" : "gabrielle" handcuff_overlay = mutable_appearance('icons/hud/screen_gen.dmi', state) if(!hud?.mymob) @@ -257,7 +257,7 @@ . += blocked_overlay if(held_index == hud.mymob.active_hand_index) - . += (held_index % 2) ? "lhandactive" : "rhandactive" + . += IS_LEFT_INDEX(held_index) ? "lhandactive" : "rhandactive" /atom/movable/screen/inventory/hand/Click(location, control, params) // At this point in client Click() code we have passed the 1/10 sec check and little else diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index 900ad59bde2..a9d8dba6e13 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -1,7 +1,7 @@ // Blob Overmind Controls -/mob/camera/blob/ClickOn(atom/A, params) //Expand blob +/mob/eye/blob/ClickOn(atom/A, params) //Expand blob var/list/modifiers = params2list(params) if(LAZYACCESS(modifiers, MIDDLE_CLICK)) MiddleClickOn(A, params) @@ -19,18 +19,18 @@ if(T) expand_blob(T) -/mob/camera/blob/MiddleClickOn(atom/A) //Rally spores +/mob/eye/blob/MiddleClickOn(atom/A) //Rally spores . = ..() var/turf/T = get_turf(A) if(T) rally_spores(T) -/mob/camera/blob/CtrlClickOn(atom/A) //Create a shield +/mob/eye/blob/CtrlClickOn(atom/A) //Create a shield var/turf/T = get_turf(A) if(T) create_shield(T) -/mob/camera/blob/proc/blob_click_alt(atom/A) //Remove a blob +/mob/eye/blob/proc/blob_click_alt(atom/A) //Remove a blob var/turf/T = get_turf(A) if(T) remove_blob(T) diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 2c5852fb0df..ca9fe150661 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -443,6 +443,10 @@ min_val = 0 integer = FALSE // It is in hours, but just in case one wants to specify minutes. +/// Will drones be restricted from interacting with the Supermatter and Atmospherics area? +/datum/config_entry/flag/drone_area_interaction_restrict + default = TRUE + /datum/config_entry/flag/native_fov /datum/config_entry/flag/disallow_title_music diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 02212965ef6..d5958426f26 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -795,3 +795,11 @@ default = 100 min_val = 0 max_val = 100 + +/// If admins with +DEBUG can initialize byond-tracy midround. +/datum/config_entry/flag/allow_tracy_start + protection = CONFIG_ENTRY_LOCKED + +/// If admins with +DEBUG can queue byond-tracy to run the next round. +/datum/config_entry/flag/allow_tracy_queue + protection = CONFIG_ENTRY_LOCKED diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index bbc21becc0f..8332d9b1133 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -222,6 +222,7 @@ SUBSYSTEM_DEF(air) cost_atoms = MC_AVERAGE(cost_atoms, TICK_DELTA_TO_MS(cached_cost)) resumed = FALSE + currentpart = SSAIR_PIPENETS SStgui.update_uis(SSair) //Lightning fast debugging motherfucker @@ -551,10 +552,10 @@ SUBSYSTEM_DEF(air) // Taking advantage of current cycle being set to negative before this run to do A->B B->A prevention for(var/turf/open/potential_diff as anything in difference_check) // I can't use 0 here, so we're gonna do this instead. If it ever breaks I'll eat my shoe - potential_diff.current_cycle = -INFINITE + potential_diff.current_cycle = -INFINITY for(var/turf/open/enemy_tile as anything in potential_diff.atmos_adjacent_turfs) // If it's already been processed, then it's already talked to us - if(enemy_tile.current_cycle == -INFINITE) + if(enemy_tile.current_cycle == -INFINITY) continue // .air instead of .return_air() because we can guarantee that the proc won't do anything if(potential_diff.air.compare(enemy_tile.air, MOLES)) diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index 83c666de64a..e869f21c615 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -88,7 +88,7 @@ SUBSYSTEM_DEF(blackbox) for(var/player_key in GLOB.player_details) var/datum/player_details/PD = GLOB.player_details[player_key] - record_feedback("tally", "client_byond_version", 1, PD.byond_version) + record_feedback("tally", "client_byond_version", 1, PD.full_byond_version()) /datum/controller/subsystem/blackbox/Shutdown() sealed = FALSE diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm index 7d01226b2cf..115250104f0 100644 --- a/code/controllers/subsystem/dbcore.dm +++ b/code/controllers/subsystem/dbcore.dm @@ -384,12 +384,30 @@ SUBSYSTEM_DEF(dbcore) return FALSE return new /datum/db_query(connection, sql_query, arguments) +/** + * Creates and executes a query without waiting for or tracking the results. + * Query is executed asynchronously (without blocking) and deleted afterwards - any results or errors are discarded. + * + * Arguments: + * * sql_query - The SQL query string to execute + * * arguments - List of arguments to pass to the query for parameter binding + * * allow_during_shutdown - If TRUE, allows query to be created during subsystem shutdown. Generally, only cleanup queries should set this. + */ +/datum/controller/subsystem/dbcore/proc/FireAndForget(sql_query, arguments, allow_during_shutdown = FALSE) + var/datum/db_query/query = NewQuery(sql_query, arguments, allow_during_shutdown) + if(!query) + return + ASYNC + query.Execute() + qdel(query) + /** QuerySelect Run a list of query datums in parallel, blocking until they all complete. * queries - List of queries or single query datum to run. * warn - Controls rather warn_execute() or Execute() is called. * qdel - If you don't care about the result or checking for errors, you can have the queries be deleted afterwards. - This can be combined with invoke_async as a way of running queries async without having to care about waiting for them to finish so they can be deleted. + This can be combined with invoke_async as a way of running queries async without having to care about waiting for them to finish so they can be deleted, + however you should probably just use FireAndForget instead if it's just a single query. */ /datum/controller/subsystem/dbcore/proc/QuerySelect(list/queries, warn = FALSE, qdel = FALSE) if (!islist(queries)) @@ -415,8 +433,6 @@ SUBSYSTEM_DEF(dbcore) if (qdel) qdel(query) - - /* Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query. Rows missing columns present in other rows will resolve to SQL NULL diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm index 09eef9e7d96..734cb49a472 100644 --- a/code/controllers/subsystem/dynamic/dynamic.dm +++ b/code/controllers/subsystem/dynamic/dynamic.dm @@ -569,7 +569,7 @@ SUBSYSTEM_DEF(dynamic) /datum/controller/subsystem/dynamic/proc/post_setup(report) for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules) rule.candidates.Cut() // The rule should not use candidates at this point as they all are null. - addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/dynamic/, execute_roundstart_rule), rule), rule.delay) + addtimer(CALLBACK(src, PROC_REF(execute_roundstart_rule), rule), rule.delay) if (!CONFIG_GET(flag/no_intercept_report)) addtimer(CALLBACK(src, PROC_REF(send_intercept)), rand(waittime_l, waittime_h)) diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm index ba02d898b7b..50bdb472452 100644 --- a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm +++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm @@ -263,7 +263,7 @@ candidates -= player else if(is_centcom_level(player.z)) candidates -= player // We don't autotator people in CentCom - else if(player.mind && (player.mind.special_role || player.mind.can_roll_midround())) + else if(player.mind && (player.mind.special_role || !player.mind.can_roll_midround())) candidates -= player // We don't autotator people with roles already /datum/dynamic_ruleset/midround/from_living/autotraitor/execute() @@ -312,7 +312,7 @@ continue if(isnull(player.mind)) continue - if(player.mind.special_role || player.mind.can_roll_midround()) + if(player.mind.special_role || !player.mind.can_roll_midround()) continue candidates += player @@ -483,7 +483,7 @@ candidates -= player continue - if(player.mind && (player.mind.special_role || player.mind.can_roll_midround())) + if(player.mind && (player.mind.special_role || !player.mind.can_roll_midround())) candidates -= player /datum/dynamic_ruleset/midround/from_living/blob_infection/execute() diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm index 867c99cccc6..ae9afb831f5 100644 --- a/code/controllers/subsystem/economy.dm +++ b/code/controllers/subsystem/economy.dm @@ -30,7 +30,7 @@ SUBSYSTEM_DEF(economy) var/list/bank_accounts_by_id = list() /// A list of bank accounts indexed by their assigned job typepath. var/list/bank_accounts_by_job = list() - ///List of the departmental budget cards in existance. + ///List of the departmental budget cards in existence. var/list/dep_cards = list() /// A var that collects the total amount of credits owned in player accounts on station, reset and recounted on fire() var/station_total = 0 diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm index db397d51474..3ae9cfab0c5 100644 --- a/code/controllers/subsystem/ipintel.dm +++ b/code/controllers/subsystem/ipintel.dm @@ -8,7 +8,7 @@ SUBSYSTEM_DEF(ipintel) /// Cache for previously queried IP addresses and those stored in the database var/list/datum/ip_intel/cached_queries = list() /// The store for rate limiting - var/list/rate_limit_minute + var/rate_limit_minute /// The ip intel for a given address /datum/ip_intel diff --git a/code/controllers/subsystem/pathfinder.dm b/code/controllers/subsystem/pathfinder.dm index 70dc152b06d..17ee754718e 100644 --- a/code/controllers/subsystem/pathfinder.dm +++ b/code/controllers/subsystem/pathfinder.dm @@ -82,7 +82,7 @@ SUBSYSTEM_DEF(pathfinder) // Otherwise we're gonna make a new one, and turn it into a path for the callbacks passed into us var/list/datum/callback/pass_in = list() - pass_in += CALLBACK(GLOBAL_PROC, /proc/path_map_passalong, on_finish, get_turf(caller), mintargetdist, skip_first) + pass_in += CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(path_map_passalong), on_finish, get_turf(caller), mintargetdist, skip_first) // And to allow subsequent calls to reuse the same map, we'll put a placeholder in the cache, and fill it up when the pathing finishes var/datum/path_map/empty = new() empty.pass_info = new(caller, access) diff --git a/code/controllers/subsystem/points_of_interest.dm b/code/controllers/subsystem/points_of_interest.dm index 4280d747b61..7bec303d66a 100644 --- a/code/controllers/subsystem/points_of_interest.dm +++ b/code/controllers/subsystem/points_of_interest.dm @@ -225,7 +225,7 @@ SUBSYSTEM_DEF(points_of_interest) /datum/point_of_interest/mob_poi/proc/get_type_sort_priority() if(isAI(target)) return 0 - if(iscameramob(target)) + if(iseyemob(target)) return 1 if(ispAI(target)) return 2 diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm index 7bb83fa133d..9979a1a7312 100644 --- a/code/controllers/subsystem/research.dm +++ b/code/controllers/subsystem/research.dm @@ -171,7 +171,7 @@ SUBSYSTEM_DEF(research) /datum/controller/subsystem/research/proc/initialize_all_techweb_designs(clearall = FALSE) if(islist(techweb_designs) && clearall) - item_to_design = null + item_to_design = list() QDEL_LIST(techweb_designs) var/list/returned = list() for(var/path in subtypesof(/datum/design)) diff --git a/code/controllers/subsystem/spatial_gridmap.dm b/code/controllers/subsystem/spatial_gridmap.dm index 65bcb0ec365..d906de2dc7b 100644 --- a/code/controllers/subsystem/spatial_gridmap.dm +++ b/code/controllers/subsystem/spatial_gridmap.dm @@ -45,9 +45,9 @@ atmos_contents = dummy_list /datum/spatial_grid_cell/Destroy(force) - if(force)//the response to someone trying to qdel this is a right proper fuck you + if(!force)//the response to someone trying to qdel this is a right proper fuck you stack_trace("dont try to destroy spatial grid cells without a good reason. if you need to do it use force") - return + return QDEL_HINT_LETMELIVE . = ..() diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index cf158586ce4..9d33e977e2b 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -173,6 +173,24 @@ SUBSYSTEM_DEF(statpanels) list("Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)), list("","") ) +#if defined(MC_TAB_TRACY_INFO) || defined(SPACEMAN_DMM) + var/static/tracy_dll + var/static/tracy_present + if(isnull(tracy_dll)) + tracy_dll = TRACY_DLL_PATH + tracy_present = fexists(tracy_dll) + if(tracy_present) + if(GLOB.tracy_initialized) + mc_data.Insert(2, list(list("byond-tracy:", "Active (reason: [GLOB.tracy_init_reason || "N/A"])"))) + else if(GLOB.tracy_init_error) + mc_data.Insert(2, list(list("byond-tracy:", "Errored ([GLOB.tracy_init_error])"))) + else if(fexists(TRACY_ENABLE_PATH)) + mc_data.Insert(2, list(list("byond-tracy:", "Queued for next round"))) + else + mc_data.Insert(2, list(list("byond-tracy:", "Inactive"))) + else + mc_data.Insert(2, list(list("byond-tracy:", "[tracy_dll] not present"))) +#endif for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems) mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), text_ref(sub_system)) mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", text_ref(GLOB.cameranet)) diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index 07dee0534b4..a74c6501c40 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -63,7 +63,7 @@ SUBSYSTEM_DEF(tgui) * Returns null if pool was exhausted. * * required user mob - * return datum/tgui + * return datum/tgui_window */ /datum/controller/subsystem/tgui/proc/request_pooled_window(mob/user) if(!user.client) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 57f854ede97..de18cff0e62 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -640,7 +640,7 @@ SUBSYSTEM_DEF(ticker) if(STATION_NUKED) // There was a blob on board, guess it was nuked to stop it if(length(GLOB.overminds)) - for(var/mob/camera/blob/overmind as anything in GLOB.overminds) + for(var/mob/eye/blob/overmind as anything in GLOB.overminds) if(overmind.max_count < overmind.announcement_size) continue diff --git a/code/datums/actions/items/beserk.dm b/code/datums/actions/items/beserk.dm index 43e29dbd150..01183fd8e3f 100644 --- a/code/datums/actions/items/beserk.dm +++ b/code/datums/actions/items/beserk.dm @@ -7,14 +7,27 @@ overlay_icon_state = "bg_demon_border" /datum/action/item_action/berserk_mode/Trigger(trigger_flags) - if(istype(target, /obj/item/clothing/head/hooded/berserker)) - var/obj/item/clothing/head/hooded/berserker/berserk = target - if(berserk.berserk_active) + . = ..() + if(!.) + return FALSE + var/obj/item/clothing/head/hooded/berserker/berserk = target + berserk.berserk_mode(owner) + return TRUE + +/datum/action/item_action/berserk_mode/IsAvailable(feedback = FALSE) + . = ..() + if(!.) + return FALSE + if(!istype(target, /obj/item/clothing/head/hooded/berserker)) + return FALSE + + var/obj/item/clothing/head/hooded/berserker/berserk = target + if(berserk.berserk_active) + if(feedback) to_chat(owner, span_warning("You are already berserk!")) - return - if(berserk.berserk_charge < 100) + return FALSE + if(berserk.berserk_charge < 100) + if(feedback) to_chat(owner, span_warning("You don't have a full charge.")) - return - berserk.berserk_mode(owner) - return - return ..() + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/base_basic_controller.dm b/code/datums/ai/basic_mobs/base_basic_controller.dm index eb1c38437e3..d222cc1ef6d 100644 --- a/code/datums/ai/basic_mobs/base_basic_controller.dm +++ b/code/datums/ai/basic_mobs/base_basic_controller.dm @@ -35,7 +35,7 @@ var/mob/living/living_pawn = pawn if(!(ai_traits & CAN_ACT_WHILE_DEAD)) // Unroll for flags here - if (ai_traits & CAN_ACT_IN_STASIS && (living_pawn.stat || INCAPACITATED_IGNORING(living_pawn, INCAPABLE_STASIS))) + if((ai_traits & CAN_ACT_IN_STASIS) && (living_pawn.stat || INCAPACITATED_IGNORING(living_pawn, INCAPABLE_STASIS))) return AI_UNABLE_TO_RUN if(IS_DEAD_OR_INCAP(living_pawn)) return AI_UNABLE_TO_RUN diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm index c2e9fe515c4..33cc871fddc 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm @@ -16,12 +16,6 @@ return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED -/datum/ai_behavior/targeted_mob_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if (QDELETED(target)) - controller.clear_blackboard_key(target_key) - /datum/ai_behavior/targeted_mob_ability/proc/get_ability_to_use(datum/ai_controller/controller, ability_key) return controller.blackboard[ability_key] diff --git a/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm index 5d9841a5247..649a45d4cc7 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm @@ -11,23 +11,24 @@ /// Disables AI after a certain amount of time spent with no target, you will have to enable the AI again somewhere else /datum/ai_behavior/sleep_after_targetless_time - /// Turn off AI if we spend this many seconds without a target, don't use the macro because seconds_per_tick is already in seconds - var/time_to_wait = 10 + /// Turn off AI if we spend this many seconds without a target + var/time_to_wait = 10 SECONDS /datum/ai_behavior/sleep_after_targetless_time/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return (controller.blackboard_key_exists(target_key)) ? ( AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED) : ( AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED) -/datum/ai_behavior/sleep_after_targetless_time/finish_action(datum/ai_controller/controller, succeeded, seconds_per_tick) +/datum/ai_behavior/sleep_after_targetless_time/finish_action(datum/ai_controller/controller, succeeded, target_key) . = ..() if (!succeeded) - controller.set_blackboard_key(BB_TARGETLESS_TIME, 0) + controller.clear_blackboard_key(BB_TARGETLESS_TIME) return - controller.add_blackboard_key(BB_TARGETLESS_TIME, seconds_per_tick) - if (controller.blackboard[BB_TARGETLESS_TIME] > time_to_wait) + + if (isnull(controller.blackboard[BB_TARGETLESS_TIME])) + controller.set_blackboard_key(BB_TARGETLESS_TIME, world.time + time_to_wait) + + if (controller.blackboard[BB_TARGETLESS_TIME] < world.time) enter_sleep(controller) + controller.clear_blackboard_key(BB_TARGETLESS_TIME) /// Disables AI, override to do additional things or something else /datum/ai_behavior/sleep_after_targetless_time/proc/enter_sleep(datum/ai_controller/controller) diff --git a/code/datums/ai/hunting_behavior/hunting_behaviors.dm b/code/datums/ai/hunting_behavior/hunting_behaviors.dm index 0ab14ff0d32..db684921281 100644 --- a/code/datums/ai/hunting_behavior/hunting_behaviors.dm +++ b/code/datums/ai/hunting_behavior/hunting_behaviors.dm @@ -26,16 +26,11 @@ /datum/ai_planning_subtree/find_and_hunt_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) if(!SPT_PROB(hunt_chance, seconds_per_tick)) return - if(controller.blackboard[BB_HUNTING_COOLDOWN] >= world.time) - return - var/mob/living/living_pawn = controller.pawn - // We can't hunt if we're indisposed - if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED) || living_pawn.stat != CONSCIOUS) + + if(controller.blackboard[BB_HUNTING_COOLDOWN(type)] >= world.time) return - var/atom/hunted = controller.blackboard[target_key] - // We're not hunting anything, look around for something - if(isnull(hunted)) + if(!controller.blackboard_key_exists(target_key)) controller.queue_behavior(finding_behavior, target_key, hunt_targets, hunt_range) return @@ -44,7 +39,7 @@ // we may accidentally be executing another tree's hunt - not ideal, // try to set a unique target key if you have multiple - controller.queue_behavior(hunting_behavior, target_key, BB_HUNTING_COOLDOWN) + controller.queue_behavior(hunting_behavior, target_key, BB_HUNTING_COOLDOWN(type)) if(finish_planning) return SUBTREE_RETURN_FINISH_PLANNING //If we're hunting we're too busy for anything else @@ -115,7 +110,7 @@ /datum/ai_behavior/hunt_target/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key) . = ..() - if(succeeded) + if(succeeded && hunting_cooldown_key) controller.set_blackboard_key(hunting_cooldown_key, world.time + hunt_cooldown) else if(hunting_target_key) controller.clear_blackboard_key(hunting_target_key) diff --git a/code/datums/ai/idle_behaviors/idle_haunted.dm b/code/datums/ai/idle_behaviors/idle_haunted.dm index 5784b5104f6..756adae9313 100644 --- a/code/datums/ai/idle_behaviors/idle_haunted.dm +++ b/code/datums/ai/idle_behaviors/idle_haunted.dm @@ -10,4 +10,6 @@ return if(SPT_PROB(teleport_chance, seconds_per_tick)) playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE) + #ifndef UNIT_TESTS // hauntium teleports can cause mapping nearstation tests to fail if it teleports outside an area do_teleport(item_pawn, get_turf(item_pawn), 4, channel = TELEPORT_CHANNEL_MAGIC) + #endif diff --git a/code/datums/ai/monkey/monkey_behaviors.dm b/code/datums/ai/monkey/monkey_behaviors.dm index 126c08daa1e..3fb94191cc3 100644 --- a/code/datums/ai/monkey/monkey_behaviors.dm +++ b/code/datums/ai/monkey/monkey_behaviors.dm @@ -125,7 +125,7 @@ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED /datum/ai_behavior/monkey_attack_mob - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM //performs to increase frustration + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION //performs to increase frustration /datum/ai_behavior/monkey_attack_mob/setup(datum/ai_controller/controller, target_key) . = ..() @@ -134,104 +134,76 @@ /datum/ai_behavior/monkey_attack_mob/perform(seconds_per_tick, datum/ai_controller/controller, target_key) var/mob/living/target = controller.blackboard[target_key] var/mob/living/living_pawn = controller.pawn + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(controller.blackboard[BB_TARGETING_STRATEGY]) - if(!target || target.stat != CONSCIOUS) //Target == owned + if(QDELETED(target) || !strategy.can_attack(living_pawn, target)) //Target == owned return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(!isturf(target.loc) || IS_DEAD_OR_INCAP(living_pawn)) // Check if they're a valid target - return AI_BEHAVIOR_DELAY // check if target has a weapon - var/obj/item/W - for(var/obj/item/I in target.held_items) - if(!(I.item_flags & ABSTRACT)) - W = I + var/holding_weapon = FALSE + for(var/obj/item/potential_weapon in target.held_items) + if(!(potential_weapon.item_flags & ABSTRACT)) + holding_weapon = TRUE break - // if the target has a weapon, chance to disarm them - var/perform_flags = NONE - if(W && SPT_PROB(MONKEY_ATTACK_DISARM_PROB, seconds_per_tick)) - perform_flags = monkey_attack(controller, target, seconds_per_tick, TRUE) - else - perform_flags = monkey_attack(controller, target, seconds_per_tick, FALSE) - return AI_BEHAVIOR_DELAY | perform_flags + var/attack_results = monkey_attack(controller, target, seconds_per_tick, holding_weapon && SPT_PROB(MONKEY_ATTACK_DISARM_PROB, seconds_per_tick), holding_weapon) + + if(!attack_results || controller.blackboard[BB_MONKEY_AGGRESSIVE]) + return AI_BEHAVIOR_DELAY + + //check if we can de-aggro on the enemy... + var/hatred_value = controller.blackboard[BB_MONKEY_ENEMIES][target] + + if(isnull(hatred_value)) + hatred_value = 1 + controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, hatred_value) + + if(!SPT_PROB(MONKEY_HATRED_REDUCTION_PROB, seconds_per_tick)) + return AI_BEHAVIOR_DELAY + + //we decrease our hatred value to them by 1 + hatred_value-- + if(hatred_value <= 0) + controller.remove_thing_from_blackboard_key(BB_MONKEY_ENEMIES, target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, hatred_value) + return AI_BEHAVIOR_DELAY /datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded, target_key) . = ..() - var/mob/living/living_pawn = controller.pawn controller.clear_blackboard_key(target_key) - if(QDELETED(living_pawn)) // pawn can be null at this point - return - GLOB.move_manager.stop_looping(living_pawn) /// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little -/datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, seconds_per_tick, disarm) +/datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, seconds_per_tick, disarm, holding_weapon) var/mob/living/living_pawn = controller.pawn if(living_pawn.next_move > world.time) - return NONE - - living_pawn.changeNext_move(CLICK_CD_MELEE) //We play fair - - var/obj/item/weapon = locate(/obj/item) in living_pawn.held_items - - living_pawn.face_atom(target) - - living_pawn.set_combat_mode(TRUE) - - if(isnull(controller.blackboard[BB_MONKEY_GUN_WORKED])) - controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) - - // attack with weapon if we have one - if(living_pawn.CanReach(target, weapon)) - if(weapon) - weapon.melee_attack_chain(living_pawn, target) - else - controller.ai_interact(target = target, modifiers = disarm ? list(RIGHT_CLICK = TRUE) : null) - controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) // We reset their memory of the gun being 'broken' if they accomplish some other attack - else if(weapon) - var/atom/real_target = target - if(prob(10)) // Artificial miss - real_target = pick(oview(2, target)) - - var/obj/item/gun/gun = locate() in living_pawn.held_items - var/can_shoot = gun?.can_shoot() || FALSE - if(gun && controller.blackboard[BB_MONKEY_GUN_WORKED] && prob(95)) - // We attempt to attack even if we can't shoot so we get the effects of pulling the trigger - gun.interact_with_atom(real_target, living_pawn) - controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, can_shoot ? TRUE : prob(80)) // Only 20% likely to notice it didn't work - if(can_shoot) - controller.set_blackboard_key(BB_MONKEY_GUN_NEURONS_ACTIVATED, TRUE) - else - living_pawn.throw_item(real_target) - controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) // 'worked' - - // no de-aggro - if(controller.blackboard[BB_MONKEY_AGGRESSIVE]) - return NONE - - // we've queued up a monkey attack on a mob which isn't already an enemy, so give them 1 threat to start - // note they might immediately reduce threat and drop from the list. - // this is fine, we're just giving them a love tap then leaving them alone. - // unless they fight back, then we retaliate - - // Some mobs delete on death. If the target is no longer alive, go back to idle - if(QDELETED(target)) - finish_action(controller, TRUE) - return + return FALSE - if(isnull(controller.blackboard[BB_MONKEY_ENEMIES][target])) - controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, 1) + //are we holding a gun? can we shoot it? if so, FIRE + var/obj/item/gun/gun_to_shoot = locate() in living_pawn.held_items + if(gun_to_shoot?.can_shoot()) + if(gun_to_shoot != living_pawn.get_active_held_item()) + living_pawn.swap_hand(living_pawn.get_inactive_hand_index()) + controller.ai_interact(target = target, combat_mode = TRUE) + return TRUE - /// mob refs are uids, so this is safe - if(SPT_PROB(MONKEY_HATRED_REDUCTION_PROB, seconds_per_tick)) - controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, -1) + //look for any potential weapons we're holding + var/obj/item/potential_weapon = locate() in living_pawn.held_items + if(!living_pawn.CanReach(target, potential_weapon)) + return FALSE - // if we are not angry at our target, go back to idle - if(controller.blackboard[BB_MONKEY_ENEMIES][target] <= 0) - controller.remove_thing_from_blackboard_key(BB_MONKEY_ENEMIES, target) - if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == target) - return AI_BEHAVIOR_SUCCEEDED - return NONE + if(isnull(potential_weapon)) + controller.ai_interact(target = target, modifiers = disarm ? list(RIGHT_CLICK = TRUE) : null, combat_mode = TRUE) + if(!isnull(holding_weapon)) + controller.remove_thing_from_blackboard_key(BB_MONKEY_BLACKLISTITEMS, holding_weapon) //lets try to pickpocket it again! + return TRUE + + if(potential_weapon != living_pawn.get_active_held_item()) + living_pawn.swap_hand(living_pawn.get_inactive_hand_index()) + controller.ai_interact(target = target, combat_mode = TRUE) + return TRUE /datum/ai_behavior/disposal_mob behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM //performs to increase frustration diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm index 0074d206983..8b753b9c1cc 100644 --- a/code/datums/ai/monkey/monkey_controller.dm +++ b/code/datums/ai/monkey/monkey_controller.dm @@ -15,6 +15,7 @@ have ways of interacting with a specific mob and control it. /datum/ai_planning_subtree/monkey_shenanigans, ) blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/monkey, BB_MONKEY_AGGRESSIVE = FALSE, BB_MONKEY_BEST_FORCE_FOUND = 0, BB_MONKEY_ENEMIES = list(), @@ -22,12 +23,18 @@ have ways of interacting with a specific mob and control it. BB_MONKEY_PICKPOCKETING = FALSE, BB_MONKEY_DISPOSING = FALSE, BB_MONKEY_GUN_NEURONS_ACTIVATED = FALSE, - BB_MONKEY_GUN_WORKED = TRUE, BB_SONG_LINES = MONKEY_SONG, BB_RESISTING = FALSE, ) idle_behavior = /datum/idle_behavior/idle_monkey +/datum/targeting_strategy/basic/monkey + +/datum/targeting_strategy/basic/monkey/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) + if(controller.blackboard[BB_MONKEY_ENEMIES][the_target]) + return FALSE + return ..() + /datum/ai_controller/monkey/process(seconds_per_tick) var/mob/living/living_pawn = src.pawn diff --git a/code/datums/ai/monkey/monkey_subtrees.dm b/code/datums/ai/monkey/monkey_subtrees.dm index 572eeb6b56e..1210fe44ba1 100644 --- a/code/datums/ai/monkey/monkey_subtrees.dm +++ b/code/datums/ai/monkey/monkey_subtrees.dm @@ -53,6 +53,8 @@ if(controller.blackboard[BB_MONKEY_RECRUIT_COOLDOWN] < world.time) controller.queue_behavior(/datum/ai_behavior/recruit_monkeys, BB_MONKEY_CURRENT_ATTACK_TARGET) + return + controller.queue_behavior(/datum/ai_behavior/battle_screech/monkey) controller.queue_behavior(/datum/ai_behavior/monkey_attack_mob, BB_MONKEY_CURRENT_ATTACK_TARGET) return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm index 33b7e4e214f..1e27b33b2db 100644 --- a/code/datums/ai/movement/_ai_movement.dm +++ b/code/datums/ai/movement/_ai_movement.dm @@ -28,29 +28,32 @@ /datum/ai_movement/proc/reset_pathing_failures(datum/ai_controller/controller) controller.consecutive_pathing_attempts = 0 -///Should the movement be allowed to happen? return TRUE if it can, FALSE otherwise /datum/ai_movement/proc/allowed_to_move(datum/move_loop/source) SHOULD_BE_PURE(TRUE) var/atom/movable/pawn = source.moving var/datum/ai_controller/controller = source.extra_info - var/can_move = TRUE if((controller.ai_traits & STOP_MOVING_WHEN_PULLED) && pawn.pulledby) //Need to store more state. Annoying. - can_move = FALSE + return FALSE if(!isturf(pawn.loc)) //No moving if not on a turf - can_move = FALSE + return FALSE if(isliving(pawn)) var/mob/living/pawn_mob = pawn if(!(pawn_mob.mobility_flags & MOBILITY_MOVE)) - can_move = FALSE + return FALSE + // Bandaid fix: AI controllers don't call /Process_Grab because it's a client proc, + // and thus, we need to check that grabbed mobs cuffed/crit can't move + // That proc should probably be moved onto the mob instead of clients + if(INCAPACITATED_IGNORING(pawn_mob, INCAPABLE_STASIS) && pawn.pulledby) + return FALSE if(HAS_TRAIT(pawn, TRAIT_NO_TRANSFORM)) - can_move = FALSE + return FALSE - return can_move + return TRUE ///Anything to do before moving; any checks if the pawn should be able to move should be placed in allowed_to_move() and called by this proc /datum/ai_movement/proc/pre_move(datum/move_loop/source) diff --git a/code/datums/ai/movement/ai_movement_basic_avoidance.dm b/code/datums/ai/movement/ai_movement_basic_avoidance.dm index ac231e075ed..6659da244f3 100644 --- a/code/datums/ai/movement/ai_movement_basic_avoidance.dm +++ b/code/datums/ai/movement/ai_movement_basic_avoidance.dm @@ -14,10 +14,10 @@ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move)) /datum/ai_movement/basic_avoidance/allowed_to_move(datum/move_loop/has_target/dist_bound/source) - . = ..() var/turf/target_turf = get_step_towards(source.moving, source.target) if(!target_turf?.can_cross_safely(source.moving)) return FALSE + return ..() /// Move immediately and don't update our facing /datum/ai_movement/basic_avoidance/backstep diff --git a/code/datums/ai/movement/ai_movement_complete_stop.dm b/code/datums/ai/movement/ai_movement_complete_stop.dm index 2b39e162719..e9609a30dda 100644 --- a/code/datums/ai/movement/ai_movement_complete_stop.dm +++ b/code/datums/ai/movement/ai_movement_complete_stop.dm @@ -1,6 +1,6 @@ /// Come to a complete stop for a set amount of time. /datum/ai_movement/complete_stop - max_pathing_attempts = INFINITE // path all you want, you can not escape your fate + max_pathing_attempts = INFINITY // path all you want, you can not escape your fate /datum/ai_movement/complete_stop/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) . = ..() diff --git a/code/datums/ai/movement/ai_movement_dumb.dm b/code/datums/ai/movement/ai_movement_dumb.dm index 2de85046a94..001501bcbd9 100644 --- a/code/datums/ai/movement/ai_movement_dumb.dm +++ b/code/datums/ai/movement/ai_movement_dumb.dm @@ -12,7 +12,7 @@ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move)) /datum/ai_movement/dumb/allowed_to_move(datum/move_loop/has_target/source) - . = ..() var/turf/target_turf = get_step_towards(source.moving, source.target) if(!target_turf?.can_cross_safely(source.moving)) return FALSE + return ..() diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm index b4c4fe1a28a..ddae792b31c 100644 --- a/code/datums/ai/movement/ai_movement_jps.dm +++ b/code/datums/ai/movement/ai_movement_jps.dm @@ -16,6 +16,7 @@ current_movement_target, delay, repath_delay = 0.5 SECONDS, + simulated_only = !HAS_TRAIT(controller.pawn, TRAIT_SPACEWALK), max_path_length = maximum_length, minimum_distance = controller.get_minimum_distance(), access = controller.get_access(), diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index ad60f6cd9a6..6945648a734 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -9,7 +9,7 @@ scan_desc = "partial schizophrenia" gain_text = span_notice("You feel in good company, for some reason.") lose_text = span_warning("You feel lonely again.") - var/mob/camera/imaginary_friend/friend + var/mob/eye/imaginary_friend/friend var/friend_initialized = FALSE /datum/brain_trauma/special/imaginary_friend/on_gain() @@ -76,7 +76,7 @@ friend.log_message("became [key_name(owner)]'s split personality.", LOG_GAME) message_admins("[ADMIN_LOOKUPFLW(friend)] became [ADMIN_LOOKUPFLW(owner)]'s split personality.") -/mob/camera/imaginary_friend +/mob/eye/imaginary_friend name = "imaginary friend" real_name = "imaginary friend" move_on_shuttle = TRUE @@ -96,7 +96,7 @@ /// Whether our host and other imaginary friends can hear us only when nearby or practically anywhere. var/extended_message_range = TRUE -/mob/camera/imaginary_friend/Login() +/mob/eye/imaginary_friend/Login() . = ..() if(!. || !client) return FALSE @@ -104,7 +104,7 @@ greet() Show() -/mob/camera/imaginary_friend/proc/greet() +/mob/eye/imaginary_friend/proc/greet() to_chat(src, span_notice("You are the imaginary friend of [owner]!")) to_chat(src, span_notice("You are absolutely loyal to your friend, no matter what.")) to_chat(src, span_notice("You cannot directly influence the world around you, but you can see what [owner] cannot.")) @@ -114,7 +114,7 @@ * * imaginary_friend_owner - The living mob that owns the imaginary friend. * * appearance_from_prefs - If this is a valid set of prefs, the appearance of the imaginary friend is based on these prefs. */ -/mob/camera/imaginary_friend/Initialize(mapload) +/mob/eye/imaginary_friend/Initialize(mapload) . = ..() var/static/list/grantable_actions = list( /datum/action/innate/imaginary_join, @@ -123,7 +123,7 @@ grant_actions_by_list(grantable_actions) /// Links this imaginary friend to the provided mob -/mob/camera/imaginary_friend/proc/attach_to_owner(mob/living/imaginary_friend_owner) +/mob/eye/imaginary_friend/proc/attach_to_owner(mob/living/imaginary_friend_owner) owner = imaginary_friend_owner if(!owner.imaginary_group) owner.imaginary_group = list(owner) @@ -131,14 +131,14 @@ greet() /// Copies appearance from passed player prefs, or randomises them if none are provided -/mob/camera/imaginary_friend/proc/setup_appearance(datum/preferences/appearance_from_prefs = null) +/mob/eye/imaginary_friend/proc/setup_appearance(datum/preferences/appearance_from_prefs = null) if(appearance_from_prefs) INVOKE_ASYNC(src, PROC_REF(setup_friend_from_prefs), appearance_from_prefs) else INVOKE_ASYNC(src, PROC_REF(setup_friend)) /// Randomise friend name and appearance -/mob/camera/imaginary_friend/proc/setup_friend() +/mob/eye/imaginary_friend/proc/setup_friend() gender = pick(MALE, FEMALE) real_name = generate_random_name_species_based(gender, FALSE, /datum/species/human) name = real_name @@ -151,7 +151,7 @@ * Arguments: * * appearance_from_prefs - If this is a valid set of prefs, the appearance of the imaginary friend is based on the currently selected character in them. Otherwise, it's random. */ -/mob/camera/imaginary_friend/proc/setup_friend_from_prefs(datum/preferences/appearance_from_prefs) +/mob/eye/imaginary_friend/proc/setup_friend_from_prefs(datum/preferences/appearance_from_prefs) if(!istype(appearance_from_prefs)) stack_trace("Attempted to create imaginary friend appearance from null prefs. Using random appearance.") setup_friend() @@ -181,14 +181,14 @@ Show() /// Returns all member clients of the imaginary_group -/mob/camera/imaginary_friend/proc/group_clients() +/mob/eye/imaginary_friend/proc/group_clients() var/group_clients = list() for(var/mob/person as anything in owner.imaginary_group) if(person.client) group_clients += person.client return group_clients -/mob/camera/imaginary_friend/proc/Show() +/mob/eye/imaginary_friend/proc/Show() if(!client || !owner) //nobody home return @@ -209,7 +209,7 @@ src.client.images |= current_image -/mob/camera/imaginary_friend/Destroy() +/mob/eye/imaginary_friend/Destroy() if(owner?.client) owner.client.images.Remove(human_image) if(client) @@ -217,12 +217,12 @@ owner.imaginary_group -= src return ..() -/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) +/mob/eye/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) if (safe_read_pref(client, /datum/preference/toggle/enable_runechat) && (safe_read_pref(client, /datum/preference/toggle/enable_runechat_non_mobs) || ismob(speaker))) create_chat_message(speaker, message_language, raw_message, spans) to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods)) -/mob/camera/imaginary_friend/send_speech(message, range = IMAGINARY_FRIEND_SPEECH_RANGE, obj/source = src, bubble_type = bubble_icon, list/spans = list(), datum/language/message_language = null, list/message_mods = list(), forced = null) +/mob/eye/imaginary_friend/send_speech(message, range = IMAGINARY_FRIEND_SPEECH_RANGE, obj/source = src, bubble_type = bubble_icon, list/spans = list(), datum/language/message_language = null, list/message_mods = list(), forced = null) message = get_message_mods(message, message_mods) message = capitalize(message) @@ -296,16 +296,16 @@ var/link = FOLLOW_LINK(dead_player, owner) to_chat(dead_player, "[link] [dead_rendered]") -/mob/camera/imaginary_friend/proc/clear_saypopup(image/say_popup) +/mob/eye/imaginary_friend/proc/clear_saypopup(image/say_popup) LAZYREMOVE(update_on_z, say_popup) -/mob/camera/imaginary_friend/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language, ignore_spam = FALSE, forced, filterproof) +/mob/eye/imaginary_friend/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language, ignore_spam = FALSE, forced, filterproof) if(!message) return say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced, filterproof) /datum/emote/imaginary_friend - mob_type_allowed_typecache = /mob/camera/imaginary_friend + mob_type_allowed_typecache = /mob/eye/imaginary_friend // We have to create our own since we can only show emotes to ourselves and our owner /datum/emote/imaginary_friend/run_emote(mob/user, params, type_override, intentional = FALSE) @@ -322,7 +322,7 @@ if(!msg) return TRUE - var/mob/camera/imaginary_friend/friend = user + var/mob/eye/imaginary_friend/friend = user var/dchatmsg = "[span_bold("[friend] (Imaginary friend of [friend.owner])")] [msg]" message = "[span_name("[user]")] [msg]" @@ -346,7 +346,7 @@ message = "points." message_param = "points at %t." -/datum/emote/imaginary_friend/point/run_emote(mob/camera/imaginary_friend/friend, params, type_override, intentional) +/datum/emote/imaginary_friend/point/run_emote(mob/eye/imaginary_friend/friend, params, type_override, intentional) message_param = initial(message_param) // reset return ..() @@ -380,7 +380,7 @@ return message // Another snowflake proc, when will they end... should have refactored it differently -/mob/camera/imaginary_friend/point_at(atom/pointed_atom) +/mob/eye/imaginary_friend/point_at(atom/pointed_atom) if(!isturf(loc)) return @@ -398,36 +398,36 @@ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), visual, group_clients(), 2.5 SECONDS) animate(visual, pixel_x = (tile.x - our_tile.x) * ICON_SIZE_X + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * ICON_SIZE_Y + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT) -/mob/camera/imaginary_friend/create_thinking_indicator() +/mob/eye/imaginary_friend/create_thinking_indicator() if(active_thinking_indicator || active_typing_indicator || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) return FALSE active_thinking_indicator = image('icons/mob/effects/talk.dmi', src, "[bubble_icon]3", TYPING_LAYER) add_image_to_clients(active_thinking_indicator, group_clients()) -/mob/camera/imaginary_friend/remove_thinking_indicator() +/mob/eye/imaginary_friend/remove_thinking_indicator() if(!active_thinking_indicator) return FALSE remove_image_from_clients(active_thinking_indicator, group_clients()) active_thinking_indicator = null -/mob/camera/imaginary_friend/create_typing_indicator() +/mob/eye/imaginary_friend/create_typing_indicator() if(active_typing_indicator || active_thinking_indicator || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) return FALSE active_typing_indicator = image('icons/mob/effects/talk.dmi', src, "[bubble_icon]0", TYPING_LAYER) add_image_to_clients(active_typing_indicator, group_clients()) -/mob/camera/imaginary_friend/remove_typing_indicator() +/mob/eye/imaginary_friend/remove_typing_indicator() if(!active_typing_indicator) return FALSE remove_image_from_clients(active_typing_indicator, group_clients()) active_typing_indicator = null -/mob/camera/imaginary_friend/remove_all_indicators() +/mob/eye/imaginary_friend/remove_all_indicators() REMOVE_TRAIT(src, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT) remove_thinking_indicator() remove_typing_indicator() -/mob/camera/imaginary_friend/Move(NewLoc, Dir = 0) +/mob/eye/imaginary_friend/Move(NewLoc, Dir = 0) if(world.time < move_delay) return FALSE setDir(Dir) @@ -438,11 +438,11 @@ abstract_move(NewLoc) move_delay = world.time + 1 -/mob/camera/imaginary_friend/setDir(newdir) +/mob/eye/imaginary_friend/setDir(newdir) . = ..() Show() // The image does not actually update until Show() gets called -/mob/camera/imaginary_friend/proc/recall() +/mob/eye/imaginary_friend/proc/recall() if(!owner || loc == owner) return FALSE abstract_move(owner) @@ -456,7 +456,7 @@ button_icon_state = "join" /datum/action/innate/imaginary_join/Activate() - var/mob/camera/imaginary_friend/I = owner + var/mob/eye/imaginary_friend/I = owner I.recall() /datum/action/innate/imaginary_hide @@ -468,7 +468,7 @@ button_icon_state = "hide" /datum/action/innate/imaginary_hide/proc/update_status() - var/mob/camera/imaginary_friend/I = owner + var/mob/eye/imaginary_friend/I = owner if(I.hidden) name = "Show" desc = "Become visible to your owner." @@ -480,13 +480,13 @@ build_all_button_icons() /datum/action/innate/imaginary_hide/Activate() - var/mob/camera/imaginary_friend/fake_friend = owner + var/mob/eye/imaginary_friend/fake_friend = owner fake_friend.hidden = !fake_friend.hidden fake_friend.Show() build_all_button_icons(UPDATE_BUTTON_NAME|UPDATE_BUTTON_ICON) /datum/action/innate/imaginary_hide/update_button_name(atom/movable/screen/movable/action_button/button, force) - var/mob/camera/imaginary_friend/fake_friend = owner + var/mob/eye/imaginary_friend/fake_friend = owner if(fake_friend.hidden) name = "Show" desc = "Become visible to your owner." @@ -496,7 +496,7 @@ return ..() /datum/action/innate/imaginary_hide/apply_button_icon(atom/movable/screen/movable/action_button/current_button, force = FALSE) - var/mob/camera/imaginary_friend/fake_friend = owner + var/mob/eye/imaginary_friend/fake_friend = owner if(fake_friend.hidden) button_icon_state = "unhide" else @@ -515,7 +515,7 @@ random_gain = FALSE /datum/brain_trauma/special/imaginary_friend/trapped_owner/make_friend() - friend = new /mob/camera/imaginary_friend/trapped(get_turf(owner), src) + friend = new /mob/eye/imaginary_friend/trapped(get_turf(owner), src) /datum/brain_trauma/special/imaginary_friend/trapped_owner/reroll_friend() //no rerolling- it's just the last owner's hell if(friend.client) //reconnected @@ -527,17 +527,17 @@ /datum/brain_trauma/special/imaginary_friend/trapped_owner/get_ghost() //no randoms return -/mob/camera/imaginary_friend/trapped +/mob/eye/imaginary_friend/trapped name = "figment of imagination?" real_name = "figment of imagination?" desc = "The previous host of this body." -/mob/camera/imaginary_friend/trapped/greet() +/mob/eye/imaginary_friend/trapped/greet() to_chat(src, span_notice(span_bold("You have managed to hold on as a figment of the new host's imagination!"))) to_chat(src, span_notice("All hope is lost for you, but at least you may interact with your host. You do not have to be loyal to them.")) to_chat(src, span_notice("You cannot directly influence the world around you, but you can see what the host cannot.")) -/mob/camera/imaginary_friend/trapped/setup_friend() +/mob/eye/imaginary_friend/trapped/setup_friend() real_name = "[owner.real_name]?" name = real_name human_image = icon('icons/mob/simple/lavaland/lavaland_monsters.dmi', icon_state = "curseblob") diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index f78e2af6be1..85def333f4c 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -17,15 +17,6 @@ */ 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 @@ -57,7 +48,7 @@ qdel(src, TRUE, TRUE) return - _JoinParent(parent) + _JoinParent() /** * Called during component creation with the same arguments as in new excluding parent. @@ -217,7 +208,7 @@ * * Use this to do any special cleanup you might need to do before being deregged from an object */ -/datum/component/proc/PreTransfer() +/datum/component/proc/PreTransfer(datum/new_parent) return /** @@ -328,7 +319,6 @@ CRASH("[component_type] attempted instantiation!") var/dupe_mode = initial(component_type.dupe_mode) - var/dupe_type = initial(component_type.dupe_type) var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES) if(uses_sources && !source) CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!") @@ -339,10 +329,7 @@ raw_args[1] = src if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES) - if(!dupe_type) - old_component = GetExactComponent(component_type) - else - old_component = GetComponent(dupe_type) + old_component = GetComponent(component_type) if(old_component) switch(dupe_mode) @@ -434,11 +421,11 @@ * Removes the component from parent, ends up with a null parent * Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does */ -/datum/component/proc/ClearFromParent() +/datum/component/proc/ClearFromParent(datum/new_parent) if(!parent) return var/datum/old_parent = parent - PreTransfer() + PreTransfer(new_parent) _RemoveFromParent() parent = null SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src) @@ -455,16 +442,17 @@ if(!target || target.parent == src) return if(target.parent) - target.ClearFromParent() - target.parent = src - var/result = target.PostTransfer() + target.ClearFromParent(src) + var/result = target.PostTransfer(src) switch(result) if(COMPONENT_INCOMPATIBLE) var/c_type = target.type qdel(target) CRASH("Incompatible [c_type] transfer attempt to a [type]!") - if(target == AddComponent(target)) + AddComponent(target) + if(!QDELETED(target)) + target.parent = src target._JoinParent() /** diff --git a/code/datums/components/acid.dm b/code/datums/components/acid.dm index 74fa1b1ae7f..3bb06ab639d 100644 --- a/code/datums/components/acid.dm +++ b/code/datums/components/acid.dm @@ -25,8 +25,10 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e var/turf_acid_ignores_mobs = FALSE /// The ambient sound of acid eating away at the parent [/atom]. var/datum/looping_sound/acid/sizzle - /// Particle holder for acid particles (sick) + /// Particle holder for acid particles (sick). Still utilized over shared holders because they're movable-only var/obj/effect/abstract/particle_holder/particle_effect + /// Particle type we're using for cleaning up our shared holder + var/particle_type /// The proc used to handle the parent [/atom] when processing. TODO: Unify damage and resistance flags so that this doesn't need to exist! var/datum/callback/process_effect @@ -68,8 +70,13 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e sizzle = new(atom_parent, TRUE) if(acid_particles) - // acid particles look pretty bad when they stack on mobs, so that behavior is not wanted for items - particle_effect = new(atom_parent, acid_particles, isitem(atom_parent) ? NONE : PARTICLE_ATTACH_MOB) + if (ismovable(parent)) + var/atom/movable/movable_parent = parent + movable_parent.add_shared_particles(acid_particles, "[acid_particles]_[isitem(parent)]", isitem(parent) ? NONE : PARTICLE_ATTACH_MOB) + particle_type = acid_particles + else + // acid particles look pretty bad when they stack on mobs, so that behavior is not wanted for items + particle_effect = new(atom_parent, acid_particles, isitem(atom_parent) ? NONE : PARTICLE_ATTACH_MOB) START_PROCESSING(SSacid, src) /datum/component/acid/Destroy(force) @@ -78,6 +85,9 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e QDEL_NULL(sizzle) if(particle_effect) QDEL_NULL(particle_effect) + if (ismovable(parent) && particle_type) + var/atom/movable/movable_parent = parent + movable_parent.remove_shared_particles("[particle_type]_[isitem(parent)]") process_effect = null return ..() diff --git a/code/datums/components/aquarium.dm b/code/datums/components/aquarium.dm new file mode 100644 index 00000000000..3a07c2f1481 --- /dev/null +++ b/code/datums/components/aquarium.dm @@ -0,0 +1,564 @@ +///Defines that clamp the beauty of the aquarium, to prevent it from making most areas great or horrid all by itself. +#define MIN_AQUARIUM_BEAUTY -3500 +#define MAX_AQUARIUM_BEAUTY 6000 + +/** + * The component that manages the aquariums UI, fluid, temperature, the current fish inside the parent object, as well as beauty, + * and a few other common aquarium features. + */ +/datum/component/aquarium + dupe_mode = COMPONENT_DUPE_UNIQUE + can_transfer = TRUE + /// list of fishes inside the parent object, sorted by type - does not include things with aquarium visuals that are not fish + var/list/tracked_fish_by_type + + ///The current type of fluid of the aquarium + var/fluid_type = AQUARIUM_FLUID_FRESHWATER + ///The current temperature of the fluid of the aquarium + var/fluid_temp = DEFAULT_AQUARIUM_TEMP + + ///A lazy list of key instances and assoc vals representing how much beauty they contribute to the aquarium + var/list/beauty_by_content + + ///The default beauty of the aquarium when empty. + var/default_beauty + + ///A list of layers that are currently being used for the various overlays of the aquarium (from aquarium_content comp) + var/list/used_layers = list() + + ///The minimum pixel x of the area where vis overlays should be displayed + var/aquarium_zone_min_px + ///The maximum pixel x of the area where vis overlays should be displayed + var/aquarium_zone_max_px + ///The minimum pixel y of the area where vis overlays should be displayed + var/aquarium_zone_min_py + ///The maximum pixel y of the area where vis overlays should be displayed + var/aquarium_zone_max_py + + ///While the feed (reagent) storage is not empty, this is the interval which the fish are fed. + var/feeding_interval = 3 MINUTES + ///The last time fishes were fed by the acquarium itsef. + var/last_feeding + + ///The minimum fluid temperature that can be reached by this aquarium + var/min_fluid_temp = MIN_AQUARIUM_TEMP + ///The maximum fluid temperature that can be reached by this aquarium + var/max_fluid_temp = MAX_AQUARIUM_TEMP + + ///static list of available fluid types. + var/static/list/fluid_types = list( + AQUARIUM_FLUID_SALTWATER, + AQUARIUM_FLUID_FRESHWATER, + AQUARIUM_FLUID_SULPHWATEVER, + AQUARIUM_FLUID_AIR, + ) + + ///The size of the reagents holder which will store fish feed. + var/reagents_size + +/datum/component/aquarium/Initialize( + min_px, + max_px, + min_py, + max_py, + default_beauty = 0, + reagents_size = 6, + min_fluid_temp = MIN_AQUARIUM_TEMP, + max_fluid_temp = MAX_AQUARIUM_TEMP, +) + + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + src.default_beauty = default_beauty + src.reagents_size = reagents_size + + aquarium_zone_min_px = min_px + aquarium_zone_max_px = max_px + aquarium_zone_min_py = min_py + aquarium_zone_max_py = max_py + + src.min_fluid_temp = min_fluid_temp + src.max_fluid_temp = max_fluid_temp + fluid_temp = clamp(fluid_temp, min_fluid_temp, max_fluid_temp) + +/datum/component/aquarium/RegisterWithParent() + if(default_beauty) + update_aquarium_beauty(0) + + RegisterSignals(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON), PROC_REF(on_entered)) + RegisterSignal(parent, COMSIG_ATOM_EXITED, PROC_REF(on_exited)) + + RegisterSignal(parent, COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES, PROC_REF(get_candidates)) + RegisterSignal(parent, COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS, PROC_REF(check_evolution)) + + RegisterSignal(parent, COMSIG_AQUARIUM_SET_VISUAL, PROC_REF(set_visual)) + RegisterSignal(parent, COMSIG_AQUARIUM_REMOVE_VISUAL, PROC_REF(remove_visual)) + + var/atom/movable/movable = parent + + ADD_KEEP_TOGETHER(movable, AQUARIUM_TRAIT) //render the fish on the same layer of the aquarium. + + if(reagents_size > 0) + RegisterSignal(movable.reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed)) + if(!movable.reagents) + movable.create_reagents(reagents_size, SEALED_CONTAINER) + else if(movable.reagents.total_volume) + start_autofeed(movable.reagents) + RegisterSignal(movable, COMSIG_PLUNGER_ACT, PROC_REF(on_plunger_act)) + + RegisterSignal(movable, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interaction)) + RegisterSignal(movable, COMSIG_CLICK_ALT, PROC_REF(on_click_alt)) + RegisterSignal(movable, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + + if(isitem(movable)) + RegisterSignal(movable, COMSIG_ITEM_ATTACK_SELF, PROC_REF(interact)) + RegisterSignals(movable, list(COMSIG_ATOM_ATTACK_ROBOT_SECONDARY, COMSIG_ATOM_ATTACK_HAND_SECONDARY), PROC_REF(on_secondary_attack_hand)) + else + RegisterSignal(movable, COMSIG_ATOM_UI_INTERACT, PROC_REF(interact)) + + movable.AddElement(/datum/element/relay_attackers) + + for(var/atom/movable/content as anything in movable.contents) + if(content.flags_1 & INITIALIZED_1) + on_entered(movable, content) + + ADD_TRAIT(movable, TRAIT_IS_AQUARIUM, AQUARIUM_TRAIT) + +/datum/component/aquarium/UnregisterFromParent() + var/atom/movable/movable = parent + UnregisterSignal(movable, list( + COMSIG_ATOM_ENTERED, + COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, + COMSIG_ATOM_EXITED, + COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES, + COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS, + COMSIG_AQUARIUM_SET_VISUAL, + COMSIG_AQUARIUM_REMOVE_VISUAL, + COMSIG_PLUNGER_ACT, + COMSIG_ATOM_ITEM_INTERACTION, + COMSIG_CLICK_ALT, + COMSIG_ATOM_EXAMINE, + COMSIG_ITEM_ATTACK_SELF, + COMSIG_ATOM_ATTACK_ROBOT_SECONDARY, + COMSIG_ATOM_ATTACK_HAND_SECONDARY, + COMSIG_ATOM_UI_INTERACT, + )) + if(movable.reagents) + UnregisterSignal(movable, COMSIG_REAGENTS_NEW_REAGENT) + STOP_PROCESSING(SSobj, src) + beauty_by_content = null + tracked_fish_by_type = null + movable.remove_traits(list(TRAIT_IS_AQUARIUM, TRAIT_AQUARIUM_PANEL_OPEN, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), AQUARIUM_TRAIT) + REMOVE_KEEP_TOGETHER(movable, AQUARIUM_TRAIT) + +/datum/component/aquarium/PreTransfer(atom/movable/new_parent) + if(!istype(new_parent)) + return + if(HAS_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN)) + ADD_TRAIT(new_parent, TRAIT_AQUARIUM_PANEL_OPEN, AQUARIUM_TRAIT) + if(HAS_TRAIT_FROM(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT)) + ADD_TRAIT(new_parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + var/atom/movable/movable = parent + for(var/atom/movable/moving as anything in movable.contents) + if(HAS_TRAIT(moving, TRAIT_AQUARIUM_CONTENT)) + moving.forceMove(new_parent) + if(reagents_size) + if(!new_parent.reagents) + new_parent.create_reagents(reagents_size, SEALED_CONTAINER) + movable.reagents.trans_to(new_parent, movable.reagents.total_volume) + +/datum/component/aquarium/PostTransfer(datum/new_parent) + if(!ismovable(new_parent)) + return COMPONENT_INCOMPATIBLE + +/datum/component/aquarium/InheritComponent(datum/component/aquarium/new_comp, i_am_original) + fluid_temp = clamp(new_comp.fluid_temp, min_fluid_temp, max_fluid_temp) + set_fluid_type(new_comp.fluid_type) + feeding_interval = new_comp.feeding_interval + last_feeding = new_comp.last_feeding + var/atom/movable/movable = parent + movable.update_appearance() + +/datum/component/aquarium/proc/on_click_alt(atom/movable/source, mob/living/user) + SIGNAL_HANDLER + var/closing = HAS_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN) + if(closing) + REMOVE_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN, AQUARIUM_TRAIT) + source.reagents.flags &= ~(TRANSPARENT|REFILLABLE) + SStgui.close_uis(src) + else + ADD_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN, AQUARIUM_TRAIT) + source.reagents.flags |= TRANSPARENT|REFILLABLE + + source.balloon_alert(user, "panel [closing ? "closed" : "open"]") + source.update_appearance() + return CLICK_ACTION_SUCCESS + +///This proc handles feeding the aquarium and inserting aquarium content. +/datum/component/aquarium/proc/on_item_interaction(atom/movable/source, mob/living/user, obj/item/item, modifiers) + SIGNAL_HANDLER + + if(istype(item, /obj/item/reagent_containers/cup/fish_feed)) + if(source.reagents && HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN)) + return //don't block, we'll be transferring reagents to the feed storage. + if(!item.reagents.total_volume) + source.balloon_alert(user, "[item] is empty!") + return ITEM_INTERACT_BLOCKING + var/list/fishes = get_fishes() + if(!length(fishes)) + source.balloon_alert(user, "no fish to feed!") + return ITEM_INTERACT_BLOCKING + for(var/obj/item/fish/fish as anything in fishes) + fish.feed(item.reagents) + source.balloon_alert(user, "fed the fish") + return ITEM_INTERACT_SUCCESS + + if(!HAS_TRAIT(item, TRAIT_AQUARIUM_CONTENT)) + return //proceed with normal interactions + + var/broken = source.get_integrity_percentage() <= source.integrity_failure + if(!can_insert(source, item, user)) + return ITEM_INTERACT_BLOCKING + if(broken) + source.balloon_alert(user, "aquarium is broken!") + return ITEM_INTERACT_BLOCKING + if(!user.transferItemToLoc(item, source)) + user.balloon_alert(user, "stuck to your hand!") + return ITEM_INTERACT_BLOCKING + source.balloon_alert(user, "added to aquarium") + source.update_appearance() + return ITEM_INTERACT_SUCCESS + +///Called when the feed storage is no longer empty. +/datum/component/aquarium/proc/start_autofeed(atom/movable/source, new_reagent, amount, reagtemp, data, no_react) + SIGNAL_HANDLER + START_PROCESSING(SSobj, src) + UnregisterSignal(source.reagents, COMSIG_REAGENTS_NEW_REAGENT) + +///Feed the fish at defined intervals until the feed storage is empty. +/datum/component/aquarium/process(seconds_per_tick) + var/atom/movable/movable = parent + if(!movable.reagents?.total_volume) + if(movable.reagents) + RegisterSignal(movable.reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed)) + return PROCESS_KILL + if(world.time < last_feeding + feeding_interval) + return + last_feeding = world.time + var/list/fishes = get_fishes() + for(var/obj/item/fish/fish as anything in fishes) + fish.feed(movable.reagents) + +/datum/component/aquarium/proc/on_plunger_act(atom/movable/source, obj/item/plunger/plunger, mob/living/user, reinforced) + SIGNAL_HANDLER + if(!HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN)) + return + INVOKE_ASYNC(src, PROC_REF(do_plunging), source, user) + return COMPONENT_NO_AFTERATTACK + +/datum/component/aquarium/proc/do_plunging(atom/movable/source, mob/living/user) + user.balloon_alert_to_viewers("plunging...") + if(do_after(user, 3 SECONDS, target = source)) + user.balloon_alert_to_viewers("finished plunging") + source.reagents.expose(get_turf(source), TOUCH) //splash on the floor + source.reagents.clear_reagents() + +/datum/component/aquarium/proc/on_examine(atom/movable/source, mob/user, list/examine_list) + SIGNAL_HANDLER + var/panel_open = HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN) + examine_list += span_notice("Alt-click to [panel_open ? "close" : "open"] the control and feed panel.") + if(panel_open && source.reagents.total_volume) + examine_list += span_notice("You can use a plunger to empty the feed storage.") + +///Check if an item can be inserted into the aquarium +/datum/component/aquarium/proc/can_insert(atom/movable/source, obj/item/item, mob/living/user) + var/return_value = SEND_SIGNAL(src, COMSIG_AQUARIUM_CAN_INSERT, item, user) + if(return_value & COMSIG_CANNOT_INSERT_IN_AQUARIUM) + return FALSE + if(return_value & COMSIG_CAN_INSERT_IN_AQUARIUM) + return TRUE + + if(HAS_TRAIT(item, TRAIT_UNIQUE_AQUARIUM_CONTENT)) + for(var/atom/movable/content as anything in source) + if(content == item) + continue + if(content.type == item.type) + source.balloon_alert(user, "cannot add to aquarium!") + return FALSE + return TRUE + +///Handles aquarium content insertion +/datum/component/aquarium/proc/on_entered(atom/movable/source, atom/movable/entered) + SIGNAL_HANDLER + get_content_beauty(entered) + if(!isfish(entered)) + return + var/obj/item/fish/fish = entered + LAZYADDASSOCLIST(tracked_fish_by_type, entered.type, entered) + if(fish.stable_population < length(tracked_fish_by_type[fish.type])) + for(var/obj/item/fish/anyfin as anything in tracked_fish_by_type[entered.type]) + anyfin.fish_flags |= FISH_FLAG_OVERPOPULATED + check_fluid_and_temperature(fish) + RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed)) + +///update the beauty_by_content of a 'beauty_by_content' key and then recalculate the beauty. +/datum/component/aquarium/proc/get_content_beauty(atom/movable/content) + var/list/beauty_holder = list() + SEND_SIGNAL(content, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, beauty_holder) + var/beauty = beauty_holder[1] + if(!beauty) + return + var/old_beauty = default_beauty + for(var/key in beauty_by_content) + old_beauty += beauty_by_content[key] + LAZYSET(beauty_by_content, content, beauty) + update_aquarium_beauty(old_beauty) + +///Handles aquarium content removal. +/datum/component/aquarium/proc/on_exited(atom/movable/source, atom/movable/gone) + SIGNAL_HANDLER + var/beauty = beauty_by_content?[gone] + if(beauty) + var/old_beauty = default_beauty + for(var/key in beauty_by_content) + old_beauty += beauty_by_content[key] + LAZYREMOVE(beauty_by_content, gone) + update_aquarium_beauty(old_beauty) + if(!isfish(gone)) + return + var/obj/item/fish/fish = gone + if(fish.stable_population == length(tracked_fish_by_type[fish.type])) + for(var/obj/item/fish/anyfin as anything in tracked_fish_by_type[fish.type]) + anyfin.fish_flags &= ~FISH_FLAG_OVERPOPULATED + LAZYREMOVEASSOC(tracked_fish_by_type, fish.type, fish) + fish.fish_flags &= ~(FISH_FLAG_SAFE_TEMPERATURE|FISH_FLAG_SAFE_FLUID) + UnregisterSignal(gone, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed)) + +///Return a list of fish which our fishie can reproduce with (including itself if self-reproducing) +/datum/component/aquarium/proc/get_candidates(atom/movable/source, obj/item/fish/fish, list/candidates) + SIGNAL_HANDLER + var/list/types_to_mate_with = tracked_fish_by_type + if(!HAS_TRAIT(fish, TRAIT_FISH_CROSSBREEDER)) + var/list/types_to_check = list(fish.type) + if(fish.compatible_types) + types_to_check |= fish.compatible_types + types_to_mate_with = types_to_mate_with & types_to_check + + for(var/obj/item/fish/fish_type as anything in types_to_mate_with) + var/list/type_fishes = types_to_mate_with[fish_type] + if(length(type_fishes) >= initial(fish_type.stable_population)) + continue + candidates += type_fishes + +///Check if an offspring of two fish (or one if self-reproducing) can evolve. +/datum/component/aquarium/proc/check_evolution(atom/movable/source, obj/item/fish/fish, obj/item/fish/mate, datum/fish_evolution/evolution) + SIGNAL_HANDLER + //chances are halved if only one parent has this evolution. + var/real_probability = (mate && (evolution.type in mate.evolution_types)) ? evolution.probability : evolution.probability * 0.5 + if(HAS_TRAIT(fish, TRAIT_FISH_MUTAGENIC) || (mate && HAS_TRAIT(mate, TRAIT_FISH_MUTAGENIC))) + real_probability *= 3 + if(!prob(real_probability)) + return NONE + if(!ISINRANGE(fluid_temp, evolution.required_temperature_min, evolution.required_temperature_max)) + return NONE + return COMPONENT_ALLOW_EVOLUTION + +/** + * Toggles a couple flags that determine if the fish is in safe waters so that we won't have to use signals or + * access this comp in multiple places just to confirm that. + */ +/datum/component/aquarium/proc/check_fluid_and_temperature(obj/item/fish/fish) + if(compatible_fluid_type(fish.required_fluid_type, fluid_type) || (fluid_type == AQUARIUM_FLUID_AIR && HAS_TRAIT(fish, TRAIT_FISH_AMPHIBIOUS))) + fish.fish_flags |= FISH_FLAG_SAFE_FLUID + else + fish.fish_flags &= ~FISH_FLAG_SAFE_FLUID + if(ISINRANGE(fluid_temp, fish.required_temperature_min, fish.required_temperature_max)) + fish.fish_flags |= FISH_FLAG_SAFE_TEMPERATURE + else + fish.fish_flags &= ~FISH_FLAG_SAFE_TEMPERATURE + +///Fish beauty changes when they're dead, so we need to update the beauty of the aquarium too. +/datum/component/aquarium/proc/on_fish_status_changed(obj/item/fish/fish) + get_content_beauty(fish) + +/datum/component/aquarium/proc/update_aquarium_beauty(old_beauty) + if(QDELETED(parent)) + return + old_beauty = clamp(old_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY) + var/new_beauty = 0 + for(var/key in beauty_by_content) + new_beauty += beauty_by_content[key] + new_beauty = clamp(new_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY) + if(new_beauty == old_beauty) + return + if(old_beauty) + parent.RemoveElement(/datum/element/beauty, old_beauty) + if(new_beauty) + parent.AddElement(/datum/element/beauty, new_beauty) + +///Remove a visual overlay from an aquarium_content comp +/datum/component/aquarium/proc/remove_visual(atom/movable/source, obj/effect/aquarium/visual) + SIGNAL_HANDLER + source.vis_contents -= visual + used_layers -= visual.layer + +///set values for a visual overlay for an aquarium_content comp +/datum/component/aquarium/proc/set_visual(atom/movable/source, obj/effect/aquarium/visual) + SIGNAL_HANDLER + used_layers -= visual.layer + visual.layer = request_layer(visual.layer_mode) + visual.aquarium_zone_min_px = aquarium_zone_min_px + visual.aquarium_zone_max_px = aquarium_zone_max_px + visual.aquarium_zone_min_py = aquarium_zone_min_py + visual.aquarium_zone_max_py = aquarium_zone_max_py + visual.fluid_type = fluid_type + +/datum/component/aquarium/proc/request_layer(layer_type) + var/atom/movable/movable = parent + switch(layer_type) + if(AQUARIUM_LAYER_MODE_BEHIND_GLASS) + return movable.layer + AQUARIUM_BELOW_GLASS_LAYER + if(AQUARIUM_LAYER_MODE_BOTTOM) + return movable.layer + AQUARIUM_MIN_OFFSET + if(AQUARIUM_LAYER_MODE_TOP) + return movable.layer + AQUARIUM_MAX_OFFSET + if(AQUARIUM_LAYER_MODE_AUTO) + var/chosen_layer = AQUARIUM_MIN_OFFSET + AQUARIUM_LAYER_STEP + while((chosen_layer in used_layers) && (chosen_layer <= AQUARIUM_MAX_OFFSET)) + chosen_layer += AQUARIUM_LAYER_STEP + used_layers += chosen_layer + return movable.layer + chosen_layer + +/datum/component/aquarium/proc/get_fishes() + var/list/fishes = list() + for(var/key in tracked_fish_by_type) + fishes += tracked_fish_by_type[key] + return fishes + +/datum/component/aquarium/proc/interact(atom/movable/source, mob/user) + SIGNAL_HANDLER + + if(HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN)) + INVOKE_ASYNC(src, PROC_REF(ui_interact), user) + else if(!isitem(source)) + INVOKE_ASYNC(src, PROC_REF(admire), user) + +/datum/component/aquarium/proc/on_secondary_attack_hand(obj/item/source, mob/living/user) + SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(admire), user) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/datum/component/aquarium/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + var/atom/movable/movable = parent + ui = new(user, src, "Aquarium", movable.name) + ui.open() + +/datum/component/aquarium/ui_data(atom/movable/source, mob/user) + . = ..() + .["fluidType"] = fluid_type + .["temperature"] = fluid_temp + .["allowBreeding"] = HAS_TRAIT_FROM(source, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + .["fishData"] = list() + .["feedingInterval"] = feeding_interval / (1 MINUTES) + .["propData"] = list() + for(var/atom/movable/item as anything in source.contents) + if(isfish(item)) + var/obj/item/fish/fish = item + .["fishData"] += list(list( + "fish_ref" = REF(fish), + "fish_name" = uppertext(fish.name), + "fish_happiness" = fish.get_happiness_value(), + "fish_icon" = fish::icon, + "fish_icon_state" = fish::icon_state, + "fish_health" = fish.health, + )) + continue + .["propData"] += list(list( + "prop_ref" = REF(item), + "prop_name" = item.name, + "prop_icon" = item::icon, + "prop_icon_state" = item::icon_state, + )) + +/datum/component/aquarium/ui_static_data(mob/user) + . = ..() + //I guess these should depend on the fluid so lava critters can get high or stuff below water freezing point but let's keep it simple for now. + .["minTemperature"] = min_fluid_temp + .["maxTemperature"] = max_fluid_temp + .["fluidTypes"] = fluid_types + .["heartIcon"] = 'icons/effects/effects.dmi' + +/datum/component/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/user = usr + var/atom/movable/movable = parent + switch(action) + if("temperature") + var/temperature = params["temperature"] + if(isnum(temperature) && temperature != fluid_temp) + fluid_temp = clamp(temperature, min_fluid_temp, max_fluid_temp) + for(var/obj/item/fish/fish as anything in get_fishes()) + check_fluid_and_temperature(fish) + . = TRUE + if("fluid") + if(params["fluid"] != fluid_type && (params["fluid"] in fluid_types)) + set_fluid_type(params["fluid"]) + . = TRUE + if("allow_breeding") + if(HAS_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) + REMOVE_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + else + ADD_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + . = TRUE + if("feeding_interval") + feeding_interval = params["feeding_interval"] MINUTES + . = TRUE + if("pet_fish") + var/obj/item/fish/fish = locate(params["fish_reference"]) in movable.contents + fish?.pet_fish(user) + if("remove_item") + var/atom/movable/item = locate(params["item_reference"]) in movable.contents + item?.forceMove(movable.drop_location()) + to_chat(user, span_notice("You take out [item] from [movable].")) + if("rename_fish") + var/new_name = sanitize_name(params["chosen_name"]) + var/atom/movable/fish = locate(params["fish_reference"]) in movable.contents + if(!fish || !new_name || new_name == fish.name) + return + fish.AddComponent(/datum/component/rename, new_name, fish.desc) + +/datum/component/aquarium/proc/set_fluid_type(new_fluid_type) + var/atom/movable/movable = parent + fluid_type = new_fluid_type + SEND_SIGNAL(movable, COMSIG_AQUARIUM_FLUID_CHANGED, fluid_type) + for(var/obj/item/fish/fish as anything in get_fishes()) + check_fluid_and_temperature(fish) + +/datum/component/aquarium/proc/admire(atom/movable/source, mob/living/user) + source.balloon_alert(user, "admiring aquarium...") + if(!do_after(user, 5 SECONDS, target = source)) + return + var/alive_fish = 0 + var/dead_fish = 0 + for(var/obj/item/fish/fish as anything in get_fishes()) + if(fish.status == FISH_ALIVE) + alive_fish++ + else + dead_fish++ + + var/morb = HAS_MIND_TRAIT(user, TRAIT_MORBID) + //Check if there are live fish - good mood + //All fish dead - bad mood. + //No fish - nothing. + if(alive_fish > 0) + user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_bad : /datum/mood_event/aquarium_positive) + else if(dead_fish > 0) + user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_good : /datum/mood_event/aquarium_negative) + +#undef MIN_AQUARIUM_BEAUTY +#undef MAX_AQUARIUM_BEAUTY diff --git a/code/datums/components/aquarium_content.dm b/code/datums/components/aquarium_content.dm index 1307cb45fbb..4e6e25c86bc 100644 --- a/code/datums/components/aquarium_content.dm +++ b/code/datums/components/aquarium_content.dm @@ -1,18 +1,5 @@ -///Malus to the beauty value if the fish content is dead -#define DEAD_FISH_BEAUTY -500 -///Prevents more impressive fishes from providing a positive beauty even when dead. -#define MAX_DEAD_FISH_BEAUTY -200 -///Some fish are already so ugly, they can't get much worse when dead -#define MIN_DEAD_FISH_BEAUTY -600 - -///Defines that clamp the beauty of the aquarium, to prevent it from making most areas great or horrid all by itself. -#define MIN_AQUARIUM_BEAUTY -3500 -#define MAX_AQUARIUM_BEAUTY 6000 - /// Allows movables to be inserted/displayed in aquariums. /datum/component/aquarium_content - /// Keeps track of our current aquarium. - var/obj/structure/aquarium/current_aquarium //This is visual effect holder that will end up in aquarium's vis_contents var/obj/effect/aquarium/vc_obj @@ -32,161 +19,103 @@ /// Does this behviour need additional processing in aquarium, will be added to SSobj processing on insertion var/processing = FALSE - /// Signals of the parent that will trigger animation update - var/animation_update_signals - - /// The current beauty this component gives to the aquarium it's in - var/beauty + /// Signals for the aquarium we're in that trigger an animation update + var/list/animation_update_signals - /// The original value of the beauty this component had when initialized - var/original_beauty - -/datum/component/aquarium_content/Initialize(animation_update_signals, beauty) - if(!ismovable(parent)) +/datum/component/aquarium_content/Initialize(animation_update_signals) + if(!isitem(parent)) return COMPONENT_INCOMPATIBLE - src.animation_update_signals = animation_update_signals - src.beauty = original_beauty = beauty - if(animation_update_signals) - RegisterSignals(parent, animation_update_signals, PROC_REF(generate_animation)) + src.animation_update_signals = islist(animation_update_signals) ? animation_update_signals : list(animation_update_signals) - ADD_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src)) - RegisterSignal(parent, COMSIG_TRY_INSERTING_IN_AQUARIUM, PROC_REF(is_ready_to_insert)) + ADD_TRAIT(parent, TRAIT_AQUARIUM_CONTENT, REF(src)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(enter_aquarium)) - if(isfish(parent)) - RegisterSignal(parent, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed)) - //If component is added to something already in aquarium at the time initialize it properly. var/atom/movable/movable_parent = parent - if(istype(movable_parent.loc, /obj/structure/aquarium)) + if(movable_parent.loc && HAS_TRAIT(movable_parent.loc, TRAIT_IS_AQUARIUM)) on_inserted(movable_parent.loc) -/datum/component/aquarium_content/proc/on_fish_status_changed(obj/item/fish/source) - SIGNAL_HANDLER - var/old_beauty = beauty - beauty = original_beauty - if(source.status == FISH_DEAD) - beauty = clamp(beauty + DEAD_FISH_BEAUTY, MIN_DEAD_FISH_BEAUTY, MAX_DEAD_FISH_BEAUTY) - if(current_aquarium) - change_aquarium_beauty(beauty - old_beauty) - generate_animation() - -/datum/component/aquarium_content/PreTransfer() - . = ..() - REMOVE_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src)) - /datum/component/aquarium_content/Destroy(force) - if(current_aquarium) - remove_from_aquarium() + var/atom/movable/movable = parent + if(movable.loc && HAS_TRAIT(movable.loc, TRAIT_IS_AQUARIUM)) + remove_from_aquarium(movable.loc) QDEL_NULL(vc_obj) + REMOVE_TRAIT(parent, TRAIT_AQUARIUM_CONTENT, REF(src)) return ..() /datum/component/aquarium_content/proc/enter_aquarium(datum/source, OldLoc, Dir, Forced) SIGNAL_HANDLER var/atom/movable/movable_parent = parent - if(istype(movable_parent.loc, /obj/structure/aquarium)) + if(HAS_TRAIT(movable_parent.loc, TRAIT_IS_AQUARIUM)) on_inserted(movable_parent.loc) -/datum/component/aquarium_content/proc/is_ready_to_insert(datum/source, obj/structure/aquarium/aquarium) - SIGNAL_HANDLER - if(HAS_TRAIT(parent, TRAIT_UNIQUE_AQUARIUM_CONTENT)) - for(var/atom/movable/content as anything in aquarium) - if(content == parent) - continue - if(content.type == parent.type) - return COMSIG_CANNOT_INSERT_IN_AQUARIUM - return COMSIG_CAN_INSERT_IN_AQUARIUM - -/datum/component/aquarium_content/proc/on_inserted(atom/aquarium) - current_aquarium = aquarium - RegisterSignal(current_aquarium, COMSIG_ATOM_EXITED, PROC_REF(on_removed)) - RegisterSignal(current_aquarium, COMSIG_AQUARIUM_SURFACE_CHANGED, PROC_REF(on_surface_changed)) - RegisterSignal(current_aquarium, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_fluid_changed)) +/datum/component/aquarium_content/proc/on_inserted(atom/movable/aquarium) + RegisterSignal(aquarium, COMSIG_ATOM_EXITED, PROC_REF(on_removed)) + RegisterSignal(aquarium, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_fluid_changed)) + RegisterSignals(aquarium, animation_update_signals, PROC_REF(animation_update_signal_proc)) if(processing) START_PROCESSING(SSobj, src) //If we don't have vc object yet build it if(!vc_obj) - generate_base_vc() + generate_base_vc(aquarium) //Set default position and layer set_vc_base_position() generate_animation(reset = TRUE) //Finally add it to to objects vis_contents - current_aquarium.vis_contents |= vc_obj + aquarium.vis_contents |= vc_obj - change_aquarium_beauty(beauty) - -///Modifies the beauty of the aquarium when content is added or removed, or when fishes die or live again somehow. -/datum/component/aquarium_content/proc/change_aquarium_beauty(change) - if(QDELETED(current_aquarium) || !change) - return - var/old_clamped_beauty = clamp(current_aquarium.current_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY) - current_aquarium.current_beauty += change - var/new_clamped_beauty = clamp(current_aquarium.current_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY) - if(new_clamped_beauty == old_clamped_beauty) - return - if(current_aquarium.current_beauty) - current_aquarium.RemoveElement(/datum/element/beauty, current_aquarium.current_beauty) - if(current_aquarium.current_beauty) - current_aquarium.AddElement(/datum/element/beauty, current_aquarium.current_beauty) - -/// Aquarium surface changed in some way, we need to recalculate base position and aninmation -/datum/component/aquarium_content/proc/on_surface_changed() +/datum/component/aquarium_content/proc/on_fluid_changed(datum/source, new_fluid_type) SIGNAL_HANDLER - set_vc_base_position() - generate_animation(reset = TRUE) //our animation start point changed, gotta redo + vc_obj.fluid_type = new_fluid_type + generate_animation() -/datum/component/aquarium_content/proc/on_fluid_changed() - SIGNAL_HANDLER +///Called when one of the signals in the 'animation_update_signals' is sent +/datum/component/aquarium_content/proc/animation_update_signal_proc(datum/source) generate_animation() ///Sends a signal to the parent to get them to update the aquarium animation of the visual object -/datum/component/aquarium_content/proc/generate_animation(reset=FALSE) - if(!current_aquarium) - return - SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, reset ? null : current_animation, current_aquarium, vc_obj) - -/datum/component/aquarium_content/proc/remove_visual_from_aquarium() - current_aquarium.vis_contents -= vc_obj - if(vc_obj.layer) - current_aquarium.free_layer(vc_obj.layer) +/datum/component/aquarium_content/proc/generate_animation(reset = FALSE) + var/atom/movable/movable = parent + SEND_SIGNAL(movable, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, reset ? null : current_animation, vc_obj) /// Generates common visual object, propeties that don't depend on aquarium surface -/datum/component/aquarium_content/proc/generate_base_vc() +/datum/component/aquarium_content/proc/generate_base_vc(atom/movable/aquarium) vc_obj = new vc_obj.vis_flags |= VIS_INHERIT_ID | VIS_INHERIT_PLANE //plane so it shows properly in containers on inventory ui for handheld cases - SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, vc_obj) + SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, vc_obj, aquarium) /datum/component/aquarium_content/proc/set_vc_base_position() - SEND_SIGNAL(parent, AQUARIUM_CONTENT_RANDOMIZE_POSITION, current_aquarium, vc_obj) - if(vc_obj.layer) - current_aquarium.free_layer(vc_obj.layer) - vc_obj.layer = current_aquarium.request_layer(vc_obj.layer_mode) + var/atom/movable/movable = parent + SEND_SIGNAL(movable, COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION, movable.loc, vc_obj) + SEND_SIGNAL(movable.loc, COMSIG_AQUARIUM_SET_VISUAL, vc_obj) -/datum/component/aquarium_content/proc/on_removed(obj/structure/aquarium/source, atom/movable/gone, direction) +/datum/component/aquarium_content/proc/on_removed(atom/movable/aquarium, atom/movable/gone, direction) SIGNAL_HANDLER if(parent != gone) return - remove_from_aquarium() + remove_from_aquarium(aquarium) -/datum/component/aquarium_content/proc/remove_from_aquarium() - change_aquarium_beauty(-beauty) - UnregisterSignal(current_aquarium, list(COMSIG_AQUARIUM_SURFACE_CHANGED, COMSIG_AQUARIUM_FLUID_CHANGED, COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_EXITED)) - remove_visual_from_aquarium() - current_aquarium = null +/datum/component/aquarium_content/proc/remove_from_aquarium(atom/movable/aquarium) + UnregisterSignal(aquarium, list(COMSIG_AQUARIUM_FLUID_CHANGED, COMSIG_ATOM_EXITED) + animation_update_signals) + SEND_SIGNAL(aquarium, COMSIG_AQUARIUM_REMOVE_VISUAL, vc_obj) -///The visual overlay of the aquarium content. It can hold a couple vars that we can modify through signal procs. +///The visual overlay of the aquarium content. It can hold a few vars with values about the component of the aquarium it's in. /obj/effect/aquarium layer = 0 //set on set_vc_base_position /// How the visual will be layered var/layer_mode = AQUARIUM_LAYER_MODE_AUTO - -#undef DEAD_FISH_BEAUTY -#undef MIN_DEAD_FISH_BEAUTY -#undef MAX_DEAD_FISH_BEAUTY -#undef MIN_AQUARIUM_BEAUTY -#undef MAX_AQUARIUM_BEAUTY + ///minimum pixel x, inherited from the aquarium + var/aquarium_zone_min_px + ///maximum pixel x, inherited from the aquarium + var/aquarium_zone_max_px + ///minimum pixel y, inherited from the aquarium + var/aquarium_zone_min_py + ///maximum pixel y, inherited from the aquarium + var/aquarium_zone_max_py + ///The current fluid type, inherited fom the aquarium + var/fluid_type diff --git a/code/datums/components/basic_inhands.dm b/code/datums/components/basic_inhands.dm index ac50f618861..4d286e4eed3 100644 --- a/code/datums/components/basic_inhands.dm +++ b/code/datums/components/basic_inhands.dm @@ -39,7 +39,7 @@ SIGNAL_HANDLER var/list/held_overlays = list() for(var/obj/item/held in holding_mob.held_items) - var/is_right = holding_mob.get_held_index_of_item(held) % 2 == 0 + var/is_right = IS_RIGHT_INDEX(holding_mob.get_held_index_of_item(held)) var/icon_file = is_right ? held.righthand_file : held.lefthand_file var/mutable_appearance/held_overlay = held.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) held_overlay.pixel_y += y_offset diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm index fb92e1139cc..8366a65b511 100644 --- a/code/datums/components/blob_minion.dm +++ b/code/datums/components/blob_minion.dm @@ -4,23 +4,23 @@ /datum/component/blob_minion dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS /// Overmind who is our boss - var/mob/camera/blob/overmind + var/mob/eye/blob/overmind /// Callback to run if overmind strain changes var/datum/callback/on_strain_changed -/datum/component/blob_minion/Initialize(mob/camera/blob/overmind, datum/callback/on_strain_changed) +/datum/component/blob_minion/Initialize(mob/eye/blob/overmind, datum/callback/on_strain_changed) . = ..() if (!isliving(parent)) return COMPONENT_INCOMPATIBLE src.on_strain_changed = on_strain_changed register_overlord(overmind) -/datum/component/blob_minion/InheritComponent(datum/component/new_comp, i_am_original, mob/camera/blob/overmind, datum/callback/on_strain_changed) +/datum/component/blob_minion/InheritComponent(datum/component/new_comp, i_am_original, mob/eye/blob/overmind, datum/callback/on_strain_changed) if (!isnull(on_strain_changed)) src.on_strain_changed = on_strain_changed register_overlord(overmind) -/datum/component/blob_minion/proc/register_overlord(mob/camera/blob/overmind) +/datum/component/blob_minion/proc/register_overlord(mob/eye/blob/overmind) if (isnull(overmind)) return src.overmind = overmind @@ -36,7 +36,7 @@ overmind_properties_changed() /// Our overmind has changed colour and properties -/datum/component/blob_minion/proc/overmind_properties_changed(mob/camera/blob/overmind, datum/blobstrain/new_strain) +/datum/component/blob_minion/proc/overmind_properties_changed(mob/eye/blob/overmind, datum/blobstrain/new_strain) SIGNAL_HANDLER var/mob/living/living_parent = parent living_parent.update_appearance(UPDATE_ICON) @@ -139,6 +139,7 @@ /// We only speak telepathically to blobs /datum/component/blob_minion/proc/on_try_speech(mob/living/minion, message, ignore_spam, forced) SIGNAL_HANDLER + minion.log_talk(message, LOG_SAY, tag = "blob hivemind telepathy") var/spanned_message = minion.say_quote(message) var/rendered = span_blob("\[Blob Telepathy\] [minion.real_name] [spanned_message]") relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, minion) @@ -149,6 +150,6 @@ SIGNAL_HANDLER overmind?.assume_direct_control(replacement) -/datum/component/blob_minion/PostTransfer() - if(!isliving(parent)) +/datum/component/blob_minion/PostTransfer(datum/new_parent) + if(!isliving(new_parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/bullet_intercepting.dm b/code/datums/components/bullet_intercepting.dm index 32e757c1823..f327cae9543 100644 --- a/code/datums/components/bullet_intercepting.dm +++ b/code/datums/components/bullet_intercepting.dm @@ -56,7 +56,7 @@ wearer = null /// Called when wearer is shot, check if we're going to block the hit -/datum/component/bullet_intercepting/proc/on_wearer_shot(mob/living/victim, list/signal_args, obj/projectile/bullet) +/datum/component/bullet_intercepting/proc/on_wearer_shot(mob/living/victim, obj/projectile/bullet) SIGNAL_HANDLER if (victim != wearer || victim.stat == DEAD || bullet.armor_flag != block_type) return NONE diff --git a/code/datums/components/burning.dm b/code/datums/components/burning.dm index 2535a5b6f90..fa60b3ecbce 100644 --- a/code/datums/components/burning.dm +++ b/code/datums/components/burning.dm @@ -8,8 +8,10 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e /datum/component/burning /// Fire overlay appearance we apply var/fire_overlay - /// Particle holder for fire particles, if any + /// Particle holder for fire particles, if any. Still utilized over shared holders because they're movable-only var/obj/effect/abstract/particle_holder/particle_effect + /// Particle type we're using for cleaning up our shared holder + var/particle_type /datum/component/burning/Initialize(fire_overlay = GLOB.fire_overlay, fire_particles = /particles/smoke/burning) if(!isatom(parent)) @@ -25,9 +27,14 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e return src.fire_overlay = fire_overlay - if(fire_particles) - // burning particles look pretty bad when they stack on mobs, so that behavior is not wanted for items - particle_effect = new(atom_parent, fire_particles, isitem(atom_parent) ? NONE : PARTICLE_ATTACH_MOB) + if (fire_particles) + if(ismovable(parent)) + var/atom/movable/movable_parent = parent + // burning particles look pretty bad when they stack on mobs, so that behavior is not wanted for items + movable_parent.add_shared_particles(fire_particles, "[fire_particles]_[isitem(parent)]", isitem(parent) ? NONE : PARTICLE_ATTACH_MOB) + particle_type = fire_particles + else + particle_effect = new(atom_parent, fire_particles) START_PROCESSING(SSburning, src) /datum/component/burning/Destroy(force) @@ -35,6 +42,9 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e fire_overlay = null if(particle_effect) QDEL_NULL(particle_effect) + if (ismovable(parent) && particle_type) + var/atom/movable/movable_parent = parent + movable_parent.remove_shared_particles("[particle_type]_[isitem(parent)]") return ..() /datum/component/burning/RegisterWithParent() diff --git a/code/datums/components/crafting/furniture.dm b/code/datums/components/crafting/furniture.dm index 39f5a25a19f..c3f6557f89a 100644 --- a/code/datums/components/crafting/furniture.dm +++ b/code/datums/components/crafting/furniture.dm @@ -22,12 +22,22 @@ result = /obj/structure/aquarium time = 10 SECONDS reqs = list( - /obj/item/stack/sheet/iron = 15, + /obj/item/stack/sheet/iron = 10, /obj/item/stack/sheet/glass = 10, /obj/item/aquarium_kit = 1, ) category = CAT_FURNITURE +/datum/crafting_recipe/fish_tank + name = "Portable Fish Tank" + result = /obj/item/fish_tank + time = 10 SECONDS + reqs = list( + /obj/item/stack/sheet/plastic = 5, + /obj/item/aquarium_kit = 1, + ) + category = CAT_FURNITURE + /datum/crafting_recipe/mirror name = "Mirror" result = /obj/item/wallframe/mirror diff --git a/code/datums/components/crafting/robot.dm b/code/datums/components/crafting/robot.dm index 21abedb8b47..d290c39aa0f 100644 --- a/code/datums/components/crafting/robot.dm +++ b/code/datums/components/crafting/robot.dm @@ -42,9 +42,9 @@ time = 4 SECONDS category = CAT_ROBOT -/datum/crafting_recipe/floorbot - name = "Floorbot" - result = /mob/living/simple_animal/bot/floorbot +/datum/crafting_recipe/repairbot + name = "Repairbot" + result = /mob/living/basic/bot/repairbot reqs = list( /obj/item/storage/toolbox = 1, /obj/item/stack/tile/iron = 10, diff --git a/code/datums/components/customizable_reagent_holder.dm b/code/datums/components/customizable_reagent_holder.dm index fd1b8a2ac64..fda64d6c256 100644 --- a/code/datums/components/customizable_reagent_holder.dm +++ b/code/datums/components/customizable_reagent_holder.dm @@ -82,10 +82,10 @@ )) REMOVE_TRAIT(parent, TRAIT_CUSTOMIZABLE_REAGENT_HOLDER, REF(src)) -/datum/component/customizable_reagent_holder/PostTransfer() - if(!isatom(parent)) +/datum/component/customizable_reagent_holder/PostTransfer(datum/new_parent) + if(!isatom(new_parent)) return COMPONENT_INCOMPATIBLE - var/atom/atom_parent = parent + var/atom/atom_parent = new_parent if (!atom_parent.reagents) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/earprotection.dm b/code/datums/components/earprotection.dm index 6439e49b831..6dfa7d9568b 100644 --- a/code/datums/components/earprotection.dm +++ b/code/datums/components/earprotection.dm @@ -2,10 +2,13 @@ signals = list(COMSIG_CARBON_SOUNDBANG) mobtype = /mob/living/carbon proctype = PROC_REF(reducebang) + var/reduce_amount = 1 -/datum/component/wearertargeting/earprotection/Initialize(_valid_slots) +/datum/component/wearertargeting/earprotection/Initialize(valid_slots, reduce_amount = 1) . = ..() - valid_slots = _valid_slots + src.valid_slots = valid_slots + if(reduce_amount) + src.reduce_amount = reduce_amount /datum/component/wearertargeting/earprotection/proc/reducebang(datum/source, list/reflist) - reflist[1]-- + reflist[1] -= reduce_amount diff --git a/code/datums/components/fish_growth.dm b/code/datums/components/fish_growth.dm index 7f1e411ed3a..f4c835045e8 100644 --- a/code/datums/components/fish_growth.dm +++ b/code/datums/components/fish_growth.dm @@ -56,7 +56,7 @@ var/growth = growth_rate * deciseconds_elapsed if(HAS_TRAIT(source, TRAIT_FISH_QUICK_GROWTH)) growth *= 2 - if(SEND_SIGNAL(source, COMSIG_FISH_BEFORE_GROWING, seconds_per_tick, growth) & COMPONENT_DONT_GROW) + if(SEND_SIGNAL(source, COMSIG_FISH_BEFORE_GROWING, seconds_per_tick, growth, result_type) & COMPONENT_DONT_GROW) return maturation += growth if(maturation >= 100) @@ -87,7 +87,7 @@ addtimer(CALLBACK(result, TYPE_PROC_REF(/mob/living/basic, hop_on_nearby_turf)), 0.1 SECONDS) if(is_evo || location == source.loc) - var/message_verb = del_on_grow ? "grows into" : "generates" + var/message_verb = del_on_grow ? "grows into" : "lays" location.visible_message(span_notice("[source] [message_verb] \a [result]."), vision_distance = 3) if(inherit_name && HAS_TRAIT(source, TRAIT_WAS_RENAMED)) diff --git a/code/datums/components/food/decomposition.dm b/code/datums/components/food/decomposition.dm index a3850ab39a6..c883f82f658 100644 --- a/code/datums/components/food/decomposition.dm +++ b/code/datums/components/food/decomposition.dm @@ -27,8 +27,6 @@ var/produce_ants = FALSE /// Stink particle type, if we are supposed to create stink particles var/stink_particles - /// Stink particle holder - var/obj/effect/abstract/particle_holder/particle_effect /datum/component/decomposition/Initialize(mapload, decomp_req_handle, decomp_flags = NONE, decomp_result, ant_attracting = FALSE, custom_time = 0, stink_particles = /particles/stink) if(!ismovable(parent) || !HAS_TRAIT(parent, TRAIT_GERM_SENSITIVE)) @@ -52,9 +50,11 @@ src.stink_particles = stink_particles /datum/component/decomposition/Destroy() - . = ..() - if(particle_effect) - QDEL_NULL(particle_effect) + remove_timer() + if (stink_particles) + var/atom/movable/movable_parent = parent + movable_parent.remove_shared_particles("[stink_particles]_[isitem(parent)]") + return ..() /datum/component/decomposition/RegisterWithParent() RegisterSignal(parent, COMSIG_ATOM_GERM_EXPOSED, PROC_REF(start_timer)) @@ -78,17 +78,13 @@ // If all other checks fail, then begin decomposition. decomp_timerid = addtimer(CALLBACK(src, PROC_REF(decompose)), time_remaining, TIMER_STOPPABLE | TIMER_UNIQUE) - // Also start the stinking timer, if have stink particles and aren't stinking yet - if(!stink_particles || particle_effect) + // Also start the stinking timer, if have stink particles + if(!stink_particles) return var/stink_time = max(0, time_remaining - (original_time * 0.5)) stink_timerid = addtimer(CALLBACK(src, PROC_REF(stink_up)), stink_time, TIMER_STOPPABLE | TIMER_UNIQUE) -/datum/component/decomposition/Destroy() - remove_timer() - return ..() - /// Returns the time remaining in decomp, either from our potential timer or our own value, whichever is more useful /datum/component/decomposition/proc/get_time() if(!decomp_timerid) @@ -108,11 +104,12 @@ /datum/component/decomposition/proc/stink_up() stink_timerid = null - // Neither should happen, but to be sure - if(particle_effect || !stink_particles) + // Shouldn't happen, but to be sure + if(!stink_particles) return // we don't want stink lines on mobs (even though it'd be quite funny) - particle_effect = new(parent, stink_particles, isitem(parent) ? NONE : PARTICLE_ATTACH_MOB) + var/atom/movable/movable_parent = parent + movable_parent.add_shared_particles(stink_particles, "[stink_particles]_[isitem(parent)]", isitem(parent) ? NONE : PARTICLE_ATTACH_MOB) /datum/component/decomposition/proc/decompose() decomp_timerid = null diff --git a/code/datums/components/gunpoint.dm b/code/datums/components/gunpoint.dm index edde591c0c3..3699ee75b8c 100644 --- a/code/datums/components/gunpoint.dm +++ b/code/datums/components/gunpoint.dm @@ -194,7 +194,7 @@ return var/flinch_chance = 50 - var/gun_hand = (source.get_held_index_of_item(weapon) % 2) ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM + var/gun_hand = IS_LEFT_INDEX(source.get_held_index_of_item(weapon)) ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM if(isbodypart(def_zone)) var/obj/item/bodypart/hitting = def_zone diff --git a/code/datums/components/hat_stabilizer.dm b/code/datums/components/hat_stabilizer.dm index 7a4033c3b2b..40cae0633f4 100644 --- a/code/datums/components/hat_stabilizer.dm +++ b/code/datums/components/hat_stabilizer.dm @@ -14,27 +14,32 @@ if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE + var/atom/movable/source = parent + source.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1 + src.use_worn_icon = use_worn_icon src.pixel_y_offset = pixel_y_offset - RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) - RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_qdel)) - RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND_SECONDARY, PROC_REF(on_secondary_attack_hand)) - RegisterSignals(parent, list(COMSIG_MODULE_GENERATE_WORN_OVERLAY, COMSIG_ITEM_GET_WORN_OVERLAYS), PROC_REF(get_worn_overlays)) + RegisterSignal(source, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(source, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) + RegisterSignal(source, COMSIG_QDELETING, PROC_REF(on_qdel)) + RegisterSignal(source, COMSIG_ATOM_ATTACK_HAND_SECONDARY, PROC_REF(on_secondary_attack_hand)) + RegisterSignals(source, list(COMSIG_MODULE_GENERATE_WORN_OVERLAY, COMSIG_ITEM_GET_WORN_OVERLAYS), PROC_REF(get_worn_overlays)) + RegisterSignal(source, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item)) if (add_overlay) - RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) + RegisterSignal(source, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) /datum/component/hat_stabilizer/UnregisterFromParent() if (attached_hat) remove_hat() UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_ATTACK_HAND_SECONDARY, COMSIG_MODULE_GENERATE_WORN_OVERLAY, - COMSIG_ITEM_GET_WORN_OVERLAYS, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_QDELETING)) + COMSIG_ITEM_GET_WORN_OVERLAYS, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_QDELETING, + COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)) /datum/component/hat_stabilizer/proc/on_examine(datum/source, mob/user, list/base_examine) SIGNAL_HANDLER if(attached_hat) - base_examine += span_notice("There's \a [attached_hat] placed on [parent]. Right-click to remove it.") + base_examine += span_notice("There's \a [attached_hat] placed on [parent].") else base_examine += span_notice("There's nothing placed on [parent]. Yet.") @@ -44,11 +49,13 @@ return if(attached_hat) var/mutable_appearance/worn_overlay = attached_hat.build_worn_icon(default_layer = ABOVE_BODY_FRONT_HEAD_LAYER-0.1, default_icon_file = 'icons/mob/clothing/head/default.dmi') - worn_overlay.pixel_y = pixel_y_offset + worn_overlay.pixel_y = pixel_y_offset + attached_hat.worn_y_offset overlays += worn_overlay /datum/component/hat_stabilizer/proc/on_update_overlays(atom/movable/source, list/overlays) SIGNAL_HANDLER + if (isnull(attached_hat)) + return var/mutable_appearance/worn_overlay = use_worn_icon ? attached_hat.build_worn_icon(default_layer = ABOVE_OBJ_LAYER, default_icon_file = 'icons/mob/clothing/head/default.dmi') : mutable_appearance(attached_hat, layer = ABOVE_OBJ_LAYER) worn_overlay.pixel_y = pixel_y_offset overlays += worn_overlay @@ -86,7 +93,7 @@ RegisterSignal(hat, COMSIG_MOVABLE_MOVED, PROC_REF(remove_hat)) if (!isnull(user)) - movable_parent.balloon_alert(user, "hat attached, right-click to remove") + movable_parent.balloon_alert(user, "hat attached") if (!istype(parent, /obj/item/clothing)) movable_parent.update_appearance() @@ -146,3 +153,16 @@ if (ismob(apparel.loc)) var/mob/wearer = apparel.loc wearer.update_clothing(wearer.get_slot_by_item(apparel)) + +/datum/component/hat_stabilizer/proc/on_requesting_context_from_item(atom/source, list/context, obj/item/held_item, mob/user) + SIGNAL_HANDLER + + if(attached_hat && !held_item) + context[SCREENTIP_CONTEXT_RMB] = "Remove hat" + return CONTEXTUAL_SCREENTIP_SET + + if(istype(held_item, /obj/item/clothing/head)) + context[SCREENTIP_CONTEXT_LMB] = "Attach hat" + return CONTEXTUAL_SCREENTIP_SET + + return NONE diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm index ecd2f1ff836..f168a3eba35 100644 --- a/code/datums/components/infective.dm +++ b/code/datums/components/infective.dm @@ -13,7 +13,7 @@ return COMPONENT_INCOMPATIBLE if(!islist(diseases)) - diseases = islist(diseases) + diseases = list(diseases) ///Make sure the diseases list is populated with instances of diseases so that it doesn't have to be for each AddComponent call. for(var/datum/disease/disease as anything in diseases) @@ -115,8 +115,8 @@ return if(!is_weak) - var/appendage_zone = feeder.held_items.Find(source) - appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : (appendage_zone % 2 ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM) + var/appendage_zone = feeder.get_held_index_of_item(source) + appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : (IS_RIGHT_INDEX(appendage_zone) ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM) try_infect(feeder, appendage_zone) for(var/datum/disease/disease as anything in diseases) diff --git a/code/datums/components/item_killsound.dm b/code/datums/components/item_killsound.dm new file mode 100644 index 00000000000..f6e5b4d9b76 --- /dev/null +++ b/code/datums/components/item_killsound.dm @@ -0,0 +1,41 @@ +/datum/component/item_killsound + /// list of allowed types, not null/empty + var/list/allowed_mobs + /// list of blacklisted types + var/list/blacklisted_mobs + var/killsound + var/killsound_volume = 100 + /** + * on true will act as replacement for mob's death sound, + * otherwise it will just play sound on death + */ + var/replace_default_death_sound + +/datum/component/item_killsound/Initialize( + allowed_mobs, + blacklisted_mobs, + killsound, + killsound_volume = 100, + replace_default_death_sound = FALSE +) + src.allowed_mobs = allowed_mobs + src.blacklisted_mobs = blacklisted_mobs + src.killsound = killsound + src.killsound_volume = killsound_volume + src.replace_default_death_sound = replace_default_death_sound + +/datum/component/item_killsound/RegisterWithParent() + var/obj/item/item_parent = parent + RegisterSignal(item_parent, COMSIG_ITEM_ATTACK, PROC_REF(on_attack)) + +/datum/component/item_killsound/proc/on_attack(host, target_mob, user, params) + SIGNAL_HANDLER + + if(!allowed_mobs || is_type_in_list(target_mob, allowed_mobs)) + if(is_type_in_list(target_mob, blacklisted_mobs)) + return + var/mob/living/mob = target_mob + if(replace_default_death_sound) + mob.apply_status_effect(/datum/status_effect/replace_death_sound, 1 SECONDS, killsound) + else + mob.apply_status_effect(/datum/status_effect/death_sound, 1 SECONDS, killsound, killsound_volume) diff --git a/code/datums/components/itempicky.dm b/code/datums/components/itempicky.dm index bda8b1ae138..f8e4e2256d8 100644 --- a/code/datums/components/itempicky.dm +++ b/code/datums/components/itempicky.dm @@ -27,8 +27,8 @@ /datum/component/itempicky/UnregisterFromParent() UnregisterSignal(parent, COMSIG_LIVING_TRY_PUT_IN_HAND) -/datum/component/itempicky/PostTransfer() - if(!ismob(parent)) +/datum/component/itempicky/PostTransfer(datum/new_parent) + if(!ismob(new_parent)) return COMPONENT_INCOMPATIBLE /datum/component/itempicky/InheritComponent(datum/component/itempicky/friend, i_am_original, list/arguments) diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm index ccbe2b3fd4d..fd49aa2b67b 100644 --- a/code/datums/components/jetpack.dm +++ b/code/datums/components/jetpack.dm @@ -164,6 +164,12 @@ /datum/component/jetpack/proc/on_pushoff(mob/source, movement_dir, continuous_move, atom/backup) SIGNAL_HANDLER + if (get_dir(source, backup) == movement_dir || source.loc == backup.loc) + return + + if (!source.client.intended_direction || (source.client.intended_direction & get_dir(source, backup))) + return + if (!should_trigger(source) || !check_on_move.Invoke(FALSE)) return diff --git a/code/datums/components/lock_on_cursor.dm b/code/datums/components/lock_on_cursor.dm index 84e315dc6ce..1d8adedcb3c 100644 --- a/code/datums/components/lock_on_cursor.dm +++ b/code/datums/components/lock_on_cursor.dm @@ -36,7 +36,7 @@ lock_amount = 1, list/target_typecache = list(), list/immune = list(), - icon = 'icons/mob/silicon/cameramob.dmi', + icon = 'icons/mob/eyemob.dmi', icon_state = "marker", datum/callback/on_lock, datum/callback/can_target_callback, diff --git a/code/datums/components/manual_breathing.dm b/code/datums/components/manual_breathing.dm index 8e5f096766a..c8b1af483d0 100644 --- a/code/datums/components/manual_breathing.dm +++ b/code/datums/components/manual_breathing.dm @@ -71,7 +71,7 @@ /datum/component/manual_breathing/proc/check_added_organ(mob/who_cares, obj/item/organ/O) SIGNAL_HANDLER - var/obj/item/organ/eyes/new_lungs = O + var/obj/item/organ/lungs/new_lungs = O if(istype(new_lungs,/obj/item/organ/lungs)) L = new_lungs diff --git a/code/datums/components/material/material_container.dm b/code/datums/components/material/material_container.dm index e4f7941f03a..d57f5a95d1e 100644 --- a/code/datums/components/material/material_container.dm +++ b/code/datums/components/material/material_container.dm @@ -274,7 +274,7 @@ * * user - the mob inserting this item * * context - the atom performing the operation, this is the last argument sent in COMSIG_MATCONTAINER_ITEM_CONSUMED and is used mostly for silo logging */ -/datum/component/material_container/proc/user_insert(obj/item/held_item, mob/living/user, atom/context = parent, forced_type = FALSE) +/datum/component/material_container/proc/user_insert(obj/item/held_item, mob/living/user, atom/context = parent) set waitfor = FALSE . = 0 @@ -312,7 +312,7 @@ if(SEND_SIGNAL(src, COMSIG_MATCONTAINER_PRE_USER_INSERT, target_item, user) & MATCONTAINER_BLOCK_INSERT) continue //item is either indestructible, not allowed for redemption or not in the allowed types - if((target_item.resistance_flags & INDESTRUCTIBLE) || (target_item.item_flags & NO_MAT_REDEMPTION) || (allowed_item_typecache && !is_type_in_typecache(target_item, allowed_item_typecache) && !forced_type)) + if((target_item.resistance_flags & INDESTRUCTIBLE) || (target_item.item_flags & NO_MAT_REDEMPTION) || (allowed_item_typecache && !is_type_in_typecache(target_item, allowed_item_typecache))) if(!(mat_container_flags & MATCONTAINER_SILENT)) var/list/status_data = chat_msgs["[MATERIAL_INSERT_ITEM_FAILURE]"] || list() var/list/item_data = status_data[target_item.name] || list() diff --git a/code/datums/components/material/remote_materials.dm b/code/datums/components/material/remote_materials.dm index 8ae52069c1b..65ba94e8b02 100644 --- a/code/datums/components/material/remote_materials.dm +++ b/code/datums/components/material/remote_materials.dm @@ -23,8 +23,6 @@ handles linking back and forth. var/mat_container_flags = NONE ///List of signals to hook onto the local container var/list/mat_container_signals - ///Typecache for items that the silo will accept through this remote no matter what - var/list/whitelist_typecache /datum/component/remote_materials/Initialize( mapload, @@ -32,7 +30,6 @@ handles linking back and forth. force_connect = FALSE, mat_container_flags = NONE, list/mat_container_signals = null, - list/whitelist_typecache = null ) if (!isatom(parent)) return COMPONENT_INCOMPATIBLE @@ -40,7 +37,6 @@ handles linking back and forth. src.allow_standalone = allow_standalone src.mat_container_flags = mat_container_flags src.mat_container_signals = mat_container_signals - src.whitelist_typecache = whitelist_typecache RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(OnMultitool)) @@ -48,11 +44,9 @@ handles linking back and forth. var/connect_to_silo = FALSE if(force_connect || (mapload && is_station_level(T.z))) connect_to_silo = TRUE - if(!(mat_container_flags & MATCONTAINER_NO_INSERT)) - RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, TYPE_PROC_REF(/datum/component/remote_materials, SiloAttackBy)) if(mapload) // wait for silo to initialize during mapload - addtimer(CALLBACK(src, PROC_REF(_PrepareStorage), connect_to_silo)) + SSticker.OnRoundstart(CALLBACK(src, PROC_REF(_PrepareStorage), connect_to_silo)) else //directly register in round _PrepareStorage(connect_to_silo) @@ -70,17 +64,19 @@ handles linking back and forth. silo = GLOB.ore_silo_default if (silo) silo.ore_connected_machines += src - mat_container = silo.GetComponent(/datum/component/material_container) + mat_container = silo.materials + if(!(mat_container_flags & MATCONTAINER_NO_INSERT)) + RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_insert)) + if (!mat_container && allow_standalone) _MakeLocal() /datum/component/remote_materials/Destroy() - if (silo) - silo.ore_connected_machines -= src - silo.holds -= src - silo = null - UnregisterSignal(parent, COMSIG_ATOM_ATTACKBY) + if(silo) + allow_standalone = FALSE + disconnect_from(silo) mat_container = null + return ..() /datum/component/remote_materials/proc/_MakeLocal() @@ -97,16 +93,12 @@ handles linking back and forth. allowed_items = /obj/item/stack \ ) - if (whitelist_typecache) - mat_container.allowed_item_typecache |= whitelist_typecache - -/datum/component/remote_materials/proc/toggle_holding(force_hold = FALSE) +/// Adds/Removes this connection from the silo +/datum/component/remote_materials/proc/toggle_holding() if(isnull(silo)) return - if(force_hold) - silo.holds[src] = TRUE - else if(!silo.holds[src]) + if(!silo.holds[src]) silo.holds[src] = TRUE else silo.holds -= src @@ -129,28 +121,17 @@ handles linking back and forth. * old_silo- The silo we are trying to disconnect from */ /datum/component/remote_materials/proc/disconnect_from(obj/machinery/ore_silo/old_silo) - if (!old_silo || silo != old_silo) + if (QDELETED(old_silo) || silo != old_silo) return + + UnregisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION) silo.ore_connected_machines -= src silo = null mat_container = null - UnregisterSignal(parent, COMSIG_ATOM_ATTACKBY) + if (allow_standalone) _MakeLocal() -///Insert mats into silo -/datum/component/remote_materials/proc/SiloAttackBy(datum/source, obj/item/target, mob/living/user) - SIGNAL_HANDLER - - //Allows you to attack the machine with iron sheets for e.g. - if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode) - return - - if(silo) - mat_container.user_insert(target, user, parent, (whitelist_typecache && is_type_in_typecache(target, whitelist_typecache))) - - return COMPONENT_NO_AFTERATTACK - /datum/component/remote_materials/proc/OnMultitool(datum/source, mob/user, obj/item/multitool/M) SIGNAL_HANDLER @@ -182,10 +163,24 @@ handles linking back and forth. silo.ore_connected_machines += src mat_container = new_container if(!(mat_container_flags & MATCONTAINER_NO_INSERT)) - RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, TYPE_PROC_REF(/datum/component/remote_materials, SiloAttackBy)) + RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_insert)) to_chat(user, span_notice("You connect [parent] to [silo] from the multitool's buffer.")) return ITEM_INTERACT_SUCCESS +///Insert mats into silo +/datum/component/remote_materials/proc/on_item_insert(datum/source, mob/living/user, obj/item/target) + SIGNAL_HANDLER + + //Allows you to attack the machine with iron sheets for e.g. + if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode) + return + + if(silo) + mat_container.user_insert(target, user, parent) + + return ITEM_INTERACT_SUCCESS + + /** * Checks if the param silo is in the same level as this components parent i.e. connected machine, rcd, etc * diff --git a/code/datums/components/multiple_lives.dm b/code/datums/components/multiple_lives.dm index ce2c4a5053f..f374b3e3775 100644 --- a/code/datums/components/multiple_lives.dm +++ b/code/datums/components/multiple_lives.dm @@ -51,6 +51,6 @@ /datum/component/multiple_lives/InheritComponent(datum/component/multiple_lives/new_comp , lives_left) src.lives_left += new_comp ? new_comp.lives_left : lives_left -/datum/component/multiple_lives/PostTransfer() - if(!isliving(parent)) +/datum/component/multiple_lives/PostTransfer(datum/new_parent) + if(!isliving(new_parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/mutant_hands.dm b/code/datums/components/mutant_hands.dm index f7e19e59fb5..66e19852ae7 100644 --- a/code/datums/components/mutant_hands.dm +++ b/code/datums/components/mutant_hands.dm @@ -160,11 +160,9 @@ if(!base_icon_state) return - // Even hand indexes are right hands, - // Odd hand indexes are left hand - // ...But also, we swap it intentionally here, + // We swap it intentionally here, // so right icon is shown on the left (Because hands) - if(user.get_held_index_of_item(src) % 2 == 1) + if(IS_LEFT_INDEX(user.get_held_index_of_item(src))) icon_state = "[base_icon_state]_right" else icon_state = "[base_icon_state]_left" diff --git a/code/datums/components/omen.dm b/code/datums/components/omen.dm index bb72654f978..080ee58a0f8 100644 --- a/code/datums/components/omen.dm +++ b/code/datums/components/omen.dm @@ -106,7 +106,7 @@ var/has_watchers = FALSE for(var/mob/viewer in viewers(our_guy, world.view)) - if(viewer.client) + if(viewer.client && !viewer.client.is_afk()) has_watchers = TRUE break if(!has_watchers) @@ -115,7 +115,9 @@ if(!prob(8 * effective_luck)) return - var/our_guy_pos = get_turf(living_guy) + var/turf/open/our_guy_pos = living_guy.loc + if(!isopenturf(our_guy_pos)) + return for(var/obj/machinery/door/airlock/darth_airlock in our_guy_pos) if(darth_airlock.locked || !darth_airlock.hasPower()) continue diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm index 71f391e599a..74a59301e73 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -56,10 +56,10 @@ orbiter_list += newcomp.orbiter_list newcomp.orbiter_list = null -/datum/component/orbiter/PostTransfer() - if(!isatom(parent) || isarea(parent) || !get_turf(parent)) +/datum/component/orbiter/PostTransfer(datum/new_parent) + if(!isatom(parent) || isarea(new_parent) || !get_turf(new_parent)) return COMPONENT_INCOMPATIBLE - move_react(parent) + move_react(new_parent) /datum/component/orbiter/proc/begin_orbit(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation) if(orbiter.orbiting) diff --git a/code/datums/components/parry.dm b/code/datums/components/parry.dm index 98bc9e3a84d..2486796d378 100644 --- a/code/datums/components/parry.dm +++ b/code/datums/components/parry.dm @@ -86,10 +86,9 @@ return parriers[user] = world.time + grace_period -/datum/component/parriable_projectile/proc/before_hit(obj/projectile/source, list/bullet_args) +/datum/component/parriable_projectile/proc/before_hit(obj/projectile/source, mob/living/user) SIGNAL_HANDLER - var/mob/user = bullet_args[2] if (!istype(user) || !parriers[user] || parried) return parriers -= user diff --git a/code/datums/components/profound_fisher.dm b/code/datums/components/profound_fisher.dm index 9638af4a8f2..cc7e87aeb40 100644 --- a/code/datums/components/profound_fisher.dm +++ b/code/datums/components/profound_fisher.dm @@ -2,15 +2,18 @@ /datum/component/profound_fisher ///the fishing rod this mob will use var/obj/item/fishing_rod/mob_fisher/our_rod + ///Wether we should delete the fishing rod along with the component or replace it if it's somehow removed from the parent + var/delete_rod_when_deleted = TRUE -/datum/component/profound_fisher/Initialize(our_rod) +/datum/component/profound_fisher/Initialize(our_rod, delete_rod_when_deleted = TRUE) var/isgloves = istype(parent, /obj/item/clothing/gloves) if(!isliving(parent) && !isgloves) return COMPONENT_INCOMPATIBLE src.our_rod = our_rod || new(parent) src.our_rod.internal = TRUE + src.delete_rod_when_deleted = delete_rod_when_deleted ADD_TRAIT(src.our_rod, TRAIT_NOT_BARFABLE, REF(src)) - RegisterSignal(src.our_rod, COMSIG_QDELETING, PROC_REF(on_rod_qdel)) + RegisterSignal(src.our_rod, COMSIG_MOVABLE_MOVED, PROC_REF(on_rod_moved)) if(!isgloves) RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack)) @@ -37,14 +40,26 @@ examine_list += span_info("When [EXAMINE_HINT("held")] or [EXAMINE_HINT("equipped")], [EXAMINE_HINT("right-click")] with a empty hand to open the integrated fishing rod interface.") examine_list += span_tinynoticeital("To fish, you need to turn combat mode off.") -/datum/component/profound_fisher/proc/on_rod_qdel(datum/source) +///Handles replacing the fishing rod if somehow removed from the parent movable if delete_rod_when_deleted is TRUE, otherwise delete the component. +/datum/component/profound_fisher/proc/on_rod_moved(datum/source) SIGNAL_HANDLER - qdel(src) + if(QDELETED(src) || our_rod.loc == parent) + return + if(delete_rod_when_deleted) + UnregisterSignal(our_rod, COMSIG_MOVABLE_MOVED) + if(!QDELETED(our_rod)) + qdel(our_rod) + our_rod = new our_rod.type(parent) + else + qdel(src) /datum/component/profound_fisher/Destroy() - our_rod.internal = FALSE - UnregisterSignal(our_rod, COMSIG_QDELETING) - REMOVE_TRAIT(our_rod, TRAIT_NOT_BARFABLE, REF(src)) + UnregisterSignal(our_rod, COMSIG_MOVABLE_MOVED) + if(!delete_rod_when_deleted) + our_rod.internal = FALSE + REMOVE_TRAIT(our_rod, TRAIT_NOT_BARFABLE, REF(src)) + else if(!QDELETED(our_rod)) + QDEL_NULL(our_rod) our_rod = null return ..() diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm index 724495b7c88..f5162bf918a 100644 --- a/code/datums/components/riding/riding_mob.dm +++ b/code/datums/components/riding/riding_mob.dm @@ -541,7 +541,7 @@ RegisterSignal(parent, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_hit)) RegisterSignal(parent, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(on_attacked)) -/datum/component/riding/creature/raptor/proc/on_bullet_hit(atom/target, list/bullet_args, obj/projectile/hit_projectile) +/datum/component/riding/creature/raptor/proc/on_bullet_hit(atom/target, obj/projectile/hit_projectile) SIGNAL_HANDLER if(hit_projectile.armor_flag == ENERGY) diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm index 40df294af12..16d7bddec87 100644 --- a/code/datums/components/rotation.dm +++ b/code/datums/components/rotation.dm @@ -28,7 +28,7 @@ RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item)) return ..() -/datum/component/simple_rotation/PostTransfer() +/datum/component/simple_rotation/PostTransfer(datum/new_parent) //Because of the callbacks which we don't track cleanly we can't transfer this //item cleanly, better to let the new of the new item create a new rotation datum //instead (there's no real state worth transferring) diff --git a/code/datums/components/shell.dm b/code/datums/components/shell.dm index bb3054aea6a..feb6e572bfd 100644 --- a/code/datums/components/shell.dm +++ b/code/datums/components/shell.dm @@ -346,7 +346,11 @@ )) if(attached_circuit.loc == parent || (!QDELETED(attached_circuit) && attached_circuit.loc == null)) var/atom/parent_atom = parent - attached_circuit.forceMove(parent_atom.drop_location()) + var/drop_location = parent_atom.drop_location() + if(drop_location) + attached_circuit.forceMove(drop_location) + else + attached_circuit.moveToNullspace() for(var/obj/item/circuit_component/to_remove as anything in unremovable_circuit_components) attached_circuit.remove_component(to_remove) @@ -376,7 +380,7 @@ return COMSIG_USB_CABLE_CONNECTED_TO_CIRCUIT /** - * Determines if a user is authorized to see the existance of this shell. Returns false if they are not + * Determines if a user is authorized to see the existence of this shell. Returns false if they are not * * Arguments: * * user - The user to check if they are authorized diff --git a/code/datums/components/shy.dm b/code/datums/components/shy.dm index a9b50a07efb..a6303b87a03 100644 --- a/code/datums/components/shy.dm +++ b/code/datums/components/shy.dm @@ -59,7 +59,7 @@ COMSIG_TRY_ALT_ACTION, )) -/datum/component/shy/PostTransfer() +/datum/component/shy/PostTransfer(datum/new_parent) if(!ismob(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/shy_in_room.dm b/code/datums/components/shy_in_room.dm index 4e0c2421561..d4eeeb108d9 100644 --- a/code/datums/components/shy_in_room.dm +++ b/code/datums/components/shy_in_room.dm @@ -31,7 +31,7 @@ COMSIG_TRY_ALT_ACTION, )) -/datum/component/shy_in_room/PostTransfer() +/datum/component/shy_in_room/PostTransfer(datum/new_parent) if(!ismob(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/spill.dm b/code/datums/components/spill.dm index 9112706046c..6c84299f689 100644 --- a/code/datums/components/spill.dm +++ b/code/datums/components/spill.dm @@ -25,7 +25,7 @@ dropsound = _dropsound drop_memory = _drop_memory -/datum/component/spill/PostTransfer() +/datum/component/spill/PostTransfer(datum/new_parent) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm index 4dac194c158..14183e4beb6 100644 --- a/code/datums/components/supermatter_crystal.dm +++ b/code/datums/components/supermatter_crystal.dm @@ -107,7 +107,7 @@ if(iscyborg(user) && atom_source.Adjacent(user)) dust_mob(source, user, cause = "cyborg attack") return - if(isaicamera(user)) + if(iscameramob(user)) return if(islarva(user)) dust_mob(source, user, cause = "larva attack") @@ -165,10 +165,7 @@ var/obj/item/cigarette/cig = item var/clumsy = HAS_TRAIT(user, TRAIT_CLUMSY) if(clumsy) - var/which_hand = BODY_ZONE_L_ARM - if(!(user.active_hand_index % 2)) - which_hand = BODY_ZONE_R_ARM - var/obj/item/bodypart/dust_arm = user.get_bodypart(which_hand) + var/obj/item/bodypart/dust_arm = user.get_active_hand() dust_arm.dismember() user.visible_message(span_danger("The [item] flashes out of existence on contact with \the [atom_source], resonating with a horrible sound..."),\ span_danger("Oops! The [item] flashes out of existence on contact with \the [atom_source], taking your arm with it! That was clumsy of you!")) diff --git a/code/datums/components/technointrovert.dm b/code/datums/components/technointrovert.dm index c6f51f80416..0797d24cb48 100644 --- a/code/datums/components/technointrovert.dm +++ b/code/datums/components/technointrovert.dm @@ -20,7 +20,7 @@ /datum/component/technointrovert/UnregisterFromParent() UnregisterSignal(parent, list(COMSIG_TRY_USE_MACHINE, COMSIG_TRY_WIRES_INTERACT)) -/datum/component/technointrovert/PostTransfer() +/datum/component/technointrovert/PostTransfer(datum/new_parent) if(!ismob(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/technoshy.dm b/code/datums/components/technoshy.dm index e8d4441a287..168f6d61ab5 100644 --- a/code/datums/components/technoshy.dm +++ b/code/datums/components/technoshy.dm @@ -26,7 +26,7 @@ /datum/component/technoshy/UnregisterFromParent() UnregisterSignal(parent, list(COMSIG_TRY_USE_MACHINE, COMSIG_TRY_WIRES_INTERACT)) -/datum/component/technoshy/PostTransfer() +/datum/component/technoshy/PostTransfer(datum/new_parent) if(!ismob(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/tether.dm b/code/datums/components/tether.dm index 1e8313fa53b..b9eb40647ac 100644 --- a/code/datums/components/tether.dm +++ b/code/datums/components/tether.dm @@ -72,9 +72,11 @@ to_chat(source, span_warning("[tether_name] prevents you from entering [new_loc]!")) return COMPONENT_MOVABLE_BLOCK_PRE_MOVE + // If this was called, we know its a movable + var/atom/movable/movable_source = source var/atom/movable/anchor = (source == tether_target ? parent : tether_target) if (get_dist(anchor, new_loc) > cur_dist) - if (!istype(anchor) || anchor.anchored || !anchor.Move(get_step_towards(anchor, new_loc))) + if (!istype(anchor) || anchor.anchored || !(!anchor.anchored && anchor.move_resist <= movable_source.move_force && anchor.Move(get_step_towards(anchor, new_loc)))) to_chat(source, span_warning("[tether_name] runs out of slack and prevents you from moving!")) return COMPONENT_MOVABLE_BLOCK_PRE_MOVE @@ -105,7 +107,6 @@ if (get_dist(anchor, new_loc) != cur_dist || !ismovable(source)) return - var/atom/movable/movable_source = source var/datum/drift_handler/handler = movable_source.drift_handler if (isnull(handler)) return @@ -179,12 +180,12 @@ var/atom/movable/movable_parent = parent var/atom/movable/movable_target = tether_target - if (istype(movable_parent) && movable_parent.Move(get_step(movable_parent.loc, get_dir(movable_parent, movable_target)))) + if (istype(movable_parent) && !movable_parent.anchored && movable_parent.move_resist <= movable_target.move_force && movable_parent.Move(get_step(movable_parent.loc, get_dir(movable_parent, movable_target)))) cur_dist -= 1 location.balloon_alert(user, "tether shortened") return - if (istype(movable_target) && movable_target.Move(get_step(movable_target.loc, get_dir(movable_target, movable_parent)))) + if (istype(movable_target) && !movable_target.anchored && movable_target.move_resist <= movable_parent.move_force && movable_target.Move(get_step(movable_target.loc, get_dir(movable_target, movable_parent)))) cur_dist -= 1 location.balloon_alert(user, "tether shortened") return diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm index d1f5b0fb1b8..f362359f3cd 100644 --- a/code/datums/components/wet_floor.dm +++ b/code/datums/components/wet_floor.dm @@ -159,14 +159,14 @@ for(var/i in time_left_list) . |= text2num(i) -/datum/component/wet_floor/PreTransfer() +/datum/component/wet_floor/PreTransfer(datum/new_parent) var/turf/O = parent O.cut_overlay(current_overlay) //That turf is no longer slippery, we're out of here //Slippery components don't transfer due to callbacks qdel(O.GetComponent(/datum/component/slippery)) -/datum/component/wet_floor/PostTransfer() +/datum/component/wet_floor/PostTransfer(datum/new_parent) if(!isopenturf(parent)) return COMPONENT_INCOMPATIBLE var/turf/T = parent diff --git a/code/datums/components/wormborn.dm b/code/datums/components/wormborn.dm index 1841dbf38cc..05f833fc28f 100644 --- a/code/datums/components/wormborn.dm +++ b/code/datums/components/wormborn.dm @@ -50,7 +50,7 @@ /mob/living/basic/wizard_worm/has_gravity(turf/gravity_turf) return TRUE -/mob/living/basic/wizard_worm/can_be_pulled() +/mob/living/basic/wizard_worm/can_be_pulled(user, force) return FALSE /mob/living/basic/wizard_worm/Initialize(mapload, spawn_bodyparts = TRUE) diff --git a/code/datums/elements/beauty.dm b/code/datums/elements/beauty.dm index ddd3a8f5eab..8bfa298c1a7 100644 --- a/code/datums/elements/beauty.dm +++ b/code/datums/elements/beauty.dm @@ -20,57 +20,76 @@ src.beauty = beauty - if(!beauty_counter[target] && ismovable(target)) + var/area/current_area = get_area(target) + var/beauty_active = TRUE + if(ismovable(target)) var/atom/movable/mov_target = target - mov_target.become_area_sensitive(BEAUTY_ELEMENT_TRAIT) - RegisterSignal(mov_target, COMSIG_ENTER_AREA, PROC_REF(enter_area)) - RegisterSignal(mov_target, COMSIG_EXIT_AREA, PROC_REF(exit_area)) + var/is_item = isitem(mov_target) + beauty_active = !is_item || isturf(mov_target.loc) + if(!beauty_counter[target]) + if(is_item) + RegisterSignal(mov_target, COMSIG_MOVABLE_MOVED, PROC_REF(on_item_moved)) + if(beauty_active) + mov_target.become_area_sensitive(BEAUTY_ELEMENT_TRAIT) + RegisterSignal(mov_target, COMSIG_ENTER_AREA, PROC_REF(enter_area)) + RegisterSignal(mov_target, COMSIG_EXIT_AREA, PROC_REF(exit_area)) beauty_counter[target]++ - var/area/current_area = get_area(target) - if(current_area && !current_area.outdoors) + if(current_area && !current_area.outdoors && beauty_active) current_area.totalbeauty += beauty current_area.update_beauty() /datum/element/beauty/proc/enter_area(datum/source, area/new_area) SIGNAL_HANDLER - if(new_area.outdoors) + if(new_area.outdoors || HAS_TRAIT(source, TRAIT_BEAUTY_APPLIED)) return new_area.totalbeauty += beauty * beauty_counter[source] new_area.update_beauty() + ADD_TRAIT(source, TRAIT_BEAUTY_APPLIED, INNATE_TRAIT) /datum/element/beauty/proc/exit_area(datum/source, area/old_area) SIGNAL_HANDLER - if(old_area.outdoors) + if(old_area.outdoors || !HAS_TRAIT(source, TRAIT_BEAUTY_APPLIED)) return old_area.totalbeauty -= beauty * beauty_counter[source] old_area.update_beauty() + REMOVE_TRAIT(source, TRAIT_BEAUTY_APPLIED, INNATE_TRAIT) -/datum/element/beauty/Detach(datum/source) +///Items only contribute to beauty while not inside other objects or mobs (e.g on the floor, on a table etc.). +/datum/element/beauty/proc/on_item_moved(obj/item/source, atom/old_loc, direction, forced) + SIGNAL_HANDLER + + var/is_old_turf = isturf(old_loc) + if(!is_old_turf && isturf(source.loc)) + source.become_area_sensitive(BEAUTY_ELEMENT_TRAIT) + RegisterSignal(source, COMSIG_ENTER_AREA, PROC_REF(enter_area), TRUE) + RegisterSignal(source, COMSIG_EXIT_AREA, PROC_REF(exit_area), TRUE) + enter_area(source, get_area(source.loc)) + else if(is_old_turf && !isturf(source.loc)) + source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) + UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA)) + exit_area(source, get_area(old_loc)) + +/datum/element/beauty/Detach(atom/source) if(!beauty_counter[source]) return ..() - var/area/current_area = get_area(source) - if(QDELETED(source)) - . = ..() - UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA)) - if(current_area) - exit_area(source, current_area) - beauty_counter -= source - var/atom/movable/movable_source = source - if(istype(movable_source)) - movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) - else //lower the 'counter' down by one, update the area, and call parent if it's reached zero. + + var/area/current_area = (!isitem(source) || isturf(source.loc)) ? get_area(source) : null + if(!QDELETED(source))//lower the 'counter' down by one, update the area, and call parent if it's reached zero. beauty_counter[source]-- if(current_area && !current_area.outdoors) current_area.totalbeauty -= beauty current_area.update_beauty() - if(!beauty_counter[source]) - . = ..() - UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA)) - beauty_counter -= source - var/atom/movable/movable_source = source - if(istype(movable_source)) - movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) + if(beauty_counter[source]) + return + else if(current_area) + exit_area(source, current_area) + + UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA, COMSIG_MOVABLE_MOVED)) + beauty_counter -= source + var/atom/movable/movable_source = source + if(istype(movable_source)) + movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) diff --git a/code/datums/elements/cuffsnapping.dm b/code/datums/elements/cuffsnapping.dm index df445f4acc9..1abdc4a7a6b 100644 --- a/code/datums/elements/cuffsnapping.dm +++ b/code/datums/elements/cuffsnapping.dm @@ -42,13 +42,20 @@ src.snap_time_strong = snap_time_strong RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - RegisterSignal(target, COMSIG_ITEM_ATTACK , PROC_REF(try_cuffsnap_target)) + RegisterSignal(target, COMSIG_ITEM_ATTACK_SECONDARY, PROC_REF(try_cuffsnap_target)) + RegisterSignal(target, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(add_item_context)) /datum/element/cuffsnapping/Detach(datum/target) - UnregisterSignal(target, list(COMSIG_ITEM_ATTACK, COMSIG_ATOM_EXAMINE)) - + UnregisterSignal(target, list(COMSIG_ITEM_ATTACK_SECONDARY, COMSIG_ATOM_EXAMINE, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET)) return ..() +/datum/element/cuffsnapping/proc/add_item_context(obj/item/source, list/context, mob/living/carbon/target, mob/living/user) + SIGNAL_HANDLER + if(!iscarbon(target) || !target.handcuffed) + return NONE + context[SCREENTIP_CONTEXT_RMB] = "Cut Restraints" + return CONTEXTUAL_SCREENTIP_SET + ///signal called on parent being examined /datum/element/cuffsnapping/proc/on_examine(datum/target, mob/user, list/examine_list) SIGNAL_HANDLER @@ -56,7 +63,7 @@ var/examine_string if(isnull(snap_time_weak)) return - examine_string = "It looks like it could cut zipties or cable restraints off someone in [snap_time_weak] seconds" + examine_string = "It looks like it could be used to cut zipties or cable restraints off someone in [snap_time_weak] seconds" if(!isnull(snap_time_strong)) examine_string += ", and handcuffs in [snap_time_strong] seconds." @@ -65,7 +72,7 @@ examine_list += span_notice(examine_string) -/datum/element/cuffsnapping/proc/try_cuffsnap_target(obj/item/cutter, mob/living/carbon/target, mob/cutter_user, params) +/datum/element/cuffsnapping/proc/try_cuffsnap_target(obj/item/cutter, mob/living/carbon/target, mob/living/cutter_user, params) SIGNAL_HANDLER if(!istype(target)) //we aren't the kind of mob that can even have cuffs, so we skip. diff --git a/code/datums/elements/cult_eyes.dm b/code/datums/elements/cult_eyes.dm index 3e685419836..68d70a47092 100644 --- a/code/datums/elements/cult_eyes.dm +++ b/code/datums/elements/cult_eyes.dm @@ -26,13 +26,11 @@ return ADD_TRAIT(target, TRAIT_UNNATURAL_RED_GLOWY_EYES, CULT_TRAIT) - if (ishuman(target)) - var/mob/living/carbon/human/human_parent = target - human_parent.eye_color_left = BLOODCULT_EYE - human_parent.eye_color_right = BLOODCULT_EYE - human_parent.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK) - human_parent.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK) - human_parent.update_body() + if (!ishuman(target)) + return + var/mob/living/carbon/human/human_parent = target + human_parent.add_eye_color(BLOODCULT_EYE, EYE_COLOR_CULT_PRIORITY) + human_parent.update_body() /** * Detach proc @@ -43,10 +41,7 @@ REMOVE_TRAIT(target, TRAIT_UNNATURAL_RED_GLOWY_EYES, CULT_TRAIT) if (ishuman(target)) var/mob/living/carbon/human/human_parent = target - human_parent.eye_color_left = initial(human_parent.eye_color_left) - human_parent.eye_color_right = initial(human_parent.eye_color_right) - human_parent.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK) - human_parent.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK) + human_parent.remove_eye_color(EYE_COLOR_CULT_PRIORITY) human_parent.update_body() UnregisterSignal(target, list(COMSIG_CHANGELING_TRANSFORM, COMSIG_HUMAN_MONKEYIZE, COMSIG_MONKEY_HUMANIZE)) return ..() diff --git a/code/datums/elements/immerse.dm b/code/datums/elements/immerse.dm index d50ae906c0a..4c57c40a791 100644 --- a/code/datums/elements/immerse.dm +++ b/code/datums/elements/immerse.dm @@ -1,3 +1,10 @@ +/// A list of movables that shouldn't be affected by the element, either because it'd look bad or barely perceptible +GLOBAL_LIST_INIT(immerse_ignored_movable, typecacheof(list( + /obj/effect, + /mob/dead, + /obj/projectile, +))) + /** * A visual element that makes movables entering the attached turfs look immersed into that turf. * @@ -9,11 +16,6 @@ ///An association list of turfs that have this element attached and their affected contents. var/list/attached_turfs_and_movables = list() - /** - * A list of movables that shouldn't be affected by the element, either because it'd look bad - * or barely perceptible. - */ - var/static/list/movables_to_ignore ///A list of icons generated from a target and a mask, later used as appearances for the overlays. var/static/list/generated_immerse_icons = list() ///A list of instances of /atom/movable/immerse_overlay then used as visual overlays for the immersed movables. @@ -32,16 +34,6 @@ if(!isturf(target) || !icon || !icon_state || !mask_icon) return ELEMENT_INCOMPATIBLE - if(isnull(movables_to_ignore)) - movables_to_ignore = typecacheof(list( - /obj/effect, - /mob/dead, - /obj/projectile, - )) - - movables_to_ignore += GLOB.WALLITEMS_INTERIOR - movables_to_ignore += GLOB.WALLITEMS_EXTERIOR - src.icon = icon src.icon_state = icon_state src.color = color @@ -109,11 +101,15 @@ SIGNAL_HANDLER if(QDELETED(movable)) return - if(HAS_TRAIT(movable, TRAIT_IMMERSED)) + if(HAS_TRAIT(movable, TRAIT_IMMERSED) || HAS_TRAIT(movable, TRAIT_WALLMOUNTED)) + return + if(!ISINRANGE(movable.plane, MUTATE_PLANE(FLOOR_PLANE, source), MUTATE_PLANE(GAME_PLANE, source))) return - if(movable.layer >= ABOVE_ALL_MOB_LAYER || !ISINRANGE(movable.plane, MUTATE_PLANE(FLOOR_PLANE, source), MUTATE_PLANE(GAME_PLANE, source))) + var/layer_to_check = IS_TOPDOWN_PLANE(source.plane) ? TOPDOWN_ABOVE_WATER_LAYER : ABOVE_ALL_MOB_LAYER + //First, floor plane objects use TOPDOWN_LAYER, second this check shouldn't apply to them anyway. + if(movable.layer >= layer_to_check) return - if(is_type_in_typecache(movable, movables_to_ignore)) + if(is_type_in_typecache(movable, GLOB.immerse_ignored_movable)) return var/atom/movable/buckled @@ -145,7 +141,9 @@ var/width = icon_dimensions["width"] || ICON_SIZE_X var/height = icon_dimensions["height"] || ICON_SIZE_Y - var/is_below_water = movable.layer < WATER_LEVEL_LAYER ? "underwater-" : "" + ///This determines if the overlay should cover the entire surface of the object or not + var/layer_to_check = IS_TOPDOWN_PLANE(movable.plane) ? TOPDOWN_WATER_LEVEL_LAYER : WATER_LEVEL_LAYER + var/is_below_water = (movable.layer < layer_to_check) ? "underwater-" : "" var/atom/movable/immerse_overlay/vis_overlay = generated_visual_overlays["[is_below_water][width]x[height]"] diff --git a/code/datums/elements/organ_set_bonus.dm b/code/datums/elements/organ_set_bonus.dm index 082933e409e..118c64fbeaf 100644 --- a/code/datums/elements/organ_set_bonus.dm +++ b/code/datums/elements/organ_set_bonus.dm @@ -42,8 +42,8 @@ /datum/status_effect/organ_set_bonus id = "organ_set_bonus" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null ///how many organs the carbon with this has in the set var/organs = 0 diff --git a/code/datums/elements/ranged_armour.dm b/code/datums/elements/ranged_armour.dm index 6d1322c687b..de5ba61d223 100644 --- a/code/datums/elements/ranged_armour.dm +++ b/code/datums/elements/ranged_armour.dm @@ -40,7 +40,7 @@ return ..() /// Modify or ignore bullet damage based on projectile properties -/datum/element/ranged_armour/proc/pre_bullet_impact(atom/parent, list/signal_args, obj/projectile/bullet) +/datum/element/ranged_armour/proc/pre_bullet_impact(atom/parent, obj/projectile/bullet) SIGNAL_HANDLER if (bullet.damage >= minimum_projectile_force || (bullet.damage_type in vulnerable_projectile_types)) return diff --git a/code/datums/elements/relay_attackers.dm b/code/datums/elements/relay_attackers.dm index fd87cb3bc2c..5b7202608ec 100644 --- a/code/datums/elements/relay_attackers.dm +++ b/code/datums/elements/relay_attackers.dm @@ -60,7 +60,7 @@ relay_attacker(target, attacker, ATTACKER_DAMAGING_ATTACK) /// Even if another component blocked this hit, someone still shot at us -/datum/element/relay_attackers/proc/on_bullet_act(atom/target, list/bullet_args, obj/projectile/hit_projectile) +/datum/element/relay_attackers/proc/on_bullet_act(atom/target, obj/projectile/hit_projectile) SIGNAL_HANDLER if(!hit_projectile.is_hostile_projectile()) return diff --git a/code/datums/elements/tenacious.dm b/code/datums/elements/tenacious.dm index 4d906812c13..35dd5774cf4 100644 --- a/code/datums/elements/tenacious.dm +++ b/code/datums/elements/tenacious.dm @@ -18,6 +18,9 @@ /datum/element/tenacious/Detach(datum/target) UnregisterSignal(target, COMSIG_MOB_STATCHANGE) REMOVE_TRAIT(target, TRAIT_TENACIOUS, ELEMENT_TRAIT(type)) + var/mob/living/carbon/human/valid_target = target + if(valid_target.remove_movespeed_modifier(/datum/movespeed_modifier/tenacious)) + valid_target.balloon_alert(valid_target, "your tenacity wears off") return ..() ///signal called by the stat of the target changing @@ -27,6 +30,5 @@ if(new_stat == SOFT_CRIT) target.balloon_alert(target, "your tenacity kicks in") target.add_movespeed_modifier(/datum/movespeed_modifier/tenacious) - else + else if(target.remove_movespeed_modifier(/datum/movespeed_modifier/tenacious)) target.balloon_alert(target, "your tenacity wears off") - target.remove_movespeed_modifier(/datum/movespeed_modifier/tenacious) diff --git a/code/datums/elements/watery_tile.dm b/code/datums/elements/watery_tile.dm index 6e32c7bb155..36e893fe0fc 100644 --- a/code/datums/elements/watery_tile.dm +++ b/code/datums/elements/watery_tile.dm @@ -51,7 +51,7 @@ /datum/status_effect/watery_tile_wetness id = "watery_tile_wetness" alert_type = null - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_UNIQUE /datum/status_effect/washing_regen/tick(seconds_between_ticks) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index b979c9cda0f..b51f7097a9f 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -23,7 +23,7 @@ // argument handling // if the precision is not specified, default to 0, but apply BoH penalties - if (isnull(precision)) + if(isnull(precision)) precision = 0 switch(channel) @@ -40,7 +40,7 @@ to_chat(MM, span_warning("The bluespace interface on your bag of holding interferes with the teleport!")) // if effects are not specified and not explicitly disabled, sparks - if ((!effectin || !effectout) && !no_effects) + if((!effectin || !effectout) && !no_effects) var/datum/effect_system/spark_spread/sparks = new sparks.set_up(5, 1, teleatom) if (!effectin) @@ -78,10 +78,16 @@ return TRUE tele_play_specials(teleatom, curturf, effectin, asoundin) + var/success = teleatom.forceMove(destturf) - if(success) - log_game("[key_name(teleatom)] has teleported from [loc_name(curturf)] to [loc_name(destturf)]") - tele_play_specials(teleatom, destturf, effectout, asoundout) + if(!success) + return FALSE + + . = TRUE + /* Past this point, the teleport is successful and you can assume that they're already there */ + + log_game("[key_name(teleatom)] has teleported from [loc_name(curturf)] to [loc_name(destturf)]") + tele_play_specials(teleatom, destturf, effectout, asoundout) if(ismob(teleatom)) var/mob/M = teleatom @@ -90,7 +96,22 @@ SEND_SIGNAL(teleatom, COMSIG_MOVABLE_POST_TELEPORT, destination, channel) - return TRUE + //We need to be sure that the buckled mobs can teleport too + if(teleatom.has_buckled_mobs()) + for(var/mob/living/rider in teleatom.buckled_mobs) + //just in case it fails, but the mob gets unbuckled anyways even if it passes + teleatom.unbuckle_mob(rider, TRUE, FALSE) + + var/rider_success = do_teleport(rider, destturf, precision, channel=channel, no_effects=TRUE) + if(!rider_success) + continue + + if(get_turf(rider) != destturf) //precision made them teleport somewhere else + to_chat(rider, span_warning("As you reorient your senses, you realize you aren't riding [teleatom] anymore!")) + continue + + // [mob/living].forceMove() forces mobs to unbuckle, so we need to buckle them again + teleatom.buckle_mob(rider, force=TRUE) /proc/tele_play_specials(atom/movable/teleatom, atom/location, datum/effect_system/effect, sound) if(!location) diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index fead0417db9..ceaa53e0455 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -1,15 +1,16 @@ -/mob/camera/ai_eye/remote/holo/setLoc(turf/destination, force_update = FALSE) +/mob/eye/camera/remote/holo/setLoc(turf/destination, force_update = FALSE) // If we're moving outside the space of our projector, then just... don't - var/obj/machinery/holopad/H = origin - if(!H?.move_hologram(eye_user, destination)) + var/obj/machinery/holopad/H = origin_ref?.resolve() + if(!H?.move_hologram(user_ref?.resolve(), destination)) sprint = initial(sprint) // Reset sprint so it doesn't balloon in our calling proc return return ..() /obj/machinery/holopad/remove_eye_control(mob/living/user) - if(user.client) - user.reset_perspective(null) - user.remote_control = null + var/mob/eye/camera/remote/eye = user.remote_control + if(!istype(eye)) + CRASH("Attempted to remove eye control from non-camera eye. Something has gone horribly wrong.") + eye.assign_user(null) //this datum manages its own references @@ -24,7 +25,7 @@ var/list/dialed_holopads ///user's eye, once connected - var/mob/camera/ai_eye/remote/holo/eye + var/mob/eye/camera/remote/holo/eye ///user's hologram, once connected var/obj/effect/overlay/holo_pad_hologram/hologram ///hangup action @@ -155,15 +156,8 @@ hologram = answering_holopad.activate_holo(user) hologram.HC = src - //eyeobj code is horrid, this is the best copypasta I could make - eye = new - eye.origin = answering_holopad - eye.eye_initialized = TRUE - eye.eye_user = user - eye.name = "Camera Eye ([user.name])" - user.remote_control = eye - user.reset_perspective(eye) - eye.setLoc(answering_holopad.loc) + eye = new(get_turf(answering_holopad), answering_holopad) + eye.assign_user(user) hangup = new(eye, src) hangup.Grant(user) diff --git a/code/datums/mind/initialization.dm b/code/datums/mind/initialization.dm index e3b3e8225dc..a09cb040dbe 100644 --- a/code/datums/mind/initialization.dm +++ b/code/datums/mind/initialization.dm @@ -11,8 +11,10 @@ mind.set_current(src) // There's nowhere else to set this up, mind code makes me depressed mind.antag_hud = add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/antagonist_hud, "combo_hud", mind) + RegisterSignal(src, COMSIG_ADMIN_DELETING, PROC_REF(ghost_before_admin_delete), override = TRUE) SEND_SIGNAL(src, COMSIG_MOB_MIND_INITIALIZED, mind) + /mob/living/carbon/mind_initialize() ..() last_mind = mind @@ -35,3 +37,8 @@ . = ..() mind.set_assigned_role(SSjob.get_job_type(/datum/job/personal_ai)) mind.special_role = "" + +/// Signal proc for [COMSIG_ADMIN_DELETING], to ghostize a mob beforehand if an admin is manually deleting it. +/mob/proc/ghost_before_admin_delete(datum/source) + SIGNAL_HANDLER + ghostize(can_reenter_corpse = FALSE) diff --git a/code/datums/mood_events/needs_events.dm b/code/datums/mood_events/needs_events.dm index 93a8f186da6..434110ff27c 100644 --- a/code/datums/mood_events/needs_events.dm +++ b/code/datums/mood_events/needs_events.dm @@ -81,6 +81,24 @@ mood_change = -2 timeout = 3 MINUTES +/datum/mood_event/hot_spring + description = "It's so relaxing to bathe in steamy water..." + mood_change = 5 + +/datum/mood_event/hot_spring_hater + description = "No, no, no, no, I don't want to take a bath!" + mood_change = -2 + +/datum/mood_event/hot_spring_left + description = "That was an enjoyable bath." + mood_change = 4 + timeout = 4 MINUTES + +/datum/mood_event/hot_spring_hater_left + description = "I hate baths! And I hate how cold it's once you step out of it!" + mood_change = -3 + timeout = 2 MINUTES + /datum/mood_event/fresh_laundry description = "There's nothing like the feeling of a freshly laundered jumpsuit." mood_change = 2 diff --git a/code/datums/mutations/_combined.dm b/code/datums/mutations/_combined.dm index 0a71b4bd4ca..58970faba51 100644 --- a/code/datums/mutations/_combined.dm +++ b/code/datums/mutations/_combined.dm @@ -31,12 +31,12 @@ /datum/generecipe/cindikinesis input_one = /datum/mutation/human/geladikinesis - input_two = /datum/mutation/human/firebreath + input_two = /datum/mutation/human/fire // fiery sweat NOT fiery breath result = /datum/mutation/human/cindikinesis /datum/generecipe/pyrokinesis input_one = /datum/mutation/human/cryokinesis - input_two = /datum/mutation/human/firebreath + input_two = /datum/mutation/human/fire // fiery sweat NOT fiery breath result = /datum/mutation/human/pyrokinesis /datum/generecipe/thermal_adaptation diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index 49d00d80d86..570ac76b3a1 100644 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -98,7 +98,7 @@ /** * extra types for chameleon outfit changes, mostly guns * - * Format of this list is (typepath, typepath, typepath) + * Valid values are a single typepath or list of typepaths * * These are all added and returns in the list for get_chamelon_diguise_info proc */ diff --git a/code/datums/proximity_monitor/fields/gravity.dm b/code/datums/proximity_monitor/fields/gravity.dm index 745072d69e1..51a73978886 100644 --- a/code/datums/proximity_monitor/fields/gravity.dm +++ b/code/datums/proximity_monitor/fields/gravity.dm @@ -80,6 +80,7 @@ alpha = 200 /// our emissive appearance var/mutable_appearance/emissive + var/particles/particle_type /obj/gravity_fluff_field/Initialize(mapload, strength) . = ..() @@ -89,20 +90,22 @@ QUEUE_SMOOTH_NEIGHBORS(src) switch(strength) if(2 to INFINITY) - particles = new /particles/grav_field_down/strong() + particle_type = /particles/grav_field_down/strong if(1 to 2) - particles = new /particles/grav_field_down() + particle_type = /particles/grav_field_down if(0 to 1) - particles = new /particles/grav_field_float() + particle_type = /particles/grav_field_float if(-INFINITY to -1) - particles = new /particles/grav_field_up() - color = particles.color + particle_type = /particles/grav_field_up + if (particle_type) + add_shared_particles(/particles/grav_field_down/strong) + color = particle_type::color RegisterSignal(src, COMSIG_ATOM_SMOOTHED_ICON, PROC_REF(smoothed)) /obj/gravity_fluff_field/Destroy(force) - . = ..() - QDEL_NULL(particles) + remove_shared_particles(particle_type) emissive = null + return ..() /obj/gravity_fluff_field/proc/smoothed(datum/source) SIGNAL_HANDLER diff --git a/code/datums/quirks/negative_quirks/numb.dm b/code/datums/quirks/negative_quirks/numb.dm index cd4f28cb302..ee8b86d3426 100644 --- a/code/datums/quirks/negative_quirks/numb.dm +++ b/code/datums/quirks/negative_quirks/numb.dm @@ -10,6 +10,8 @@ /datum/quirk/numb/add(client/client_source) quirk_holder.apply_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy, type) + quirk_holder.add_traits(list(TRAIT_ANALGESIA, QUIRK_TRAIT)) /datum/quirk/numb/remove(client/client_source) quirk_holder.remove_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy, type) + quirk_holder.remove_traits(list(TRAIT_ANALGESIA, QUIRK_TRAIT)) diff --git a/code/datums/quirks/neutral_quirks/heretochromatic.dm b/code/datums/quirks/neutral_quirks/heretochromatic.dm index daa018726ad..96982b4089a 100644 --- a/code/datums/quirks/neutral_quirks/heretochromatic.dm +++ b/code/datums/quirks/neutral_quirks/heretochromatic.dm @@ -27,7 +27,6 @@ return eyes_of_the_holder.eye_color_right = color - eyes_of_the_holder.old_eye_color_right = color eyes_of_the_holder.refresh() if(was_not_hetero) diff --git a/code/datums/quirks/positive_quirks/spacer.dm b/code/datums/quirks/positive_quirks/spacer.dm index 344462703e9..8d1503ccf2f 100644 --- a/code/datums/quirks/positive_quirks/spacer.dm +++ b/code/datums/quirks/positive_quirks/spacer.dm @@ -9,7 +9,7 @@ gain_text = span_notice("You feel at home in space.") lose_text = span_danger("You feel homesick.") icon = FA_ICON_USER_ASTRONAUT - value = 7 + value = 5 quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_CHANGES_APPEARANCE medical_record_text = "Patient is well-adapted to non-terrestrial environments." mail_goodies = list( diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm index 8b07eb57ebd..1a5a0e1259a 100644 --- a/code/datums/station_traits/negative_traits.dm +++ b/code/datums/station_traits/negative_traits.dm @@ -502,7 +502,7 @@ ///The max intensity of a nebula VAR_PROTECTED/maximum_nebula_intensity = 2 HOURS ///How long it takes to go to the next nebula level/intensity - VAR_PROTECTED/intensity_increment_time = INFINITE + VAR_PROTECTED/intensity_increment_time = 30 MINUTES ///Objects that we use to calculate the current shielding level var/list/shielding = list() diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm index 637f2c3a076..df525fa8c81 100644 --- a/code/datums/status_effects/_status_effect.dm +++ b/code/datums/status_effects/_status_effect.dm @@ -6,7 +6,7 @@ /// When set initially / in on_creation, this is how long the status effect lasts in deciseconds. /// While processing, this becomes the world.time when the status effect will expire. /// -1 = infinite duration. - var/duration = -1 + var/duration = STATUS_EFFECT_PERMANENT /// When set initially / in on_creation, this is how long between [proc/tick] calls in deciseconds. /// Note that this cannot be faster than the processing subsystem you choose to fire the effect on. (See: [var/processing_speed]) /// While processing, this becomes the world.time when the next tick will occur. @@ -50,9 +50,13 @@ LAZYADD(owner.status_effects, src) RegisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(remove_effect_on_heal)) - if(duration != -1) + if(duration == INFINITY) + // we will optionally allow INFINITY, because i imagine it'll be convenient in some places, + // but we'll still set it to -1 / STATUS_EFFECT_PERMANENT for proper unified handling + duration = STATUS_EFFECT_PERMANENT + if(duration != STATUS_EFFECT_PERMANENT) duration = world.time + duration - if(tick_interval != -1) + if(tick_interval != STATUS_EFFECT_NO_TICK) tick_interval = world.time + tick_interval if(alert_type) @@ -107,7 +111,7 @@ qdel(src) return - if(tick_interval != -1 && tick_interval < world.time) + if(tick_interval != STATUS_EFFECT_NO_TICK && tick_interval < world.time) var/tick_length = initial(tick_interval) tick(tick_length / (1 SECONDS)) tick_interval = world.time + tick_length @@ -115,7 +119,7 @@ // tick deleted us, no need to continue return - if(duration != -1) + if(duration != STATUS_EFFECT_PERMANENT) if(duration < world.time) qdel(src) return @@ -170,7 +174,7 @@ /// has its duration refreshed in apply_status_effect - is passed New() args /datum/status_effect/proc/refresh(effect, ...) var/original_duration = initial(duration) - if(original_duration == -1) + if(original_duration == STATUS_EFFECT_PERMANENT) return duration = world.time + original_duration @@ -194,7 +198,7 @@ /// Remove [seconds] of duration from the status effect, qdeling / ending if we eclipse the current world time. /datum/status_effect/proc/remove_duration(seconds) - if(duration == -1) // Infinite duration + if(duration == STATUS_EFFECT_PERMANENT) // Infinite duration return FALSE duration -= seconds @@ -213,6 +217,18 @@ SHOULD_CALL_PARENT(FALSE) return +/datum/status_effect/vv_edit_var(var_name, var_value) + . = ..() + if(!.) + return + if(var_name == NAMEOF(src, duration)) + if(var_value == INFINITY) + duration = STATUS_EFFECT_PERMANENT + update_shown_duration() + + if(var_name == NAMEOF(src, show_duration)) + update_shown_duration() + /// Alert base type for status effect alerts /atom/movable/screen/alert/status_effect name = "Curse of Mundanity" diff --git a/code/datums/status_effects/agent_pinpointer.dm b/code/datums/status_effects/agent_pinpointer.dm index c22242be400..653b04f6261 100644 --- a/code/datums/status_effects/agent_pinpointer.dm +++ b/code/datums/status_effects/agent_pinpointer.dm @@ -10,7 +10,7 @@ /datum/status_effect/agent_pinpointer id = "agent_pinpointer" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = PINPOINTER_PING_TIME alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer ///The minimum range to start pointing towards your target. @@ -22,7 +22,7 @@ ///The range until you're considered 'too far away' var/range_far = 16 ///The target we are pointing towards, refreshes every tick. - var/mob/scan_target + var/atom/movable/scan_target /datum/status_effect/agent_pinpointer/tick(seconds_between_ticks) if(!owner) @@ -40,6 +40,9 @@ var/turf/here = get_turf(owner) var/turf/there = get_turf(scan_target) + if(isnull(there)) + scan_target = null + return if(here.z != there.z) linked_alert.icon_state = "pinonnull" return diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 3b197ec7f97..a24e8d4ccda 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -2,7 +2,7 @@ /datum/status_effect/his_grace id = "his_grace" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 0.4 SECONDS alert_type = /atom/movable/screen/alert/status_effect/his_grace var/bloodlust = 0 @@ -75,7 +75,7 @@ /datum/status_effect/blooddrunk id = "blooddrunk" duration = 10 - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/blooddrunk /atom/movable/screen/alert/status_effect/blooddrunk @@ -250,7 +250,7 @@ /datum/status_effect/hippocratic_oath id = "Hippocratic Oath" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 2.5 SECONDS alert_type = null @@ -306,7 +306,7 @@ newRod.activated() if(!itemUser.has_hand_for_held_index(hand)) //If user does not have the corresponding hand anymore, give them one and return the rod to their hand - var/zone = (hand % 2) ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM + var/zone = IS_LEFT_INDEX(hand) ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM if(itemUser.regenerate_limb(zone, FALSE)) itemUser.put_in_hand(newRod, hand, forced = TRUE) else @@ -502,7 +502,7 @@ /datum/status_effect/nest_sustenance id = "nest_sustenance" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 0.4 SECONDS alert_type = /atom/movable/screen/alert/status_effect/nest_sustenance @@ -534,8 +534,8 @@ */ /datum/status_effect/blessing_of_insanity id = "blessing_of_insanity" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/blessing_of_insanity /atom/movable/screen/alert/status_effect/blessing_of_insanity diff --git a/code/datums/status_effects/buffs/bioware/_bioware.dm b/code/datums/status_effects/buffs/bioware/_bioware.dm index c2f7259f270..24443f4f1ee 100644 --- a/code/datums/status_effects/buffs/bioware/_bioware.dm +++ b/code/datums/status_effects/buffs/bioware/_bioware.dm @@ -6,8 +6,8 @@ /datum/status_effect/bioware id = "bioware" alert_type = null - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK /datum/status_effect/bioware/on_apply() if(!ishuman(owner)) diff --git a/code/datums/status_effects/buffs/stun_absorption.dm b/code/datums/status_effects/buffs/stun_absorption.dm index 9532d79c4f5..393a6031e83 100644 --- a/code/datums/status_effects/buffs/stun_absorption.dm +++ b/code/datums/status_effects/buffs/stun_absorption.dm @@ -9,7 +9,7 @@ */ /datum/status_effect/stun_absorption id = "absorb_stun" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null status_type = STATUS_EFFECT_MULTIPLE diff --git a/code/datums/status_effects/death_sound.dm b/code/datums/status_effects/death_sound.dm new file mode 100644 index 00000000000..b71c070729a --- /dev/null +++ b/code/datums/status_effects/death_sound.dm @@ -0,0 +1,46 @@ +/datum/status_effect/death_sound + id = "death_sound" + tick_interval = -1 + alert_type = null + status_type = STATUS_EFFECT_REPLACE + var/death_sound + var/death_sound_volume = 100 + +/datum/status_effect/death_sound/on_creation(mob/living/new_owner, duration, death_sound, death_sound_volume = 100) + src.duration = duration + src.death_sound = death_sound + src.death_sound_volume = death_sound_volume + return ..() + +/datum/status_effect/death_sound/on_apply() + RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(on_death)) + return TRUE + +/datum/status_effect/death_sound/proc/on_death() + SIGNAL_HANDLER + playsound(owner, death_sound, death_sound_volume, FALSE) + +/datum/status_effect/death_sound/on_remove() + UnregisterSignal(owner, COMSIG_LIVING_DEATH) + + +/datum/status_effect/replace_death_sound + id = "replace_death_sound" + tick_interval = -1 + alert_type = null + status_type = STATUS_EFFECT_REPLACE + var/death_sound + var/old_death_sound + +/datum/status_effect/replace_death_sound/on_creation(mob/living/new_owner, duration, death_sound) + src.duration = duration + src.death_sound = death_sound + return ..() + +/datum/status_effect/replace_death_sound/on_apply() + old_death_sound = owner.death_sound + owner.death_sound = death_sound + return TRUE + +/datum/status_effect/replace_death_sound/on_remove() + owner.death_sound = old_death_sound diff --git a/code/datums/status_effects/debuffs/blindness.dm b/code/datums/status_effects/debuffs/blindness.dm index fb87d2fde85..edb10d27ba4 100644 --- a/code/datums/status_effects/debuffs/blindness.dm +++ b/code/datums/status_effects/debuffs/blindness.dm @@ -5,13 +5,18 @@ /// Nearsighted /datum/status_effect/grouped/nearsighted id = "nearsighted" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null // This is not "remove on fullheal" as in practice, // fullheal should instead remove all the sources and in turn cure this /// Static list of signals that, when received, we force an update to our nearsighted overlay - var/static/list/update_signals = list(SIGNAL_ADDTRAIT(TRAIT_NEARSIGHTED_CORRECTED), SIGNAL_REMOVETRAIT(TRAIT_NEARSIGHTED_CORRECTED)) + var/static/list/update_signals = list( + SIGNAL_ADDTRAIT(TRAIT_NEARSIGHTED_CORRECTED), + SIGNAL_REMOVETRAIT(TRAIT_NEARSIGHTED_CORRECTED), + SIGNAL_ADDTRAIT(TRAIT_SIGHT_BYPASS), + SIGNAL_REMOVETRAIT(TRAIT_SIGHT_BYPASS), + ) /// How severe is our nearsightedness right now var/overlay_severity = 2 @@ -37,7 +42,11 @@ var/mob/living/carbon/human/human_owner = owner if (human_owner.get_eye_scars()) return TRUE - return !HAS_TRAIT(owner, TRAIT_NEARSIGHTED_CORRECTED) + if(HAS_TRAIT(owner, TRAIT_NEARSIGHTED_CORRECTED)) + return FALSE + if(HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS)) + return FALSE + return TRUE /// Updates our nearsightd overlay, either removing it if we have the trait or adding it if we don't /datum/status_effect/grouped/nearsighted/proc/update_nearsighted_overlay() @@ -59,8 +68,12 @@ /// Blindness /datum/status_effect/grouped/blindness id = "blindness" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/blind + var/static/list/update_signals = list( + SIGNAL_REMOVETRAIT(TRAIT_SIGHT_BYPASS), + SIGNAL_ADDTRAIT(TRAIT_SIGHT_BYPASS), + ) // This is not "remove on fullheal" as in practice, // fullheal should instead remove all the sources and in turn cure this @@ -68,14 +81,34 @@ if(!CAN_BE_BLIND(owner)) return FALSE + RegisterSignals(owner, update_signals, PROC_REF(update_blindness)) + + update_blindness() + + return ..() + +/datum/status_effect/grouped/blindness/proc/update_blindness() + if(!CAN_BE_BLIND(owner)) // future proofing + qdel(src) + return + + if(HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS)) + make_unblind() + return + make_blind() + +/datum/status_effect/grouped/blindness/proc/make_blind() owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind) // You are blind - at most, able to make out shapes near you owner.add_client_colour(/datum/client_colour/monochrome/blind) - return ..() -/datum/status_effect/grouped/blindness/on_remove() +/datum/status_effect/grouped/blindness/proc/make_unblind() owner.clear_fullscreen(id) owner.remove_client_colour(/datum/client_colour/monochrome/blind) + +/datum/status_effect/grouped/blindness/on_remove() + make_unblind() + UnregisterSignal(owner, update_signals) return ..() /atom/movable/screen/alert/status_effect/blind diff --git a/code/datums/status_effects/debuffs/cursed.dm b/code/datums/status_effects/debuffs/cursed.dm index 8d331bbe90a..083bd678635 100644 --- a/code/datums/status_effects/debuffs/cursed.dm +++ b/code/datums/status_effects/debuffs/cursed.dm @@ -31,6 +31,8 @@ /datum/status_effect/grouped/cursed/Destroy() UnregisterSignal(SSdcs, COMSIG_GLOB_CURSED_SLOT_MACHINE_WON) branded_hand = null + if (smoke_path) + owner.remove_shared_particles(smoke_path) return ..() /// Checks the number of curses we have and returns information back to the slot machine. `max_curse_amount` is set by the slot machine itself. @@ -123,7 +125,6 @@ span_notice("The smoke slowly clears from [owner.name]..."), span_notice("Your skin finally settles down and your throat no longer feels as dry... The brand disappearing confirms that the curse has been lifted."), ) - QDEL_NULL(particle_effect) qdel(src) /// If our owner's stat changes, rapidly surge the damage chance. @@ -140,10 +141,8 @@ /datum/status_effect/grouped/cursed/proc/on_death(mob/living/source, gibbed) SIGNAL_HANDLER - if(gibbed) - return - - QDEL_NULL(particle_effect) + if(!gibbed && smoke_path) + owner.remove_shared_particles(smoke_path) /datum/status_effect/grouped/cursed/update_particles() var/particle_path = /particles/smoke/steam/mild @@ -156,9 +155,10 @@ if(smoke_path == particle_path) return - QDEL_NULL(particle_effect) + if (smoke_path) + owner.remove_shared_particles(smoke_path) + owner.add_shared_particles(particle_path) smoke_path = particle_path - particle_effect = new(owner, particle_path) /datum/status_effect/grouped/cursed/tick(seconds_between_ticks) if(curse_count <= 1) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 806bbc39d8b..25c133e46d1 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -8,7 +8,7 @@ //Largely negative status effects go here, even if they have small beneficial effects //STUN EFFECTS /datum/status_effect/incapacitating - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_REPLACE alert_type = null remove_on_fullheal = TRUE @@ -139,7 +139,7 @@ if(!.) return if(HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE)) - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK else ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_SLEEPIMMUNE), PROC_REF(on_owner_insomniac)) @@ -156,7 +156,7 @@ /datum/status_effect/incapacitating/sleeping/proc/on_owner_insomniac(mob/living/source) SIGNAL_HANDLER REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK ///If the mob has the TRAIT_SLEEPIMMUNE but somehow looses it we make him sleep and restart the tick() /datum/status_effect/incapacitating/sleeping/proc/on_owner_sleepy(mob/living/source) @@ -256,7 +256,7 @@ //STASIS /datum/status_effect/grouped/stasis id = "stasis" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/stasis var/last_dead_time @@ -308,7 +308,7 @@ /datum/status_effect/his_wrath //does minor damage over time unless holding His Grace id = "his_wrath" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 0.4 SECONDS alert_type = /atom/movable/screen/alert/status_effect/his_wrath @@ -331,7 +331,7 @@ /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes id = "cult_ghost" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null /datum/status_effect/cultghost/on_apply() @@ -401,7 +401,7 @@ id = "neck_slice" status_type = STATUS_EFFECT_UNIQUE alert_type = null - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/status_effect/neck_slice/on_apply() if(!ishuman(owner)) @@ -526,7 +526,7 @@ /datum/status_effect/gonbola_pacify id = "gonbolaPacify" status_type = STATUS_EFFECT_MULTIPLE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null /datum/status_effect/gonbola_pacify/on_apply() @@ -874,6 +874,7 @@ name = "Ants!" desc = span_warning("JESUS FUCKING CHRIST! CLICK TO GET THOSE THINGS OFF!") icon_state = "antalert" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/ants/Click() . = ..() diff --git a/code/datums/status_effects/debuffs/dna_transformation.dm b/code/datums/status_effects/debuffs/dna_transformation.dm index 33b6eb1d913..150ca8fa96c 100644 --- a/code/datums/status_effects/debuffs/dna_transformation.dm +++ b/code/datums/status_effects/debuffs/dna_transformation.dm @@ -2,7 +2,7 @@ /// then turns them back to how they were before transformation. /datum/status_effect/temporary_transformation id = "temp_dna_transformation" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK duration = 1 MINUTES // set in on creation, this just needs to be any value to process alert_type = null /// A reference to a COPY of the DNA that the mob will be transformed into. @@ -16,7 +16,7 @@ QDEL_NULL(old_dna) /datum/status_effect/temporary_transformation/on_creation(mob/living/new_owner, new_duration = 1 MINUTES, datum/dna/dna_to_copy) - src.duration = (new_duration == INFINITY) ? -1 : new_duration + src.duration = new_duration src.new_dna = new() src.old_dna = new() dna_to_copy.copy_dna(new_dna) @@ -79,11 +79,11 @@ // Pause if we're dead, appear dead, or in stasis if(source.stat == DEAD || HAS_TRAIT(source, TRAIT_DEATHCOMA) || HAS_TRAIT(source, TRAIT_STASIS)) - if(duration == -1) + if(duration == STATUS_EFFECT_PERMANENT) return // Already paused time_before_pause = duration - world.time - duration = -1 + duration = STATUS_EFFECT_PERMANENT // Resume if we're none of the above and also were paused else if(time_before_pause != -1) diff --git a/code/datums/status_effects/debuffs/drunk.dm b/code/datums/status_effects/debuffs/drunk.dm index a1ff3c7fb4e..706ab36fcff 100644 --- a/code/datums/status_effects/debuffs/drunk.dm +++ b/code/datums/status_effects/debuffs/drunk.dm @@ -106,6 +106,7 @@ . = ..() owner.sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC owner.add_mood_event(id, /datum/mood_event/drunk) + RegisterSignal(owner, COMSIG_MOB_FIRED_GUN, PROC_REF(drunk_gun_fired)) /datum/status_effect/inebriated/drunk/on_remove() clear_effects() @@ -123,6 +124,19 @@ if(owner.sound_environment_override == SOUND_ENVIRONMENT_PSYCHOTIC) owner.sound_environment_override = SOUND_ENVIRONMENT_NONE + UnregisterSignal(owner, COMSIG_MOB_FIRED_GUN) + +/datum/status_effect/inebriated/drunk/proc/drunk_gun_fired(datum/source, obj/item/gun/gun, atom/firing_at, params, zone, bonus_spread_values) + SIGNAL_HANDLER + + // excusing the bartender, because shotgun + if(HAS_TRAIT(owner, TRAIT_DRUNKEN_BRAWLER)) + return + // what makes me a good demoman? + if(istype(gun, /obj/item/gun/grenadelauncher) || istype(gun, /obj/item/gun/ballistic/revolver/grenadelauncher)) + return + bonus_spread_values[MAX_BONUS_SPREAD_INDEX] += (drunk_value * 0.5) + /datum/status_effect/inebriated/drunk/set_drunk_value(set_to) . = ..() if(QDELETED(src)) diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index a575d2619fe..8a629dc14ef 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -1,5 +1,5 @@ /datum/status_effect/fire_handler - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null status_type = STATUS_EFFECT_REFRESH //Custom code on_remove_on_mob_delete = TRUE @@ -135,6 +135,8 @@ var/obj/effect/dummy/lighting_obj/moblight /// Type of mob light emitter we use when on fire var/moblight_type = /obj/effect/dummy/lighting_obj/moblight/fire + /// Cached particle type + var/cached_state /datum/status_effect/fire_handler/fire_stacks/get_examine_text() if(owner.on_fire) @@ -153,6 +155,8 @@ /datum/status_effect/fire_handler/fire_stacks/on_remove() UnregisterSignal(owner, COMSIG_ATOM_TOUCHED_SPARKS) + if (cached_state) + owner.remove_shared_particles(cached_state) /datum/status_effect/fire_handler/fire_stacks/tick(seconds_between_ticks) if(stacks <= 0) @@ -177,15 +181,23 @@ deal_damage(seconds_between_ticks) /datum/status_effect/fire_handler/fire_stacks/update_particles() - if(on_fire) - if(!particle_effect) - particle_effect = new(owner, /particles/embers) - if(stacks > MOB_BIG_FIRE_STACK_THRESHOLD) - particle_effect.particles.spawning = 5 - else - particle_effect.particles.spawning = 1 - else if(particle_effect) - QDEL_NULL(particle_effect) + if (!on_fire) + if (cached_state) + owner.remove_shared_particles(cached_state) + cached_state = null + return + + var/particle_type = /particles/embers/minor + if(stacks > MOB_BIG_FIRE_STACK_THRESHOLD) + particle_type = /particles/embers + + if (cached_state == particle_type) + return + + if (cached_state) + owner.remove_shared_particles(cached_state) + owner.add_shared_particles(particle_type) + cached_state = particle_type /** * Proc that handles damage dealing and all special effects @@ -300,7 +312,7 @@ enemy_types = list(/datum/status_effect/fire_handler/fire_stacks) stack_modifier = -1 - ///If the mob has the TRAIT_SLIPPERY_WHEN_WET trait, the mob gets this component while it's wet + /// If the mob has the TRAIT_SLIPPERY_WHEN_WET trait, the mob gets this component while it's wet var/datum/component/slippery/slipperiness /datum/status_effect/fire_handler/wet_stacks/on_apply() @@ -312,12 +324,14 @@ if(HAS_TRAIT(owner, TRAIT_SLIPPERY_WHEN_WET)) become_slippery() ADD_TRAIT(owner, TRAIT_IS_WET, TRAIT_STATUS_EFFECT(id)) + owner.add_shared_particles(/particles/droplets) /datum/status_effect/fire_handler/wet_stacks/on_remove() . = ..() REMOVE_TRAIT(owner, TRAIT_IS_WET, TRAIT_STATUS_EFFECT(id)) if(HAS_TRAIT(owner, TRAIT_SLIPPERY_WHEN_WET)) no_longer_slippery() + owner.remove_shared_particles(/particles/droplets) /datum/status_effect/fire_handler/wet_stacks/proc/update_wet_stack_modifier() SIGNAL_HANDLER @@ -342,10 +356,5 @@ if(stacks <= 0) qdel(src) -/datum/status_effect/fire_handler/wet_stacks/update_particles() - if(particle_effect) - return - particle_effect = new(owner, /particles/droplets) - /datum/status_effect/fire_handler/wet_stacks/check_basic_mob_immunity(mob/living/basic/basic_owner) return !(basic_owner.basic_mob_flags & IMMUNE_TO_GETTING_WET) diff --git a/code/datums/status_effects/debuffs/genetic_damage.dm b/code/datums/status_effects/debuffs/genetic_damage.dm index 21b6f1db218..21435f476c0 100644 --- a/code/datums/status_effects/debuffs/genetic_damage.dm +++ b/code/datums/status_effects/debuffs/genetic_damage.dm @@ -5,7 +5,7 @@ id = "genetic_damage" alert_type = null status_type = STATUS_EFFECT_REFRESH // New effects will add to total_damage - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 2 SECONDS on_remove_on_mob_delete = TRUE // Need to unregister from owner, be_replaced() would cause runtimes remove_on_fullheal = TRUE diff --git a/code/datums/status_effects/debuffs/hallucination.dm b/code/datums/status_effects/debuffs/hallucination.dm index 22c74f72cbd..66e85f1900a 100644 --- a/code/datums/status_effects/debuffs/hallucination.dm +++ b/code/datums/status_effects/debuffs/hallucination.dm @@ -14,27 +14,39 @@ /// The cooldown for when the next hallucination can occur COOLDOWN_DECLARE(hallucination_cooldown) -/datum/status_effect/hallucination/on_creation(mob/living/new_owner, duration) +/datum/status_effect/hallucination/on_creation(mob/living/new_owner, duration, lower_tick_interval, upper_tick_interval) if(isnum(duration)) src.duration = duration + if(isnum(lower_tick_interval)) + src.lower_tick_interval = lower_tick_interval + if(isnum(upper_tick_interval)) + src.upper_tick_interval = upper_tick_interval return ..() /datum/status_effect/hallucination/on_apply() if(owner.mob_biotypes & barred_biotypes) return FALSE + if(HAS_TRAIT(owner, TRAIT_HALLUCINATION_IMMUNE)) + return FALSE RegisterSignal(owner, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_health_scan)) + RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_HALLUCINATION_IMMUNE), PROC_REF(delete_self)) if(iscarbon(owner)) RegisterSignal(owner, COMSIG_CARBON_CHECKING_BODYPART, PROC_REF(on_check_bodypart)) RegisterSignal(owner, COMSIG_CARBON_BUMPED_AIRLOCK_OPEN, PROC_REF(on_bump_airlock)) return TRUE +/datum/status_effect/hallucination/proc/delete_self() + SIGNAL_HANDLER + qdel(src) + /datum/status_effect/hallucination/on_remove() UnregisterSignal(owner, list( COMSIG_LIVING_HEALTHSCAN, COMSIG_CARBON_CHECKING_BODYPART, COMSIG_CARBON_BUMPED_AIRLOCK_OPEN, + SIGNAL_ADDTRAIT(TRAIT_HALLUCINATION_IMMUNE), )) /// Signal proc for [COMSIG_LIVING_HEALTHSCAN]. Show we're hallucinating to (advanced) scanners. @@ -82,7 +94,7 @@ /datum/status_effect/hallucination/sanity id = "low sanity" status_type = STATUS_EFFECT_REFRESH - duration = -1 // This lasts "forever", only goes away with sanity gain + duration = STATUS_EFFECT_PERMANENT // This lasts "forever", only goes away with sanity gain /datum/status_effect/hallucination/sanity/on_health_scan(datum/source, list/render_list, advanced, mob/user, mode, tochat) return diff --git a/code/datums/status_effects/debuffs/hooked.dm b/code/datums/status_effects/debuffs/hooked.dm index 3eb7caf1c37..8d160305b54 100644 --- a/code/datums/status_effects/debuffs/hooked.dm +++ b/code/datums/status_effects/debuffs/hooked.dm @@ -1,8 +1,8 @@ ///Status effect applied when casting a fishing rod at someone, provided the attached fishing hook allows it. /datum/status_effect/grouped/hooked id = "hooked" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_MULTIPLE alert_type = /atom/movable/screen/alert/status_effect/hooked @@ -29,6 +29,7 @@ name = "Snagged By Hook" desc = "You're being caught like a fish by some asshat! Click to safely remove the hook or move away far enough to snap it off." icon_state = "hooked" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/hooked/Click() . = ..() diff --git a/code/datums/status_effects/debuffs/screen_blur.dm b/code/datums/status_effects/debuffs/screen_blur.dm index abdd07d3cd5..acdce13b5a0 100644 --- a/code/datums/status_effects/debuffs/screen_blur.dm +++ b/code/datums/status_effects/debuffs/screen_blur.dm @@ -18,7 +18,8 @@ return FALSE // Refresh the blur when a client jumps into the mob, in case we get put on a clientless mob with no hud - RegisterSignal(owner, COMSIG_MOB_LOGIN, PROC_REF(update_blur)) + RegisterSignals(owner, list(COMSIG_MOB_LOGIN, SIGNAL_ADDTRAIT(TRAIT_SIGHT_BYPASS), SIGNAL_REMOVETRAIT(TRAIT_SIGHT_BYPASS)), PROC_REF(update_blur)) + // Apply initial blur update_blur() return TRUE @@ -43,10 +44,13 @@ if(!owner.hud_used) return + var/atom/movable/plane_master_controller/game_plane_master_controller = owner.hud_used.plane_master_controllers[PLANE_MASTERS_GAME] + if(HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS)) + game_plane_master_controller.remove_filter("eye_blur") + return + var/time_left_in_seconds = (duration - world.time) / (1 SECONDS) var/amount_of_blur = clamp(time_left_in_seconds * BLUR_DURATION_TO_INTENSITY, 0.6, 3) - - var/atom/movable/plane_master_controller/game_plane_master_controller = owner.hud_used.plane_master_controllers[PLANE_MASTERS_GAME] game_plane_master_controller.add_filter("eye_blur", 1, gauss_blur_filter(amount_of_blur)) #undef BLUR_DURATION_TO_INTENSITY diff --git a/code/datums/status_effects/debuffs/slime/slime_food.dm b/code/datums/status_effects/debuffs/slime/slime_food.dm index 538e62e27c5..259f6c8829a 100644 --- a/code/datums/status_effects/debuffs/slime/slime_food.dm +++ b/code/datums/status_effects/debuffs/slime/slime_food.dm @@ -21,9 +21,11 @@ RegisterSignal(owner, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_feeder_deleted)) RegisterSignal(owner, COMSIG_SLIME_DRAINED, PROC_REF(on_drained)) RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - return ..() +/datum/status_effect/slime_food/on_remove() + feeder = null + ///Handles the source of the pheromones getting deleted, or the owner getting washed /datum/status_effect/slime_food/proc/on_feeder_deleted(datum/source) SIGNAL_HANDLER @@ -51,6 +53,3 @@ draining_slime.befriend(feeder) new /obj/effect/temp_visual/heart(draining_slime.loc) qdel(src) - -/datum/status_effect/slime_food/on_remove() - feeder = null diff --git a/code/datums/status_effects/debuffs/slime/slimed.dm b/code/datums/status_effects/debuffs/slime/slimed.dm index 2540c4df513..32fa9048223 100644 --- a/code/datums/status_effects/debuffs/slime/slimed.dm +++ b/code/datums/status_effects/debuffs/slime/slimed.dm @@ -7,6 +7,7 @@ name = "Covered in Slime" desc = "You are covered in slime and it's eating away at you! Click to start cleaning it off, or find a faster way to wash it away!" icon_state = "slimed" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/slimed/Click() . = ..() @@ -65,6 +66,14 @@ to_chat(owner, span_userdanger("You have been covered in a thick layer of slime! Find a way to wash it off!")) return ..() +/datum/status_effect/slimed/on_remove() + owner.remove_shared_particles(rainbow ? "slimed_rainbow" : "slimed_[slime_color]") + +/datum/status_effect/slimed/update_particles() + var/obj/effect/abstract/shared_particle_holder/holder = owner.add_shared_particles(rainbow ? /particles/slime/rainbow : /particles/slime, rainbow ? "slimed_rainbow" : "slimed_[slime_color]") + if (!rainbow) + holder.particles.color = "[slime_color]a0" + /datum/status_effect/slimed/proc/remove_stacks(stacks_to_remove = 1) slime_stacks -= stacks_to_remove // lose 1 stack per second if(slime_stacks <= 0) diff --git a/code/datums/status_effects/debuffs/speech_debuffs.dm b/code/datums/status_effects/debuffs/speech_debuffs.dm index a84ca1f16ab..669ee0f68b0 100644 --- a/code/datums/status_effects/debuffs/speech_debuffs.dm +++ b/code/datums/status_effects/debuffs/speech_debuffs.dm @@ -2,7 +2,7 @@ id = null alert_type = null remove_on_fullheal = TRUE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /// If TRUE, TTS will say the original message rather than what we changed it to var/make_tts_message_original = FALSE /// If set, this will be appended to the TTS filter of the message diff --git a/code/datums/status_effects/debuffs/stamcrit.dm b/code/datums/status_effects/debuffs/stamcrit.dm index c0359c7ddf3..74c3fde12f5 100644 --- a/code/datums/status_effects/debuffs/stamcrit.dm +++ b/code/datums/status_effects/debuffs/stamcrit.dm @@ -1,8 +1,8 @@ /datum/status_effect/incapacitating/stamcrit status_type = STATUS_EFFECT_UNIQUE // Lasts until we go back to 0 stamina, which is handled by the mob - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK /// Cooldown between displaying warning messages that we hit diminishing returns COOLDOWN_DECLARE(warn_cd) /// A counter that tracks every time we've taken enough damage to trigger diminishing returns diff --git a/code/datums/status_effects/debuffs/strandling.dm b/code/datums/status_effects/debuffs/strandling.dm index 0ce0ad41882..d37961c13c9 100644 --- a/code/datums/status_effects/debuffs/strandling.dm +++ b/code/datums/status_effects/debuffs/strandling.dm @@ -88,6 +88,7 @@ desc = "Strands of Durathread are wrapped around your neck, preventing you from breathing! Click this icon to remove the strand." icon_state = "his_grace" alerttooltipstyle = "hisgrace" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/strandling/Click(location, control, params) . = ..() diff --git a/code/datums/status_effects/debuffs/tower_of_babel.dm b/code/datums/status_effects/debuffs/tower_of_babel.dm index a56ea1ac6d9..bd2d38e7546 100644 --- a/code/datums/status_effects/debuffs/tower_of_babel.dm +++ b/code/datums/status_effects/debuffs/tower_of_babel.dm @@ -32,7 +32,7 @@ // Used by wizard magic and tower of babel event /datum/status_effect/tower_of_babel/magical id = "tower_of_babel_magic" // do we need a new id? - duration = -1 + duration = STATUS_EFFECT_PERMANENT trait_source = TRAUMA_TRAIT /datum/status_effect/tower_of_babel/magical/on_apply() diff --git a/code/datums/status_effects/drug_effects.dm b/code/datums/status_effects/drug_effects.dm index bb86e2b014b..7e8466f3372 100644 --- a/code/datums/status_effects/drug_effects.dm +++ b/code/datums/status_effects/drug_effects.dm @@ -1,6 +1,6 @@ /datum/status_effect/woozy id = "woozy" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/woozy @@ -14,7 +14,7 @@ /datum/status_effect/high_blood_pressure id = "high_blood_pressure" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/high_blood_pressure @@ -40,7 +40,7 @@ /datum/status_effect/seizure id = "seizure" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/seizure @@ -65,19 +65,13 @@ duration = 10 SECONDS alert_type = /atom/movable/screen/alert/status_effect/stoned status_type = STATUS_EFFECT_REFRESH - var/original_eye_color_left - var/original_eye_color_right /datum/status_effect/stoned/on_apply() if(!ishuman(owner)) return FALSE var/mob/living/carbon/human/human_owner = owner - original_eye_color_left = human_owner.eye_color_left - original_eye_color_right = human_owner.eye_color_right human_owner.add_movespeed_modifier(/datum/movespeed_modifier/reagent/cannabis) //slows you down - human_owner.eye_color_left = BLOODCULT_EYE //makes cult eyes less obvious - human_owner.eye_color_right = BLOODCULT_EYE //makes cult eyes less obvious - human_owner.update_body() //updates eye color + human_owner.add_eye_color(BLOODCULT_EYE, EYE_COLOR_WEED_PRIORITY) //makes cult eyes less obvious human_owner.add_traits(list(TRAIT_CLUMSY, TRAIT_BLOODSHOT_EYES), type) // impairs motor coordination and dilates blood vessels in eyes human_owner.add_mood_event("stoned", /datum/mood_event/stoned) //improves mood human_owner.sound_environment_override = SOUND_ENVIRONMENT_DRUGGED //not realistic but very immersive @@ -88,9 +82,7 @@ return var/mob/living/carbon/human/human_owner = owner human_owner.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/cannabis) - human_owner.eye_color_left = original_eye_color_left - human_owner.eye_color_right = original_eye_color_right - human_owner.update_body() + human_owner.remove_eye_color(EYE_COLOR_WEED_PRIORITY) human_owner.remove_traits(list(TRAIT_CLUMSY, TRAIT_BLOODSHOT_EYES), type) human_owner.clear_mood_event("stoned") human_owner.sound_environment_override = SOUND_ENVIRONMENT_NONE diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index f06a289da78..1dc39e81bf2 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -55,7 +55,7 @@ /datum/status_effect/freon/lasting id = "lasting_frozen" - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/status_effect/hypernob_protection id = "hypernob_protection" diff --git a/code/datums/status_effects/limited_effect.dm b/code/datums/status_effects/limited_effect.dm index 0f56e72da52..b577248d35e 100644 --- a/code/datums/status_effects/limited_effect.dm +++ b/code/datums/status_effects/limited_effect.dm @@ -1,7 +1,7 @@ /// These effects reapply their on_apply() effect when refreshed while stacks < max_stacks. /datum/status_effect/limited_buff id = "limited_buff" - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_REFRESH ///How many stacks we currently have var/stacks = 1 diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 0056c55a7f6..43c4ad7a3f0 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -2,8 +2,8 @@ /datum/status_effect/crusher_damage id = "crusher_damage" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = null /// How much damage? @@ -71,7 +71,7 @@ /datum/status_effect/in_love id = "in_love" - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/in_love var/hearts @@ -146,8 +146,8 @@ // heldup is for the person being aimed at /datum/status_effect/grouped/heldup id = "heldup" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_MULTIPLE alert_type = /atom/movable/screen/alert/status_effect/heldup @@ -167,8 +167,8 @@ // holdup is for the person aiming /datum/status_effect/holdup id = "holdup" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/holdup @@ -176,6 +176,7 @@ name = "Holding Up" desc = "You're currently pointing a gun at someone. Click to cancel." icon_state = "aimed" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/holdup/Click(location, control, params) . = ..() @@ -187,8 +188,8 @@ // this status effect is used to negotiate the high-fiving capabilities of all concerned parties /datum/status_effect/offering id = "offering" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = null /// The people who were offered this item at the start @@ -349,8 +350,8 @@ //this effect gives the user an alert they can use to surrender quickly /datum/status_effect/grouped/surrender id = "surrender" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/surrender @@ -358,6 +359,7 @@ name = "Surrender" desc = "Looks like you're in trouble now, bud. Click here to surrender. (Warning: You will be incapacitated.)" icon_state = "surrender" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/surrender/Click(location, control, params) . = ..() @@ -394,7 +396,7 @@ /datum/status_effect/caltropped id = "caltropped" duration = 1 SECONDS - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_REFRESH alert_type = null @@ -596,8 +598,8 @@ /datum/status_effect/gutted id = "gutted" alert_type = null - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK /datum/status_effect/gutted/on_apply() RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(stop_gutting)) @@ -610,40 +612,71 @@ SIGNAL_HANDLER qdel(src) -/atom/movable/screen/alert/status_effect/shower_regen - name = "Washing" - desc = "A good wash fills me with energy!" - icon_state = "shower_regen" - -/atom/movable/screen/alert/status_effect/shower_regen/hater - name = "Washing" - desc = "Waaater... Fuck this WATER!!" - icon_state = "shower_regen_catgirl" - -/datum/status_effect/shower_regen +/datum/status_effect/washing_regen id = "shower_regen" - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_UNIQUE - alert_type = /atom/movable/screen/alert/status_effect/shower_regen - /// How many heals from washing. - var/stamina_heal_per_tick = 4 - -/datum/status_effect/shower_regen/on_apply() + alert_type = /atom/movable/screen/alert/status_effect/washing_regen + ///The screen alert shown if you hate water + var/hater_alert = /atom/movable/screen/alert/status_effect/washing_regen/hater + /// How much stamina we regain from washing + var/stamina_heal_per_tick = -4 + /// How much brute, tox and fie damage we heal from this + var/heal_per_tick = 0 + +/datum/status_effect/washing_regen/on_apply() . = ..() if(HAS_TRAIT(owner, TRAIT_WATER_HATER) && !HAS_TRAIT(owner, TRAIT_WATER_ADAPTATION)) - alert_type = /atom/movable/screen/alert/status_effect/shower_regen/hater + alert_type = hater_alert -/datum/status_effect/shower_regen/tick(seconds_between_ticks) +/datum/status_effect/washing_regen/tick(seconds_between_ticks) . = ..() var/water_adaptation = HAS_TRAIT(owner, TRAIT_WATER_ADAPTATION) - var/heal_or_deal = HAS_TRAIT(owner, TRAIT_WATER_HATER) && !water_adaptation ? 1 : -1 + var/water_hater = HAS_TRAIT(owner, TRAIT_WATER_HATER) + var/stam_recovery = (water_hater && !water_adaptation ? -stamina_heal_per_tick : stamina_heal_per_tick) * seconds_between_ticks + var/recovery = heal_per_tick + if(water_adaptation) + recovery -= 1 + stam_recovery *= 1.5 + else if(water_hater) + recovery *= 0 + recovery *= seconds_between_ticks + var/healed = 0 - if(water_adaptation) //very mild healing for those with the water adaptation trait (fish infusion) - healed += owner.adjustOxyLoss(-1.5 * seconds_between_ticks, updating_health = FALSE, required_biotype = MOB_ORGANIC) - healed += owner.adjustFireLoss(-1 * seconds_between_ticks, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC) - healed += owner.adjustToxLoss(-1 * seconds_between_ticks, updating_health = FALSE, required_biotype = MOB_ORGANIC) - healed += owner.adjustBruteLoss(-1 * seconds_between_ticks, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC) - heal_or_deal *= 1.5 - healed += owner.adjustStaminaLoss(stamina_heal_per_tick * heal_or_deal * seconds_between_ticks, updating_stamina = FALSE) + if(recovery) //very mild healing for those with the water adaptation trait (fish infusion) + healed += owner.adjustOxyLoss(recovery * (water_adaptation ? 1.5 : 1), updating_health = FALSE, required_biotype = MOB_ORGANIC) + healed += owner.adjustFireLoss(recovery, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC) + healed += owner.adjustToxLoss(recovery, updating_health = FALSE, required_biotype = MOB_ORGANIC) + healed += owner.adjustBruteLoss(recovery, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC) + healed += owner.adjustStaminaLoss(stam_recovery, updating_stamina = FALSE) if(healed) owner.updatehealth() + +/atom/movable/screen/alert/status_effect/washing_regen + name = "Washing" + desc = "A good wash fills me with energy!" + icon_state = "shower_regen" + +/atom/movable/screen/alert/status_effect/washing_regen/hater + desc = "Waaater... Fuck this WATER!!" + icon_state = "shower_regen_catgirl" + +/datum/status_effect/washing_regen/hot_spring + alert_type = /atom/movable/screen/alert/status_effect/washing_regen/hotspring + hater_alert = /atom/movable/screen/alert/status_effect/washing_regen/hotspring/hater + stamina_heal_per_tick = -4.5 + heal_per_tick = -0.4 + +/datum/status_effect/washing_regen/hot_spring/tick(seconds_between_ticks) + . = ..() + owner.adjust_bodytemperature(10 * seconds_between_ticks, 0, T0C + 45) + +/atom/movable/screen/alert/status_effect/washing_regen/hotspring + name = "Hotspring" + desc = "Hot Springs are so relaxing..." + icon_state = "hotspring_regen" + +/atom/movable/screen/alert/status_effect/washing_regen/hotspring/hater + name = "Hotspring" + desc = "Waaater... FUCK THIS HOT WATER!!" + icon_state = "hotspring_regen_catgirl" diff --git a/code/datums/status_effects/stacking_effect.dm b/code/datums/status_effects/stacking_effect.dm index b0d00a92ba0..3ed7846f6ff 100644 --- a/code/datums/status_effects/stacking_effect.dm +++ b/code/datums/status_effects/stacking_effect.dm @@ -2,7 +2,7 @@ /// Status effects that can stack. /datum/status_effect/stacking id = "stacking_base" - duration = -1 // Only removed under specific conditions. + duration = STATUS_EFFECT_PERMANENT // Only removed under specific conditions. tick_interval = 1 SECONDS // Deciseconds between decays, once decay starts alert_type = null /// How many stacks are currently accumulated. diff --git a/code/datums/status_effects/wound_effects.dm b/code/datums/status_effects/wound_effects.dm index 30361dc9cf1..92a3a55ac69 100644 --- a/code/datums/status_effects/wound_effects.dm +++ b/code/datums/status_effects/wound_effects.dm @@ -28,7 +28,7 @@ /datum/status_effect/limp id = "limp" status_type = STATUS_EFFECT_REPLACE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/limp var/msg_stage = 0//so you dont get the most intense messages immediately /// The left leg of the limping person @@ -49,16 +49,18 @@ /datum/status_effect/limp/on_apply() if(!iscarbon(owner)) return FALSE - var/mob/living/carbon/C = owner - left = C.get_bodypart(BODY_ZONE_L_LEG) - right = C.get_bodypart(BODY_ZONE_R_LEG) + var/mob/living/carbon/carbon_owner = owner + left = carbon_owner.get_bodypart(BODY_ZONE_L_LEG) + right = carbon_owner.get_bodypart(BODY_ZONE_R_LEG) update_limp() - RegisterSignal(C, COMSIG_MOVABLE_MOVED, PROC_REF(check_step)) - RegisterSignals(C, list(COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_POST_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB), PROC_REF(update_limp)) + RegisterSignal(carbon_owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_step)) + RegisterSignals(carbon_owner, list(COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_POST_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB), PROC_REF(update_limp)) return TRUE /datum/status_effect/limp/on_remove() UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_POST_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB)) + left = null + right = null /atom/movable/screen/alert/status_effect/limp name = "Limping" @@ -119,7 +121,6 @@ C.remove_status_effect(src) return - ///////////////////////// //////// WOUNDS ///////// ///////////////////////// diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 2cf18c0767a..856c1204c72 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -47,6 +47,8 @@ var/rustle_vary = TRUE /// Path for the item's rustle sound. var/rustle_sound = SFX_RUSTLE + /// Path for the item's rustle sound when removing items. + var/remove_rustle_sound = null /// The sound to play when we open/access the storage var/open_sound var/open_sound_vary = TRUE @@ -563,7 +565,10 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) thing.forceMove(remove_to_loc) if(do_rustle && !silent) - playsound(parent, SFX_RUSTLE, 50, TRUE, -5) + if(remove_rustle_sound) + playsound(parent, remove_rustle_sound, 50, TRUE, -5) + else + playsound(parent, rustle_sound, 50, TRUE, -5) else thing.moveToNullspace() @@ -688,10 +693,12 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) /datum/storage/proc/on_preattack(datum/source, obj/item/thing, mob/user, params) SIGNAL_HANDLER - if(!istype(thing) || !allow_quick_gather || thing.atom_storage) + if(!istype(thing) || thing == parent.loc || !allow_quick_gather || thing.atom_storage) return if(collection_mode == COLLECT_ONE) + if(thing.loc == user) + user.dropItemToGround(thing, silent = TRUE) //this is nessassary to update any inventory slot it is attached to attempt_insert(thing, user) return COMPONENT_CANCEL_ATTACK_CHAIN diff --git a/code/datums/storage/subtypes/backpack.dm b/code/datums/storage/subtypes/backpack.dm index a6b3e1477dd..a2a1a13fcd4 100644 --- a/code/datums/storage/subtypes/backpack.dm +++ b/code/datums/storage/subtypes/backpack.dm @@ -2,6 +2,16 @@ max_total_storage = 21 max_slots = 21 +/datum/storage/backpack/New( + atom/parent, + max_slots, + max_specific_storage, + max_total_storage, +) + . = ..() + var/static/list/exception_cache = typecacheof(list(/obj/item/fish_tank)) + exception_hold = exception_cache + /datum/storage/backpack/santabag max_total_storage = 60 max_slots = 21 diff --git a/code/datums/storage/subtypes/duffel_bag.dm b/code/datums/storage/subtypes/duffel_bag.dm index cfa073da5e5..bb3a236d38b 100644 --- a/code/datums/storage/subtypes/duffel_bag.dm +++ b/code/datums/storage/subtypes/duffel_bag.dm @@ -2,6 +2,16 @@ max_total_storage = 30 max_slots = 21 +/datum/storage/duffel/New( + atom/parent, + max_slots, + max_specific_storage, + max_total_storage, +) + . = ..() + var/static/list/exception_cache = typecacheof(list(/obj/item/fish_tank)) + exception_hold = exception_cache + // Syndi bags get some FUN extras // You can fit any 2 bulky objects (assuming they're in the whitelist) // Should have traitorus stuff in here, not just useful big things @@ -60,6 +70,9 @@ /obj/item/storage/bag/money, // Heads! /obj/item/bodypart/head, + // Fish + /obj/item/fish, + /obj/item/fish_tank, ) // We keep the type list and the typecache list separate... diff --git a/code/datums/storage/subtypes/fish_case.dm b/code/datums/storage/subtypes/fish_case.dm index 47103e931b1..e38d55e0e1d 100644 --- a/code/datums/storage/subtypes/fish_case.dm +++ b/code/datums/storage/subtypes/fish_case.dm @@ -7,7 +7,7 @@ . = ..() if(!.) return . - if(!HAS_TRAIT(to_insert, TRAIT_FISH_CASE_COMPATIBILE)) + if(!HAS_TRAIT(to_insert, TRAIT_AQUARIUM_CONTENT)) if(messages && user) user.balloon_alert(user, "can't hold!") return FALSE diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm index beb58fb1ce3..ec38d728981 100644 --- a/code/datums/wires/mulebot.dm +++ b/code/datums/wires/mulebot.dm @@ -29,18 +29,24 @@ if(WIRE_MOTOR1, WIRE_MOTOR2) if(is_cut(WIRE_MOTOR1) && is_cut(WIRE_MOTOR2)) ADD_TRAIT(mule, TRAIT_IMMOBILIZED, MOTOR_LACK_TRAIT) + holder.audible_message(span_hear("The motors of [src] go silent."), null, 1) else REMOVE_TRAIT(mule, TRAIT_IMMOBILIZED, MOTOR_LACK_TRAIT) + holder.audible_message(span_hear("The motors of [src] whir to life!"), null, 1) if(is_cut(WIRE_MOTOR1)) mule.set_varspeed(FAST_MOTOR_SPEED) + holder.audible_message(span_hear("The motors of [src] speed up!"), null, 1) else if(is_cut(WIRE_MOTOR2)) mule.set_varspeed(AVERAGE_MOTOR_SPEED) + holder.audible_message(span_hear("The motors of [src] whir."), null, 1) else mule.set_varspeed(SLOW_MOTOR_SPEED) + holder.audible_message(span_hear("The motors of [src] move gently."), null, 1) if(WIRE_AVOIDANCE) if (!isnull(source)) log_combat(source, mule, "[is_cut(WIRE_AVOIDANCE) ? "cut" : "mended"] the MULE safety wire of") + holder.audible_message(span_hear("Something inside [src] clicks ominously!"), null, 1) /datum/wires/mulebot/on_pulse(wire) var/mob/living/simple_animal/bot/mulebot/mule = holder diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm index 8209fb93fac..6b1e16543d7 100644 --- a/code/datums/wounds/bones.dm +++ b/code/datums/wounds/bones.dm @@ -61,9 +61,10 @@ /datum/wound/blunt/bone/set_victim(new_victim) if (victim) - UnregisterSignal(victim, COMSIG_LIVING_UNARMED_ATTACK) + UnregisterSignal(victim, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_MOB_FIRED_GUN)) if (new_victim) RegisterSignal(new_victim, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(attack_with_hurt_hand)) + RegisterSignal(new_victim, COMSIG_MOB_FIRED_GUN, PROC_REF(firing_with_messed_up_hand)) return ..() @@ -120,19 +121,49 @@ // With a severe or critical wound, you have a 15% or 30% chance to proc pain on hit if(prob((severity - 1) * 15)) // And you have a 70% or 50% chance to actually land the blow, respectively - if(prob(70 - 20 * (severity - 1))) - to_chat(victim, span_userdanger("The fracture in your [limb.plaintext_zone] shoots with pain as you strike [target]!")) - limb.receive_damage(brute=rand(1,5)) + if(HAS_TRAIT(victim, TRAIT_ANALGESIA) || prob(70 - 20 * (severity - 1))) + if(!HAS_TRAIT(victim, TRAIT_ANALGESIA)) + to_chat(victim, span_danger("The fracture in your [limb.plaintext_zone] shoots with pain as you strike [target]!")) + victim.apply_damage(rand(1, 5), BRUTE, limb, wound_bonus = CANT_WOUND, wound_clothing = FALSE) else victim.visible_message(span_danger("[victim] weakly strikes [target] with [victim.p_their()] broken [limb.plaintext_zone], recoiling from pain!"), \ span_userdanger("You fail to strike [target] as the fracture in your [limb.plaintext_zone] lights up in unbearable pain!"), vision_distance=COMBAT_MESSAGE_RANGE) INVOKE_ASYNC(victim, TYPE_PROC_REF(/mob, emote), "scream") victim.Stun(0.5 SECONDS) - limb.receive_damage(brute=rand(3,7)) + victim.apply_damage(rand(3, 7), BRUTE, limb, wound_bonus = CANT_WOUND, wound_clothing = FALSE) return COMPONENT_CANCEL_ATTACK_CHAIN return NONE +/// If we're a human who's firing a gun with a broken arm, we might hurt ourselves doing so +/datum/wound/blunt/bone/proc/firing_with_messed_up_hand(datum/source, obj/item/gun/gun, atom/firing_at, params, zone, bonus_spread_values) + SIGNAL_HANDLER + + switch(limb.body_zone) + if(BODY_ZONE_L_ARM) + // Heavy guns use both hands so they will always get a penalty + // (Yes, this means having two broken arms will make heavy weapons SOOO much worse) + // Otherwise make sure THIS hand is firing THIS gun + if(gun.weapon_weight <= WEAPON_MEDIUM && !IS_LEFT_INDEX(victim.get_held_index_of_item(gun))) + return + + if(BODY_ZONE_R_ARM) + // Ditto but for right arm + if(gun.weapon_weight <= WEAPON_MEDIUM && !IS_RIGHT_INDEX(victim.get_held_index_of_item(gun))) + return + + else + // This is not arm wound, so we don't care + return + + if(gun.recoil > 0 && severity >= WOUND_SEVERITY_SEVERE && prob(25 * (severity - 1))) + if(!HAS_TRAIT(victim, TRAIT_ANALGESIA)) + to_chat(victim, span_danger("The fracture in your [limb.plaintext_zone] explodes with pain as [gun] kicks back!")) + victim.apply_damage(rand(1, 3) * (severity - 1) * gun.weapon_weight, BRUTE, limb, wound_bonus = CANT_WOUND, wound_clothing = FALSE) + + if(!HAS_TRAIT(victim, TRAIT_ANALGESIA)) + bonus_spread_values[MAX_BONUS_SPREAD_INDEX] += (15 * severity * (limb.current_gauze?.splint_factor || 1)) + /datum/wound/blunt/bone/receive_damage(wounding_type, wounding_dmg, wound_bonus) if(!victim || wounding_dmg < WOUND_MINIMUM_DAMAGE) return diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index 19dbc2de330..b6c5b3959ca 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -881,7 +881,7 @@ var/extra_context = "" var/used_name = name - if(isliving(user) || isovermind(user) || isaicamera(user) || (ghost_screentips && isobserver(user))) + if(isliving(user) || isovermind(user) || iscameramob(user) || (ghost_screentips && isobserver(user))) var/obj/item/held_item = user.get_active_held_item() if (user.mob_flags & MOB_HAS_SCREENTIPS_NAME_OVERRIDE) diff --git a/code/game/atom/alternate_appearance.dm b/code/game/atom/alternate_appearance.dm index 8c50760ea45..3089f8d40b6 100644 --- a/code/game/atom/alternate_appearance.dm +++ b/code/game/atom/alternate_appearance.dm @@ -199,18 +199,24 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances) return TRUE return FALSE +/// Only shows the image to one person /datum/atom_hud/alternate_appearance/basic/one_person + /// The guy who gets to see the image var/mob/seer /datum/atom_hud/alternate_appearance/basic/one_person/mobShouldSee(mob/M) - if(M == seer) - return TRUE - return FALSE + return M == seer /datum/atom_hud/alternate_appearance/basic/one_person/New(key, image/I, options = NONE, mob/living/seer) src.seer = seer return ..() +/// Shows the image to everyone but one person +/datum/atom_hud/alternate_appearance/basic/one_person/reversed + +/datum/atom_hud/alternate_appearance/basic/one_person/reversed/mobShouldSee(mob/M) + return M != seer + /datum/atom_hud/alternate_appearance/basic/food_demands /datum/atom_hud/alternate_appearance/basic/heretic diff --git a/code/game/atom/atom_examine.dm b/code/game/atom/atom_examine.dm index fee219f7b4b..2151e3927b9 100644 --- a/code/game/atom/atom_examine.dm +++ b/code/game/atom/atom_examine.dm @@ -1,7 +1,7 @@ /atom /// If non-null, overrides a/an/some in all cases var/article - /// Text that appears preceding the name in examine() + /// Text that appears preceding the name in [/atom/proc/examine_title] var/examine_thats = "That's" /mob/living/carbon/human @@ -11,12 +11,12 @@ examine_thats = "This is" /** - * Called when a mob examines (shift click or verb) this atom + * Called when a mob examines this atom: [/mob/verb/examinate] * * Default behaviour is to get the name and icon of the object and its reagents where * the [TRANSPARENT] flag is set on the reagents holder * - * Produces a signal [COMSIG_ATOM_EXAMINE] + * Produces a signal [COMSIG_ATOM_EXAMINE], for modifying the list returned from this proc */ /atom/proc/examine(mob/user) . = list() @@ -29,8 +29,8 @@ var/tag_string = list() for (var/atom_tag in tags_list) tag_string += (isnull(tags_list[atom_tag]) ? atom_tag : span_tooltip(tags_list[atom_tag], atom_tag)) - // Weird bit but ensures that if the final element has its own "and" we don't add another one - tag_string = english_list(tag_string, and_text = (findtext(tag_string[length(tag_string)], " and ")) ? ", " : " and ") + // some regex to ensure that we don't add another "and" if the final element's main text (not tooltip) has one + tag_string = english_list(tag_string, and_text = (findtext(tag_string[length(tag_string)], regex(@">.*?and .*?<"))) ? " " : " and ") var/post_descriptor = examine_post_descriptor(user) . += "[p_They()] [p_are()] a [tag_string] [examine_descriptor(user)][length(post_descriptor) ? " [jointext(post_descriptor, " ")]" : ""]." @@ -58,14 +58,25 @@ SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .) -/* +/** * A list of "tags" displayed after atom's description in examine. - * This should return an assoc list of tags -> tooltips for them. If item if null, then no tooltip is assigned. + * This should return an assoc list of tags -> tooltips for them. If item is null, then no tooltip is assigned. + * + * * TGUI tooltips (not the main text) in chat cannot use HTML stuff at all, so + * trying something like `ffff` will not work for tooltips. + * * For example: - * list("small" = "This is a small size class item.", "fireproof" = "This item is impervious to fire.") + * ```byond + * . = list() + * .["small"] = "It is a small item." + * .["fireproof"] = "It is made of fire-retardant materials." + * .["and conductive"] = "It's made of conductive materials and whatnot. Blah blah blah." // having "and " in the end tag's main text/key works too! + * ``` * will result in - * This is a small, fireproof item. - * where "item" is pulled from examine_descriptor() proc + * + * It is a *small*, *fireproof* *and conductive* item. + * + * where "item" is pulled from [/atom/proc/examine_descriptor] */ /atom/proc/examine_tags(mob/user) . = list() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 8acbf59b4d8..0b46daebb01 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -514,7 +514,7 @@ /atom/movable/proc/start_pulling(atom/movable/pulled_atom, state, force = move_force, supress_message = FALSE) if(QDELETED(pulled_atom)) return FALSE - if(!(pulled_atom.can_be_pulled(src, state, force))) + if(!(pulled_atom.can_be_pulled(src, force))) return FALSE // If we're pulling something then drop what we're currently pulling and pull this instead. @@ -618,7 +618,7 @@ buckled_mob.set_glide_size(target) /** - * meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts) + * meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like eye mobs or ghosts) * if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this * most of the time you want forceMove() */ @@ -1643,7 +1643,7 @@ /atom/movable/proc/get_cell(atom/movable/interface, mob/user) return -/atom/movable/proc/can_be_pulled(user, grab_state, force) +/atom/movable/proc/can_be_pulled(user, force) if(src == user || !isturf(loc)) return FALSE if(SEND_SIGNAL(src, COMSIG_ATOM_CAN_BE_PULLED, user) & COMSIG_ATOM_CANT_PULL) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 19a385c38cd..da0cf478b4f 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -73,7 +73,7 @@ . = ..() if(!new_viewer || hud_users_all_z_levels.len != 1) return - for(var/mob/camera/ai_eye/eye as anything in GLOB.aiEyes) + for(var/mob/eye/camera/ai/eye as anything in GLOB.camera_eyes) eye.update_ai_detect_hud() /datum/atom_hud/data/malf_apc @@ -541,7 +541,7 @@ Diagnostic HUDs! switch(mode) if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons holder.icon_state = "hudcalled" - if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing + if(BOT_CLEANING, BOT_HEALING) //Cleanbot cleaning, repairbot fixing, or Medibot Healing holder.icon_state = "hudworking" if(BOT_PATROL, BOT_START_PATROL) //Patrol mode holder.icon_state = "hudpatrol" diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 282e7f6a34a..ed9691799ff 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -335,6 +335,13 @@ remove_all_languages(source = LANGUAGE_EMP) grant_random_uncommon_language(source = LANGUAGE_EMP) +/obj/machinery/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) + //takes priority in case material container or other atoms that hook onto item interaction signals won't give it a chance + if(istype(tool, /obj/item/storage/part_replacer)) + return tool.interact_with_atom(src, user, modifiers) + + return ..() + /** * Opens the machine. * diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index 029f4a17ea9..eb66e140d98 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -34,7 +34,7 @@ GLOBAL_LIST_EMPTY(announcement_systems) ///If true, researched nodes will be announced to the appropriate channels var/announce_research_node = TRUE /// The text that we send when announcing researched nodes. - var/node_message = "The '%NODE' techweb node has been researched" + var/node_message = "The %NODE techweb node has been researched" /obj/machinery/announcement_system/Initialize(mapload) . = ..() @@ -164,7 +164,7 @@ GLOBAL_LIST_EMPTY(announcement_systems) newhead = new_message usr.log_message("updated the head announcement to: [new_message]", LOG_GAME) if("node_message") - var/new_message = trim(html_encode(param["new_text"]), MAX_MESSAGE_LEN) + var/new_message = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN) if(new_message) node_message = new_message usr.log_message("updated the researched node announcement to: [node_message]", LOG_GAME) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 220870cb119..3e947d33d7b 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -85,14 +85,14 @@ . += span_warning("An [circuit.name] is installed and should be [EXAMINE_HINT("screwed")] in place.") . += span_notice("The circuit board can be [EXAMINE_HINT("pried")] out.") if(FRAME_COMPUTER_STATE_BOARD_SECURED) - . += span_warning("Its requires [EXAMINE_HINT("5 cable")] pieces to wire it.") + . += span_warning("It can be [EXAMINE_HINT("wired")] with some cable.") . += span_notice("The circuit board can be [EXAMINE_HINT("screwed")] loose.") if(FRAME_COMPUTER_STATE_WIRED) - . += span_notice("The wires can be cut out with [EXAMINE_HINT("wire cutters")].") - . += span_warning("Its requires [EXAMINE_HINT("2 glass")] sheets to complete the screen.") + . += span_notice("The wires can be cut with [EXAMINE_HINT("wirecutters")].") + . += span_warning("There is a slot for 2 [EXAMINE_HINT("glass panels")].") if(FRAME_COMPUTER_STATE_GLASSED) . += span_notice("The screen can be [EXAMINE_HINT("pried")] out.") - . += span_notice("The moniter can be [EXAMINE_HINT("screwed")] to complete it") + . += span_notice("The monitor can be [EXAMINE_HINT("screwed")] on to complete it") /obj/structure/frame/computer/circuit_added(obj/item/circuitboard/added) state = FRAME_COMPUTER_STATE_BOARD_INSTALLED diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 6640f5582fa..5633fe49047 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -8,7 +8,7 @@ var/list/z_lock = list() // Lock use to these z levels var/lock_override = NONE - var/mob/camera/ai_eye/remote/eyeobj + var/mob/eye/camera/remote/eyeobj var/mob/living/current_user = null var/list/networks = list(CAMERANET_NETWORK_SS13) /// Typepath of the action button we use as "off" @@ -76,27 +76,33 @@ /obj/machinery/computer/camera_advanced/syndie/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) return //For syndie nuke shuttle, to spy for station. +/** + * Initializes a camera eye. + * Returns TRUE if initialization was successful. + * Will return nothing if it runtimes. + */ /obj/machinery/computer/camera_advanced/proc/CreateEye() - eyeobj = new() - eyeobj.origin = src + if(eyeobj) + CRASH("Tried to make another eyeobj for some reason. Why?") + + eyeobj = new(get_turf(src), src) + return TRUE /obj/machinery/computer/camera_advanced/proc/GrantActions(mob/living/user) for(var/datum/action/to_grant as anything in actions) to_grant.Grant(user) /obj/machinery/proc/remove_eye_control(mob/living/user) - CRASH("[type] does not implement ai eye handling") + CRASH("[type] does not implement camera eye handling") /obj/machinery/computer/camera_advanced/proc/give_eye_control(mob/user) if(isnull(user?.client)) return - GrantActions(user) + current_user = user - eyeobj.eye_user = user - eyeobj.name = "Camera Eye ([user.name])" - user.remote_control = eyeobj - user.reset_perspective(eyeobj) - eyeobj.setLoc(eyeobj.loc) + eyeobj.assign_user(user) + GrantActions(user) + if(should_supress_view_changes) user.client.view_size.supress() begin_processing() @@ -110,14 +116,11 @@ for(var/datum/camerachunk/camerachunks_gone as anything in eyeobj.visibleCameraChunks) camerachunks_gone.remove(eyeobj) - user.reset_perspective(null) - if(eyeobj.visible_icon) - user.client.images -= eyeobj.user_image + eyeobj.assign_user(null) + current_user = null + user.client.view_size.unsupress() - eyeobj.eye_user = null - user.remote_control = null - current_user = null playsound(src, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE) /obj/machinery/computer/camera_advanced/on_set_is_operational(old_value) @@ -148,39 +151,43 @@ if(!QDELETED(current_user)) to_chat(user, span_warning("The console is already in use!")) return - var/mob/living/L = user - if(!eyeobj) - CreateEye() - if(!eyeobj) //Eye creation failed + + if(eyeobj) + give_eye_control(user) + eyeobj.setLoc(eyeobj.loc) + return + /* We're attempting to initialize the eye past this point */ + + if(!CreateEye()) + to_chat(user, span_warning("\The [src] flashes a bunch of never-ending errors on the display. Something is really wrong.")) return - if(!eyeobj.eye_initialized) - var/camera_location - var/turf/myturf = get_turf(src) - if(eyeobj.use_static != FALSE) - if((!length(z_lock) || (myturf.z in z_lock)) && GLOB.cameranet.checkTurfVis(myturf)) - camera_location = myturf - else - for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras) - if(!C.can_use() || length(z_lock) && !(C.z in z_lock)) - continue - var/list/network_overlap = networks & C.network - if(length(network_overlap)) - camera_location = get_turf(C) - break + + var/camera_location + var/turf/myturf = get_turf(src) + var/consider_zlock = (!!length(z_lock)) + + if(!eyeobj.use_visibility) + if(consider_zlock && !(myturf.z in z_lock)) + camera_location = locate(round(world.maxx * 0.5), round(world.maxy * 0.5), z_lock[1]) else camera_location = myturf - if(length(z_lock) && !(myturf.z in z_lock)) - camera_location = locate(round(world.maxx/2), round(world.maxy/2), z_lock[1]) - - if(camera_location) - eyeobj.eye_initialized = TRUE - give_eye_control(L) - eyeobj.setLoc(camera_location) + else + if((!consider_zlock || (myturf.z in z_lock)) && GLOB.cameranet.checkTurfVis(myturf)) + camera_location = myturf else - unset_machine() + for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras) + if(!C.can_use() || consider_zlock && !(C.z in z_lock)) + continue + var/list/network_overlap = networks & C.network + if(length(network_overlap)) + camera_location = get_turf(C) + break + + if(camera_location) + give_eye_control(user) + eyeobj.setLoc(camera_location, TRUE) else - give_eye_control(L) - eyeobj.setLoc(eyeobj.loc) + unset_machine() /obj/machinery/computer/camera_advanced/attack_robot(mob/user) return attack_hand(user) @@ -188,73 +195,6 @@ /obj/machinery/computer/camera_advanced/attack_ai(mob/user) return //AIs would need to disable their own camera procs to use the console safely. Bugs happen otherwise. -/mob/camera/ai_eye/remote - name = "Inactive Camera Eye" - ai_detector_visible = FALSE - var/sprint = 10 - var/cooldown = 0 - var/acceleration = 1 - var/mob/living/eye_user = null - var/obj/machinery/origin - var/eye_initialized = 0 - var/visible_icon = 0 - var/image/user_image = null - -/mob/camera/ai_eye/remote/update_remote_sight(mob/living/user) - user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras - user.set_sight(SEE_TURFS) - return TRUE - -/mob/camera/ai_eye/remote/Destroy() - if(origin && eye_user) - origin.remove_eye_control(eye_user,src) - origin = null - . = ..() - eye_user = null - -/mob/camera/ai_eye/remote/GetViewerClient() - if(eye_user) - return eye_user.client - return null - -/mob/camera/ai_eye/remote/setLoc(turf/destination, force_update = FALSE) - if(eye_user) - destination = get_turf(destination) - if (destination) - abstract_move(destination) - else - moveToNullspace() - - update_ai_detect_hud() - - if(use_static) - GLOB.cameranet.visibility(src, GetViewerClient(), null, use_static) - - if(visible_icon) - if(eye_user.client) - eye_user.client.images -= user_image - user_image = image(icon,loc,icon_state, FLY_LAYER) - SET_PLANE(user_image, ABOVE_GAME_PLANE, destination) - eye_user.client.images += user_image - -/mob/camera/ai_eye/remote/relaymove(mob/living/user, direction) - var/initial = initial(sprint) - var/max_sprint = 50 - - if(cooldown && cooldown < world.timeofday) // 3 seconds - sprint = initial - - for(var/i = 0; i < max(sprint, initial); i += 20) - var/turf/step = get_turf(get_step(src, direction)) - if(step) - setLoc(step) - - cooldown = world.timeofday + 5 - if(acceleration) - sprint = min(sprint + 0.5, max_sprint) - else - sprint = initial - /datum/action/innate/camera_off name = "End Camera View" button_icon = 'icons/mob/actions/actions_silicon.dmi' @@ -263,8 +203,8 @@ /datum/action/innate/camera_off/Activate() if(!owner || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control - var/obj/machinery/computer/camera_advanced/console = remote_eye.origin + var/mob/eye/camera/remote/remote_eye = owner.remote_control + var/obj/machinery/computer/camera_advanced/console = remote_eye.origin_ref.resolve() console.remove_eye_control(owner) /datum/action/innate/camera_jump @@ -275,8 +215,8 @@ /datum/action/innate/camera_jump/Activate() if(!owner || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control - var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin + var/mob/eye/camera/remote/remote_eye = owner.remote_control + var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin_ref.resolve() var/list/L = list() @@ -320,7 +260,7 @@ /datum/action/innate/camera_multiz_up/Activate() if(!owner || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control + var/mob/eye/camera/remote/remote_eye = owner.remote_control if(remote_eye.zMove(UP)) to_chat(owner, span_notice("You move upwards.")) else @@ -334,7 +274,7 @@ /datum/action/innate/camera_multiz_down/Activate() if(!owner || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control + var/mob/eye/camera/remote/remote_eye = owner.remote_control if(remote_eye.zMove(DOWN)) to_chat(owner, span_notice("You move downwards.")) else diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index a9b65fab382..e2db67f6ad8 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -142,7 +142,7 @@ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 50, FALSE) return TRUE -/obj/machinery/computer/communications/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) +/obj/machinery/computer/communications/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/ui_state) var/static/list/approved_states = list(STATE_BUYING_SHUTTLE, STATE_CHANGING_STATUS, STATE_MAIN, STATE_MESSAGES) . = ..() diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm index 83a2a08d986..3cba3ad2078 100644 --- a/code/game/machinery/computer/operating_computer.dm +++ b/code/game/machinery/computer/operating_computer.dm @@ -140,7 +140,7 @@ data["patient"]["oxyLoss"] = patient.getOxyLoss() if(patient.surgeries.len) for(var/datum/surgery/procedure in patient.surgeries) - var/datum/surgery_step/surgery_step = procedure.get_surgery_step() + var/datum/surgery_step/surgery_step = GLOB.surgery_steps[procedure.steps[procedure.status]] var/chems_needed = surgery_step.get_chem_list() var/alternative_step var/alt_chems_needed = "" diff --git a/code/game/machinery/computer/orders/order_computer/mining_order.dm b/code/game/machinery/computer/orders/order_computer/mining_order.dm index 94fda727d5f..98c1e8eed2f 100644 --- a/code/game/machinery/computer/orders/order_computer/mining_order.dm +++ b/code/game/machinery/computer/orders/order_computer/mining_order.dm @@ -34,7 +34,7 @@ /obj/machinery/computer/order_console/mining/order_groceries(mob/living/purchaser, obj/item/card/id/card, list/groceries) var/list/things_to_order = list() for(var/datum/orderable_item/item as anything in groceries) - things_to_order[item.item_path] = groceries[item] + things_to_order[item.purchase_path] = groceries[item] var/datum/supply_pack/custom/mining_pack = new( purchaser = purchaser, \ diff --git a/code/game/machinery/computer/orders/order_computer/order_computer.dm b/code/game/machinery/computer/orders/order_computer/order_computer.dm index 9098d5aeb09..b74601ee8a3 100644 --- a/code/game/machinery/computer/orders/order_computer/order_computer.dm +++ b/code/game/machinery/computer/orders/order_computer/order_computer.dm @@ -50,7 +50,7 @@ GLOBAL_LIST_EMPTY(order_console_products) if(GLOB.order_console_products.len) return for(var/datum/orderable_item/path as anything in subtypesof(/datum/orderable_item)) - if(!initial(path.item_path)) + if(!initial(path.purchase_path)) continue GLOB.order_console_products += new path @@ -120,7 +120,8 @@ GLOBAL_LIST_EMPTY(order_console_products) "cat" = item.category_index, "ref" = REF(item), "cost" = round(item.cost_per_order * cargo_cost_multiplier), - "product_icon" = icon2base64(getFlatIcon(image(icon = initial(item.item_path.icon), icon_state = initial(item.item_path.icon_state)), no_anim=TRUE)) + "icon" = item.purchase_path::icon, + "icon_state" = item.purchase_path::icon_state, )) return data @@ -198,7 +199,7 @@ GLOBAL_LIST_EMPTY(order_console_products) grocery_list.Remove(item) continue for(var/amt in 1 to grocery_list[item])//every order amount - ordered_paths += item.item_path + ordered_paths += item.purchase_path podspawn(list( "target" = get_turf(living_user), "style" = /datum/pod_style/advanced, diff --git a/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm b/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm index 9e5413f8548..e53d595c498 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm @@ -3,144 +3,154 @@ /datum/orderable_item/milk_eggs/milk name = "Milk" - item_path = /obj/item/reagent_containers/condiment/milk + purchase_path = /obj/item/reagent_containers/condiment/milk cost_per_order = 30 /datum/orderable_item/milk_eggs/soymilk name = "Soy Milk" - item_path = /obj/item/reagent_containers/condiment/soymilk + purchase_path = /obj/item/reagent_containers/condiment/soymilk cost_per_order = 30 /datum/orderable_item/milk_eggs/cream name = "Cream" - item_path = /obj/item/reagent_containers/cup/glass/bottle/juice/cream + purchase_path = /obj/item/reagent_containers/cup/glass/bottle/juice/cream cost_per_order = 40 /datum/orderable_item/milk_eggs/yoghurt name = "Yoghurt" - item_path = /obj/item/reagent_containers/condiment/yoghurt + purchase_path = /obj/item/reagent_containers/condiment/yoghurt cost_per_order = 40 /datum/orderable_item/milk_eggs/eggs name = "Egg Carton" - item_path = /obj/item/storage/fancy/egg_box + purchase_path = /obj/item/storage/fancy/egg_box cost_per_order = 40 /datum/orderable_item/milk_eggs/fillet name = "Fish Fillet" - item_path = /obj/item/food/fishmeat + purchase_path = /obj/item/food/fishmeat cost_per_order = 12 /datum/orderable_item/milk_eggs/octopus name = "Octopus Tentacle" - item_path = /obj/item/food/fishmeat/octopus + purchase_path = /obj/item/food/fishmeat/octopus cost_per_order = 12 /datum/orderable_item/milk_eggs/chicken name = "Chicken Breast" - item_path = /obj/item/food/meat/slab/chicken + purchase_path = /obj/item/food/meat/slab/chicken cost_per_order = 15 /datum/orderable_item/milk_eggs/spider_eggs name = "Spider Eggs" - item_path = /obj/item/food/spidereggs + purchase_path = /obj/item/food/spidereggs /datum/orderable_item/milk_eggs/moonfish_eggs name = "Moonfish Eggs" - item_path = /obj/item/food/moonfish_eggs + purchase_path = /obj/item/food/moonfish_eggs cost_per_order = 30 /datum/orderable_item/milk_eggs/desert_snails name = "Canned Desert Snails" - item_path = /obj/item/food/canned/desert_snails + purchase_path = /obj/item/food/canned/desert_snails cost_per_order = 20 /datum/orderable_item/milk_eggs/canned_jellyfish name = "Canned Gunner Jellyfish" - item_path = /obj/item/food/canned/jellyfish + purchase_path = /obj/item/food/canned/jellyfish cost_per_order = 20 /datum/orderable_item/milk_eggs/canned_larvae name = "Canned Larvae" - item_path = /obj/item/food/canned/larvae + purchase_path = /obj/item/food/canned/larvae cost_per_order = 20 /datum/orderable_item/milk_eggs/canned_tomatoes name = "Canned San Marzano Tomatoes" - item_path = /obj/item/food/canned/tomatoes + purchase_path = /obj/item/food/canned/tomatoes cost_per_order = 30 /datum/orderable_item/milk_eggs/canned_pine_nuts name = "Canned Pine Nuts" - item_path = /obj/item/food/canned/pine_nuts + purchase_path = /obj/item/food/canned/pine_nuts cost_per_order = 20 /datum/orderable_item/milk_eggs/canned_squid_ink name = "Canned Squid Ink" - item_path = /obj/item/food/canned/squid_ink + purchase_path = /obj/item/food/canned/squid_ink cost_per_order = 20 /datum/orderable_item/milk_eggs/chap name = "Can of CHAP" - item_path = /obj/item/food/canned/chap + purchase_path = /obj/item/food/canned/chap cost_per_order = 20 /datum/orderable_item/milk_eggs/ready_donk name = "Ready-Donk Meal: Bachelor Chow" - item_path = /obj/item/food/ready_donk + purchase_path = /obj/item/food/ready_donk cost_per_order = 40 /datum/orderable_item/milk_eggs/ready_donk_mac name = "Ready-Donk Meal: Donk-a-Roni" - item_path = /obj/item/food/ready_donk/mac_n_cheese + purchase_path = /obj/item/food/ready_donk/mac_n_cheese cost_per_order = 40 /datum/orderable_item/milk_eggs/ready_donk_mex name = "Ready-Donk Meal: Donkhiladas" - item_path = /obj/item/food/ready_donk/donkhiladas + purchase_path = /obj/item/food/ready_donk/donkhiladas cost_per_order = 40 /datum/orderable_item/milk_eggs/ready_donk_nachos name = "Ready-Donk Meal: Donk Sol Series Boritos Nachos Grandes" - item_path = /obj/item/food/ready_donk/nachos_grandes + purchase_path = /obj/item/food/ready_donk/nachos_grandes cost_per_order = 40 /datum/orderable_item/milk_eggs/ready_donk_orange name = "Ready-Donk Meal: Donk-range Chicken" - item_path = /obj/item/food/ready_donk/donkrange_chicken + purchase_path = /obj/item/food/ready_donk/donkrange_chicken + cost_per_order = 40 + +/datum/orderable_item/milk_eggs/ready_donk_salisbury + name = "Ready-Donk Meal: Donkriginals Salisbury Steak" + purchase_path = /obj/item/food/ready_donk/salisbury_steak + cost_per_order = 40 + +/datum/orderable_item/milk_eggs/ready_donk_chicken + name = "Ready-Donk Meal: Donkriginals Country-Fried Chicken" + purchase_path = /obj/item/food/ready_donk/country_chicken cost_per_order = 40 /datum/orderable_item/milk_eggs/tiziran_goods name = "Tiziran Farm-Fresh Pack" - item_path = /obj/item/storage/box/tiziran_goods + purchase_path = /obj/item/storage/box/tiziran_goods cost_per_order = 120 /datum/orderable_item/milk_eggs/tiziran_cans name = "Tiziran Canned Goods Pack" - item_path = /obj/item/storage/box/tiziran_cans + purchase_path = /obj/item/storage/box/tiziran_cans cost_per_order = 120 /datum/orderable_item/milk_eggs/tiziran_meats name = "Tiziran Meatmarket Pack" - item_path = /obj/item/storage/box/tiziran_meats + purchase_path = /obj/item/storage/box/tiziran_meats cost_per_order = 120 /datum/orderable_item/milk_eggs/mothic_goods name = "Mothic Farm-Fresh Pack" - item_path = /obj/item/storage/box/mothic_goods + purchase_path = /obj/item/storage/box/mothic_goods cost_per_order = 120 /datum/orderable_item/milk_eggs/mothic_cans_sauces name = "Mothic Pantry Pack" - item_path = /obj/item/storage/box/mothic_cans_sauces + purchase_path = /obj/item/storage/box/mothic_cans_sauces cost_per_order = 120 /datum/orderable_item/milk_eggs/armorfish name = "Cleaned Armorfish" - item_path = /obj/item/food/fishmeat/armorfish + purchase_path = /obj/item/food/fishmeat/armorfish cost_per_order = 30 /datum/orderable_item/milk_eggs/moonfish name = "Moonfish" - item_path = /obj/item/food/fishmeat/moonfish + purchase_path = /obj/item/food/fishmeat/moonfish cost_per_order = 30 diff --git a/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm b/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm index 39fb38df550..06bd251368a 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm @@ -3,105 +3,105 @@ /datum/orderable_item/reagents/flour name = "Flour Sack" - item_path = /obj/item/reagent_containers/condiment/flour + purchase_path = /obj/item/reagent_containers/condiment/flour cost_per_order = 30 /datum/orderable_item/reagents/sugar name = "Sugar Sack" - item_path = /obj/item/reagent_containers/condiment/sugar + purchase_path = /obj/item/reagent_containers/condiment/sugar cost_per_order = 30 /datum/orderable_item/reagents/rice name = "Rice Sack" - item_path = /obj/item/reagent_containers/condiment/rice + purchase_path = /obj/item/reagent_containers/condiment/rice cost_per_order = 30 /datum/orderable_item/reagents/cornmeal name = "Cornmeal Box" - item_path = /obj/item/reagent_containers/condiment/cornmeal + purchase_path = /obj/item/reagent_containers/condiment/cornmeal cost_per_order = 30 /datum/orderable_item/reagents/enzyme name = "Universal Enzyme" - item_path = /obj/item/reagent_containers/condiment/enzyme + purchase_path = /obj/item/reagent_containers/condiment/enzyme cost_per_order = 40 /datum/orderable_item/reagents/salt name = "Salt Shaker" - item_path = /obj/item/reagent_containers/condiment/saltshaker + purchase_path = /obj/item/reagent_containers/condiment/saltshaker cost_per_order = 15 /datum/orderable_item/reagents/pepper name = "Pepper Mill" - item_path = /obj/item/reagent_containers/condiment/peppermill + purchase_path = /obj/item/reagent_containers/condiment/peppermill cost_per_order = 15 /datum/orderable_item/reagents/soysauce name = "Soy Sauce" - item_path = /obj/item/reagent_containers/condiment/soysauce + purchase_path = /obj/item/reagent_containers/condiment/soysauce cost_per_order = 15 /datum/orderable_item/reagents/bbqsauce name = "BBQ Sauce" - item_path = /obj/item/reagent_containers/condiment/bbqsauce + purchase_path = /obj/item/reagent_containers/condiment/bbqsauce cost_per_order = 60 /datum/orderable_item/reagents/vinegar name = "Vinegar" - item_path = /obj/item/reagent_containers/condiment/vinegar + purchase_path = /obj/item/reagent_containers/condiment/vinegar cost_per_order = 30 /datum/orderable_item/reagents/olive_oil name = "Olive Oil" - item_path = /obj/item/reagent_containers/condiment/olive_oil + purchase_path = /obj/item/reagent_containers/condiment/olive_oil cost_per_order = 50 //Extra Virgin, just like you, the reader /datum/orderable_item/reagents/peanut_butter name = "Peanut Butter" - item_path = /obj/item/reagent_containers/condiment/peanut_butter + purchase_path = /obj/item/reagent_containers/condiment/peanut_butter cost_per_order = 30 /datum/orderable_item/reagents/cherryjelly name = "Cherry Jelly" - item_path = /obj/item/reagent_containers/condiment/cherryjelly + purchase_path = /obj/item/reagent_containers/condiment/cherryjelly cost_per_order = 30 /datum/orderable_item/reagents/worcestershire name = "Worcestershire Sauce" - item_path = /obj/item/reagent_containers/condiment/worcestershire + purchase_path = /obj/item/reagent_containers/condiment/worcestershire cost_per_order = 30 /datum/orderable_item/reagents/red_bay name = "Red Bay Seasoning" - item_path = /obj/item/reagent_containers/condiment/red_bay + purchase_path = /obj/item/reagent_containers/condiment/red_bay cost_per_order = 30 /datum/orderable_item/reagents/curry_powder name = "Curry Powder" - item_path = /obj/item/reagent_containers/condiment/curry_powder + purchase_path = /obj/item/reagent_containers/condiment/curry_powder cost_per_order = 30 /datum/orderable_item/reagents/dashi_concentrate name = "Dashi Concentrate" - item_path = /obj/item/reagent_containers/condiment/dashi_concentrate + purchase_path = /obj/item/reagent_containers/condiment/dashi_concentrate cost_per_order = 30 /datum/orderable_item/reagents/coconut_milk name = "Coconut Milk" - item_path = /obj/item/reagent_containers/condiment/coconut_milk + purchase_path = /obj/item/reagent_containers/condiment/coconut_milk cost_per_order = 30 /datum/orderable_item/reagents/grounding_solution name = "Grounding Solution" - item_path = /obj/item/reagent_containers/condiment/grounding_solution + purchase_path = /obj/item/reagent_containers/condiment/grounding_solution cost_per_order = 30 /datum/orderable_item/reagents/honey name = "Honey" - item_path = /obj/item/reagent_containers/condiment/honey + purchase_path = /obj/item/reagent_containers/condiment/honey cost_per_order = 125 //its high quality honey :) /datum/orderable_item/reagents/mayonnaise name = "Mayonnaise" - item_path = /obj/item/reagent_containers/condiment/mayonnaise + purchase_path = /obj/item/reagent_containers/condiment/mayonnaise cost_per_order = 30 diff --git a/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm b/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm index f96562724d2..5eff7055b9f 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm @@ -3,177 +3,177 @@ /datum/orderable_item/veggies/potato name = "Potato" - item_path = /obj/item/food/grown/potato + purchase_path = /obj/item/food/grown/potato /datum/orderable_item/veggies/tomato name = "Tomato" - item_path = /obj/item/food/grown/tomato + purchase_path = /obj/item/food/grown/tomato /datum/orderable_item/veggies/carrot name = "Carrot" - item_path = /obj/item/food/grown/carrot + purchase_path = /obj/item/food/grown/carrot /datum/orderable_item/veggies/eggplant name = "Eggplant" - item_path = /obj/item/food/grown/eggplant + purchase_path = /obj/item/food/grown/eggplant /datum/orderable_item/veggies/mushroom name = "Plump Helmet" desc = "Plumus Hellmus: Plump, soft and s-so inviting~" - item_path = /obj/item/food/grown/mushroom/plumphelmet + purchase_path = /obj/item/food/grown/mushroom/plumphelmet /datum/orderable_item/veggies/cabbage name = "Cabbage" - item_path = /obj/item/food/grown/cabbage + purchase_path = /obj/item/food/grown/cabbage /datum/orderable_item/veggies/onion name = "Onion" - item_path = /obj/item/food/grown/onion + purchase_path = /obj/item/food/grown/onion /datum/orderable_item/veggies/apple name = "Apple" - item_path = /obj/item/food/grown/apple + purchase_path = /obj/item/food/grown/apple /datum/orderable_item/veggies/pumpkin name = "Pumpkin" - item_path = /obj/item/food/grown/pumpkin + purchase_path = /obj/item/food/grown/pumpkin /datum/orderable_item/veggies/watermelon name = "Watermelon" - item_path = /obj/item/food/grown/watermelon + purchase_path = /obj/item/food/grown/watermelon /datum/orderable_item/veggies/corn name = "Corn" - item_path = /obj/item/food/grown/corn + purchase_path = /obj/item/food/grown/corn /datum/orderable_item/veggies/soybean name = "Soybeans" - item_path = /obj/item/food/grown/soybeans + purchase_path = /obj/item/food/grown/soybeans /datum/orderable_item/veggies/garlic name = "Garlic" - item_path = /obj/item/food/grown/garlic + purchase_path = /obj/item/food/grown/garlic /datum/orderable_item/veggies/cherries name = "Cherries" - item_path = /obj/item/food/grown/cherries + purchase_path = /obj/item/food/grown/cherries /datum/orderable_item/veggies/chanterelle name = "Chanterelle" - item_path = /obj/item/food/grown/mushroom/chanterelle + purchase_path = /obj/item/food/grown/mushroom/chanterelle /datum/orderable_item/veggies/cocoa name = "Cocoa" - item_path = /obj/item/food/grown/cocoapod + purchase_path = /obj/item/food/grown/cocoapod /datum/orderable_item/veggies/herbs name = "Bundle of Herbs" - item_path = /obj/item/food/grown/herbs + purchase_path = /obj/item/food/grown/herbs cost_per_order = 5 /datum/orderable_item/veggies/bell_pepper name = "Bell Pepper" - item_path = /obj/item/food/grown/bell_pepper + purchase_path = /obj/item/food/grown/bell_pepper /datum/orderable_item/veggies/cucumbers name = "Cucumber" - item_path = /obj/item/food/grown/cucumber + purchase_path = /obj/item/food/grown/cucumber cost_per_order = 10 /datum/orderable_item/veggies/pickles name = "Jar of pickles" - item_path = /obj/item/storage/fancy/pickles_jar + purchase_path = /obj/item/storage/fancy/pickles_jar cost_per_order = 60 /datum/orderable_item/veggies/pickled_voltvine name = "Pickled Voltvine" - item_path = /obj/item/food/pickled_voltvine + purchase_path = /obj/item/food/pickled_voltvine cost_per_order = 5 /datum/orderable_item/veggies/chili name = "Chili" - item_path = /obj/item/food/grown/chili + purchase_path = /obj/item/food/grown/chili /datum/orderable_item/veggies/berries name = "Berries" - item_path = /obj/item/food/grown/berries + purchase_path = /obj/item/food/grown/berries /datum/orderable_item/veggies/pineapple name = "Pineapple" - item_path = /obj/item/food/grown/pineapple + purchase_path = /obj/item/food/grown/pineapple /datum/orderable_item/veggies/peas name = "Peas" - item_path = /obj/item/food/grown/peas + purchase_path = /obj/item/food/grown/peas /datum/orderable_item/veggies/korta_nut //nanotrasen does not devote as much of their resources to pathetic lizard crops name = "Korta Nut" - item_path = /obj/item/food/grown/korta_nut + purchase_path = /obj/item/food/grown/korta_nut cost_per_order = 15 /datum/orderable_item/veggies/parsnip name = "Parsnip" - item_path = /obj/item/food/grown/parsnip + purchase_path = /obj/item/food/grown/parsnip /datum/orderable_item/veggies/redbeet name = "Red Beet" - item_path = /obj/item/food/grown/redbeet + purchase_path = /obj/item/food/grown/redbeet /datum/orderable_item/veggies/orange name = "Orange" - item_path = /obj/item/food/grown/citrus/orange + purchase_path = /obj/item/food/grown/citrus/orange /datum/orderable_item/veggies/vanillapod name = "Vanilla" - item_path = /obj/item/food/grown/vanillapod + purchase_path = /obj/item/food/grown/vanillapod cost_per_order = 25 //food items that are treated as mutations in game should be more expensive. groceries shouldnt include ACTUAL mutations but i think real foods are ok /datum/orderable_item/veggies/sweetkorta name = "Sweet Korta Nut" - item_path = /obj/item/food/grown/korta_nut/sweet + purchase_path = /obj/item/food/grown/korta_nut/sweet cost_per_order = 30 /datum/orderable_item/veggies/redonion name = "Red Onion" - item_path = /obj/item/food/grown/onion/red + purchase_path = /obj/item/food/grown/onion/red cost_per_order = 25 /datum/orderable_item/veggies/peanut name = "Peanut" - item_path = /obj/item/food/grown/peanut + purchase_path = /obj/item/food/grown/peanut /datum/orderable_item/veggies/sweetpotato name = "Sweet Potato" - item_path = /obj/item/food/grown/potato/sweet + purchase_path = /obj/item/food/grown/potato/sweet cost_per_order = 25 /datum/orderable_item/veggies/oat name = "Oat" - item_path = /obj/item/food/grown/oat + purchase_path = /obj/item/food/grown/oat /datum/orderable_item/veggies/trumpet name = "Spaceman's Trumpet" - item_path = /obj/item/food/grown/trumpet + purchase_path = /obj/item/food/grown/trumpet cost_per_order = 25 /datum/orderable_item/veggies/banana name = "Banana" - item_path = /obj/item/food/grown/banana + purchase_path = /obj/item/food/grown/banana /datum/orderable_item/veggies/ghostchili name = "Ghost Chili" - item_path = /obj/item/food/grown/ghost_chili + purchase_path = /obj/item/food/grown/ghost_chili cost_per_order = 25 /datum/orderable_item/veggies/lemon name = "Lemon" - item_path = /obj/item/food/grown/citrus/lemon + purchase_path = /obj/item/food/grown/citrus/lemon /datum/orderable_item/veggies/lime name = "Lime" - item_path = /obj/item/food/grown/citrus/lime + purchase_path = /obj/item/food/grown/citrus/lime /datum/orderable_item/veggies/toechtauese name = "Töchtaüse berries" - item_path = /obj/item/food/grown/toechtauese + purchase_path = /obj/item/food/grown/toechtauese cost_per_order = 15 diff --git a/code/game/machinery/computer/orders/order_items/mining/order_consumables.dm b/code/game/machinery/computer/orders/order_items/mining/order_consumables.dm index a91a34b46f2..79e82ac954b 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_consumables.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_consumables.dm @@ -2,53 +2,53 @@ category_index = CATEGORY_CONSUMABLES /datum/orderable_item/consumables/survival_pen - item_path = /obj/item/reagent_containers/hypospray/medipen/survival + purchase_path = /obj/item/reagent_containers/hypospray/medipen/survival cost_per_order = 250 /datum/orderable_item/consumables/luxury_pen - item_path = /obj/item/reagent_containers/hypospray/medipen/survival/luxury + purchase_path = /obj/item/reagent_containers/hypospray/medipen/survival/luxury cost_per_order = 750 /datum/orderable_item/consumables/medkit - item_path = /obj/item/storage/medkit/brute + purchase_path = /obj/item/storage/medkit/brute cost_per_order = 400 /datum/orderable_item/consumables/medkit_fire - item_path = /obj/item/storage/medkit/fire + purchase_path = /obj/item/storage/medkit/fire desc = "For emergency magmatic burn relief." cost_per_order = 500 /datum/orderable_item/consumables/whiskey - item_path = /obj/item/reagent_containers/cup/glass/bottle/whiskey + purchase_path = /obj/item/reagent_containers/cup/glass/bottle/whiskey cost_per_order = 100 /datum/orderable_item/consumables/absinthe - item_path = /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium + purchase_path = /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium cost_per_order = 100 /datum/orderable_item/consumables/bubblegum - item_path = /obj/item/storage/box/gum/bubblegum + purchase_path = /obj/item/storage/box/gum/bubblegum cost_per_order = 100 /datum/orderable_item/consumables/havana_cigars - item_path = /obj/item/cigarette/cigar/havana + purchase_path = /obj/item/cigarette/cigar/havana cost_per_order = 150 /datum/orderable_item/consumables/havana_cigars - item_path = /obj/item/cigarette/cigar/havana + purchase_path = /obj/item/cigarette/cigar/havana cost_per_order = 150 /datum/orderable_item/consumables/tracking_implants - item_path = /obj/item/storage/box/minertracker + purchase_path = /obj/item/storage/box/minertracker cost_per_order = 600 /datum/orderable_item/consumables/space_cash - item_path = /obj/item/stack/spacecash/c1000 + purchase_path = /obj/item/stack/spacecash/c1000 desc = "A stack of space cash worth 1000 credits." cost_per_order = 2000 /datum/orderable_item/consumables/rescue_hook name = "Rescue Fishing Rod" - item_path = /obj/item/fishing_rod/rescue + purchase_path = /obj/item/fishing_rod/rescue desc = "For when your fellow miner has inevitably fallen into a chasm, and it's up to you to save them." cost_per_order = 600 diff --git a/code/game/machinery/computer/orders/order_items/mining/order_golem.dm b/code/game/machinery/computer/orders/order_items/mining/order_golem.dm index cbd037230f2..237360978e8 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_golem.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_golem.dm @@ -2,34 +2,34 @@ category_index = CATEGORY_GOLEM /datum/orderable_item/golem/mining_id - item_path = /obj/item/card/id/advanced/mining + purchase_path = /obj/item/card/id/advanced/mining cost_per_order = 200 /datum/orderable_item/golem/science_goggles - item_path = /obj/item/clothing/glasses/science + purchase_path = /obj/item/clothing/glasses/science cost_per_order = 200 /datum/orderable_item/golem/monkeycubes - item_path = /obj/item/food/monkeycube + purchase_path = /obj/item/food/monkeycube cost_per_order = 250 /datum/orderable_item/golem/belt - item_path = /obj/item/storage/belt/utility + purchase_path = /obj/item/storage/belt/utility cost_per_order = 300 /datum/orderable_item/golem/royal_cape - item_path = /obj/item/bedsheet/rd/royal_cape + purchase_path = /obj/item/bedsheet/rd/royal_cape cost_per_order = 400 /datum/orderable_item/golem/grey_extract - item_path = /obj/item/slime_extract/grey + purchase_path = /obj/item/slime_extract/grey cost_per_order = 800 /datum/orderable_item/golem/trigger_guard - item_path = /obj/item/borg/upgrade/modkit/trigger_guard + purchase_path = /obj/item/borg/upgrade/modkit/trigger_guard cost_per_order = 750 /datum/orderable_item/golem/rnd_boards - item_path = /obj/item/storage/box/rndboards + purchase_path = /obj/item/storage/box/rndboards cost_per_order = 1000 diff --git a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm index 76af0dc806c..babbd4a4738 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm @@ -2,132 +2,132 @@ category_index = CATEGORY_MINING /datum/orderable_item/mining/marker_beacon - item_path = /obj/item/stack/marker_beacon/ten + purchase_path = /obj/item/stack/marker_beacon/ten cost_per_order = 80 /datum/orderable_item/mining/skeleton_key - item_path = /obj/item/skeleton_key + purchase_path = /obj/item/skeleton_key cost_per_order = 675 /datum/orderable_item/mining/mining_stabilizer - item_path = /obj/item/mining_stabilizer + purchase_path = /obj/item/mining_stabilizer cost_per_order = 320 /datum/orderable_item/mining/fulton_core - item_path = /obj/item/fulton_core + purchase_path = /obj/item/fulton_core cost_per_order = 320 /datum/orderable_item/mining/mining_modsuit - item_path = /obj/item/mod/control/pre_equipped/mining + purchase_path = /obj/item/mod/control/pre_equipped/mining desc = "A mining-themed MODsuit that works best when in a mining environment." cost_per_order = 2500 /datum/orderable_item/mining/mining_belt - item_path = /obj/item/storage/belt/mining + purchase_path = /obj/item/storage/belt/mining cost_per_order = 450 /datum/orderable_item/mining/jaunter - item_path = /obj/item/wormhole_jaunter + purchase_path = /obj/item/wormhole_jaunter cost_per_order = 650 /datum/orderable_item/mining/crusher - item_path = /obj/item/kinetic_crusher + purchase_path = /obj/item/kinetic_crusher cost_per_order = 650 /datum/orderable_item/mining/crusher_retool_kit - item_path = /obj/item/crusher_trophy/retool_kit + purchase_path = /obj/item/crusher_trophy/retool_kit cost_per_order = 150 /datum/orderable_item/mining/crusher_retool_kit_harpoon - item_path = /obj/item/crusher_trophy/retool_kit/harpoon + purchase_path = /obj/item/crusher_trophy/retool_kit/harpoon cost_per_order = 150 /datum/orderable_item/mining/crusher_retool_kit_dagger - item_path = /obj/item/crusher_trophy/retool_kit/dagger + purchase_path = /obj/item/crusher_trophy/retool_kit/dagger cost_per_order = 150 /datum/orderable_item/mining/resonator - item_path = /obj/item/resonator + purchase_path = /obj/item/resonator cost_per_order = 710 /datum/orderable_item/mining/resonator_advanced - item_path = /obj/item/resonator/upgraded + purchase_path = /obj/item/resonator/upgraded cost_per_order = 2000 /datum/orderable_item/mining/mining_scanner - item_path = /obj/item/t_scanner/adv_mining_scanner + purchase_path = /obj/item/t_scanner/adv_mining_scanner cost_per_order = 675 /datum/orderable_item/mining/fulton - item_path = /obj/item/extraction_pack + purchase_path = /obj/item/extraction_pack cost_per_order = 800 /datum/orderable_item/mining/lazarus - item_path = /obj/item/lazarus_injector + purchase_path = /obj/item/lazarus_injector cost_per_order = 1000 /datum/orderable_item/mining/gar_mesons - item_path = /obj/item/clothing/glasses/meson/gar + purchase_path = /obj/item/clothing/glasses/meson/gar cost_per_order = 500 /datum/orderable_item/mining/silver_pickaxe - item_path = /obj/item/pickaxe/silver + purchase_path = /obj/item/pickaxe/silver cost_per_order = 1000 /datum/orderable_item/mining/diamond_pickaxe - item_path = /obj/item/pickaxe/diamond + purchase_path = /obj/item/pickaxe/diamond cost_per_order = 2000 /datum/orderable_item/mining/conscription_kit - item_path = /obj/item/storage/backpack/duffelbag/mining_conscript + purchase_path = /obj/item/storage/backpack/duffelbag/mining_conscript desc = "A kit containing everything a crewmember needs to support a shaft miner in the field." cost_per_order = 1200 /datum/orderable_item/mining/capsule - item_path = /obj/item/survivalcapsule + purchase_path = /obj/item/survivalcapsule cost_per_order = 350 /datum/orderable_item/mining/capsule/bathroom - item_path = /obj/item/survivalcapsule/bathroom + purchase_path = /obj/item/survivalcapsule/bathroom cost_per_order = 300 /datum/orderable_item/mining/capsule_luxury - item_path = /obj/item/survivalcapsule/luxury + purchase_path = /obj/item/survivalcapsule/luxury cost_per_order = 2300 /datum/orderable_item/mining/capsule_luxuryelite - item_path = /obj/item/survivalcapsule/luxuryelite + purchase_path = /obj/item/survivalcapsule/luxuryelite cost_per_order = 7000 /datum/orderable_item/mining/kheiralcuffs - item_path = /obj/item/kheiral_cuffs + purchase_path = /obj/item/kheiral_cuffs cost_per_order = 675 /datum/orderable_item/mining/bhop - item_path = /obj/item/clothing/shoes/bhop + purchase_path = /obj/item/clothing/shoes/bhop cost_per_order = 2000 /datum/orderable_item/mining/hiking_boots - item_path = /obj/item/clothing/shoes/winterboots/ice_boots + purchase_path = /obj/item/clothing/shoes/winterboots/ice_boots cost_per_order = 2000 /datum/orderable_item/mining/style_meter - item_path = /obj/item/style_meter + purchase_path = /obj/item/style_meter cost_per_order = 1200 /datum/orderable_item/mining/weather_radio - item_path = /obj/item/radio/weather_monitor + purchase_path = /obj/item/radio/weather_monitor cost_per_order = 320 /datum/orderable_item/mining/ventpointer - item_path = /obj/item/pinpointer/vent + purchase_path = /obj/item/pinpointer/vent cost_per_order = 1150 /datum/orderable_item/mining/boulder_processing - item_path = /obj/item/boulder_beacon + purchase_path = /obj/item/boulder_beacon desc = "A Bouldertech brand all-in-one boulder processing beacon. Each use will teleport in a component of a full boulder processing assembly line. Good for when you need to process additional boulders." cost_per_order = 875 /datum/orderable_item/mining/grapple_gun - item_path = /obj/item/grapple_gun + purchase_path = /obj/item/grapple_gun cost_per_order = 3000 diff --git a/code/game/machinery/computer/orders/order_items/mining/order_pka.dm b/code/game/machinery/computer/orders/order_items/mining/order_pka.dm index f239e9f2a7e..ceaa83d8ef7 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_pka.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_pka.dm @@ -2,45 +2,45 @@ category_index = CATEGORY_PKA /datum/orderable_item/accelerator/gun - item_path = /obj/item/gun/energy/recharge/kinetic_accelerator + purchase_path = /obj/item/gun/energy/recharge/kinetic_accelerator cost_per_order = 600 /datum/orderable_item/accelerator/range - item_path = /obj/item/borg/upgrade/modkit/range + purchase_path = /obj/item/borg/upgrade/modkit/range cost_per_order = 675 /datum/orderable_item/accelerator/damage - item_path = /obj/item/borg/upgrade/modkit/damage + purchase_path = /obj/item/borg/upgrade/modkit/damage cost_per_order = 675 /datum/orderable_item/accelerator/cooldown - item_path = /obj/item/borg/upgrade/modkit/cooldown + purchase_path = /obj/item/borg/upgrade/modkit/cooldown cost_per_order = 675 /datum/orderable_item/accelerator/chasis - item_path = /obj/item/borg/upgrade/modkit/chassis_mod + purchase_path = /obj/item/borg/upgrade/modkit/chassis_mod cost_per_order = 250 /datum/orderable_item/accelerator/chasis_orange - item_path = /obj/item/borg/upgrade/modkit/chassis_mod/orange + purchase_path = /obj/item/borg/upgrade/modkit/chassis_mod/orange cost_per_order = 300 /datum/orderable_item/accelerator/tracer - item_path = /obj/item/borg/upgrade/modkit/tracer + purchase_path = /obj/item/borg/upgrade/modkit/tracer cost_per_order = 100 /datum/orderable_item/accelerator/adjustable_tracer - item_path = /obj/item/borg/upgrade/modkit/tracer/adjustable + purchase_path = /obj/item/borg/upgrade/modkit/tracer/adjustable cost_per_order = 150 /datum/orderable_item/accelerator/aoe_mobs - item_path = /obj/item/borg/upgrade/modkit/aoe/mobs + purchase_path = /obj/item/borg/upgrade/modkit/aoe/mobs cost_per_order = 1500 /datum/orderable_item/accelerator/minebot_passthrough - item_path = /obj/item/borg/upgrade/modkit/minebot_passthrough + purchase_path = /obj/item/borg/upgrade/modkit/minebot_passthrough cost_per_order = 800 /datum/orderable_item/accelerator/friendly_fire - item_path = /obj/item/borg/upgrade/modkit/human_passthrough + purchase_path = /obj/item/borg/upgrade/modkit/human_passthrough cost_per_order = 750 diff --git a/code/game/machinery/computer/orders/order_items/mining/order_toys.dm b/code/game/machinery/computer/orders/order_items/mining/order_toys.dm index fab03cabaa4..65bbee24ead 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_toys.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_toys.dm @@ -2,38 +2,38 @@ category_index = CATEGORY_TOYS_DRONE /datum/orderable_item/toys_drones/soap - item_path = /obj/item/soap/nanotrasen + purchase_path = /obj/item/soap/nanotrasen cost_per_order = 180 /datum/orderable_item/toys_drones/laser_pointer - item_path = /obj/item/laser_pointer + purchase_path = /obj/item/laser_pointer cost_per_order = 275 /datum/orderable_item/toys_drones/facehugger - item_path = /obj/item/clothing/mask/facehugger/toy + purchase_path = /obj/item/clothing/mask/facehugger/toy cost_per_order = 275 /datum/orderable_item/toys_drones/mining_drone - item_path = /mob/living/basic/mining_drone + purchase_path = /mob/living/basic/mining_drone cost_per_order = 675 /datum/orderable_item/toys_drones/drone_health - item_path = /obj/item/mine_bot_upgrade/health + purchase_path = /obj/item/mine_bot_upgrade/health cost_per_order = 350 /datum/orderable_item/toys_drones/drone_shield - item_path = /obj/item/mine_bot_upgrade/regnerative_shield + purchase_path = /obj/item/mine_bot_upgrade/regnerative_shield cost_per_order = 500 /datum/orderable_item/toys_drones/drone_remote - item_path = /obj/item/minebot_remote_control + purchase_path = /obj/item/minebot_remote_control cost_per_order = 500 /datum/orderable_item/toys_drones/drone_pka - item_path = /obj/item/borg/upgrade/modkit/cooldown/minebot + purchase_path = /obj/item/borg/upgrade/modkit/cooldown/minebot cost_per_order = 525 /datum/orderable_item/toys_drones/drone_sentience - item_path = /obj/item/slimepotion/slime/sentience/mining + purchase_path = /obj/item/slimepotion/slime/sentience/mining cost_per_order = 850 diff --git a/code/game/machinery/computer/orders/order_items/order_datum.dm b/code/game/machinery/computer/orders/order_items/order_datum.dm index 13684fc742b..28c21a14f26 100644 --- a/code/game/machinery/computer/orders/order_items/order_datum.dm +++ b/code/game/machinery/computer/orders/order_items/order_datum.dm @@ -5,7 +5,7 @@ ///Description shown in the shop, set automatically unless it's hard set by the subtype var/desc ///Path of the item that is purchased when ordering us. - var/obj/item/item_path + var/atom/movable/purchase_path ///The category this item will be displayed in. var/category_index = NONE ///How much this item costs to order. @@ -15,14 +15,9 @@ . = ..() if(!category_index) CRASH("[type] doesn't have a category_index assigned!") - if(!item_path) + if(!purchase_path) CRASH("[type] orderable item datum with no item path was created!") if(!name) - name = initial(item_path.name) + name = initial(purchase_path.name) if(!desc) - desc = initial(item_path.desc) - -/datum/orderable_item/Destroy(force) - if(item_path) - qdel(item_path) - return ..() + desc = initial(purchase_path.desc) diff --git a/code/game/machinery/computer/records/security.dm b/code/game/machinery/computer/records/security.dm index 8b32bf9af0a..89f49cf754b 100644 --- a/code/game/machinery/computer/records/security.dm +++ b/code/game/machinery/computer/records/security.dm @@ -179,7 +179,7 @@ if("set_note") var/note = strip_html_full(params["note"], MAX_MESSAGE_LEN) - investigate_log("[user] has changed the security note of record: \"[target]\" from \"[target.security_note]\" to \"[note]\".") + investigate_log("[user] has changed the security note of record: \"[target]\" from \"[target.security_note]\" to \"[note]\".", INVESTIGATE_RECORDS) target.security_note = note return TRUE diff --git a/code/game/machinery/dna_infuser/dna_infusion.dm b/code/game/machinery/dna_infuser/dna_infusion.dm index 68b3bfe3515..2069a3c8a24 100644 --- a/code/game/machinery/dna_infuser/dna_infusion.dm +++ b/code/game/machinery/dna_infuser/dna_infusion.dm @@ -40,8 +40,8 @@ /// Requires the target mob to have an existing organic organ to "mutate". // TODO: In the future, this should have more logic: // - Replace non-mutant organs before mutant ones. -/mob/living/carbon/human/proc/infuse_organ(datum/infuser_entry/entry) - var/obj/item/organ/new_organ = pick_infusion_organ(entry) +/mob/living/carbon/human/proc/infuse_organ(datum/infuser_entry/entry, atom/movable/infused_from) + var/obj/item/organ/new_organ = pick_infusion_organ(entry, infused_from) if(!new_organ) return FALSE // Valid organ successfully picked. diff --git a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm index aa479f1d316..67fe3a41f71 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm @@ -190,6 +190,7 @@ . = ..() owner.AddElementTrait(TRAIT_WADDLING, type, /datum/element/waddling) RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_location)) + RegisterSignal(owner, COMSIG_LIVING_GIBBER_ACT, PROC_REF(on_gibber_processed)) check_location(owner, null) /obj/item/organ/tail/fish/on_mob_remove(mob/living/carbon/owner) @@ -197,7 +198,12 @@ owner.remove_traits(list(TRAIT_WADDLING, TRAIT_NO_STAGGER), type) owner.remove_movespeed_modifier(/datum/movespeed_modifier/fish_on_water) owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/fish_on_water) - UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_GIBBER_ACT)) + +/obj/item/organ/tail/fish/proc/on_gibber_processed(mob/living/carbon/owner, mob/living/user, obj/machinery/gibber, list/results) + SIGNAL_HANDLER + for(var/iteration in 1 to fillet_amount * 0.5) + results += new fillet_type /obj/item/organ/tail/fish/get_greyscale_color_from_draw_color() set_greyscale(bodypart_overlay.draw_color) diff --git a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm index 82c9c9fc8e8..805277b3d9c 100644 --- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm @@ -34,7 +34,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... AddElement(/datum/element/noticable_organ, "%PRONOUN_They radiate%PRONOUN_s an aura of serenity.") AddElement(/datum/element/update_icon_blocker) -/obj/item/organ/heart/gondola/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/heart/gondola/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() if(!(FACTION_HOSTILE in receiver.faction)) factions_to_remove += FACTION_HOSTILE @@ -42,7 +42,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... factions_to_remove += FACTION_MINING receiver.faction |= list(FACTION_HOSTILE, FACTION_MINING) -/obj/item/organ/heart/gondola/mob_remove(mob/living/carbon/heartless, special, movement_flags) +/obj/item/organ/heart/gondola/on_mob_remove(mob/living/carbon/heartless, special, movement_flags) . = ..() for(var/faction in factions_to_remove) heartless.faction -= faction @@ -64,11 +64,11 @@ Fluoride Stare: After someone says 5 words, blah blah blah... AddElement(/datum/element/noticable_organ, "%PRONOUN_Their mouth is permanently affixed into a relaxed smile.", BODY_ZONE_PRECISE_MOUTH) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola) -/obj/item/organ/tongue/gondola/mob_insert(mob/living/carbon/tongue_owner, special, movement_flags) +/obj/item/organ/tongue/gondola/on_mob_insert(mob/living/carbon/tongue_owner, special, movement_flags) . = ..() tongue_owner.add_mood_event("gondola_zen", /datum/mood_event/gondola_serenity) -/obj/item/organ/tongue/gondola/mob_remove(mob/living/carbon/tongue_owner, special, movement_flags) +/obj/item/organ/tongue/gondola/on_mob_remove(mob/living/carbon/tongue_owner, special, movement_flags) tongue_owner.clear_mood_event("gondola_zen") return ..() @@ -87,7 +87,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... AddElement(/datum/element/noticable_organ, "%PRONOUN_Their left arm has small needles breaching the skin all over it.", BODY_ZONE_L_ARM) AddElement(/datum/element/noticable_organ, "%PRONOUN_Their right arm has small needles breaching the skin all over it.", BODY_ZONE_R_ARM) -/obj/item/organ/liver/gondola/mob_insert(mob/living/carbon/liver_owner, special, movement_flags) +/obj/item/organ/liver/gondola/on_mob_insert(mob/living/carbon/liver_owner, special, movement_flags) . = ..() var/has_left = liver_owner.has_left_hand(check_disabled = FALSE) var/has_right = liver_owner.has_right_hand(check_disabled = FALSE) @@ -102,7 +102,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... RegisterSignal(liver_owner, COMSIG_LIVING_TRY_PULL, PROC_REF(on_owner_try_pull)) RegisterSignal(liver_owner, COMSIG_CARBON_HELPED, PROC_REF(on_hug)) -/obj/item/organ/liver/gondola/mob_remove(mob/living/carbon/liver_owner, special, movement_flags) +/obj/item/organ/liver/gondola/on_mob_remove(mob/living/carbon/liver_owner, special, movement_flags) . = ..() UnregisterSignal(liver_owner, list(COMSIG_HUMAN_EQUIPPING_ITEM, COMSIG_LIVING_TRY_PULL, COMSIG_CARBON_HELPED)) diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm index f10dcefb0d5..251c5c35a49 100644 --- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm @@ -74,7 +74,7 @@ //but 1.5 damage human_receiver.physiology?.damage_resistance -= 50 -/obj/item/organ/heart/rat/on_mob_remove(mob/living/carbon/heartless, special) +/obj/item/organ/heart/rat/on_mob_remove(mob/living/carbon/heartless, special, movement_flags) . = ..() if(!ishuman(heartless)) return diff --git a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm index 2b32ad85702..03955f06ffe 100644 --- a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm @@ -72,7 +72,7 @@ QDEL_NULL(roach_shell) return ..() -/obj/item/organ/heart/roach/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/heart/roach/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(!ishuman(organ_owner)) return @@ -87,7 +87,7 @@ . = ..() limb.add_bodypart_overlay(roach_shell) -/obj/item/organ/heart/roach/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/heart/roach/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(!ishuman(organ_owner) || QDELETED(organ_owner)) return @@ -195,7 +195,7 @@ . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach) -/obj/item/organ/liver/roach/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/liver/roach/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(!ishuman(organ_owner)) return @@ -203,7 +203,7 @@ var/mob/living/carbon/human/human_owner = owner human_owner.physiology.tox_mod *= 2 -/obj/item/organ/liver/roach/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/liver/roach/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(!ishuman(organ_owner) || QDELETED(organ_owner)) return diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 04f67cee1c1..0e9fcc6b43a 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1390,9 +1390,10 @@ /obj/machinery/door/airlock/proc/prison_open() if(obj_flags & EMAGGED) return - locked = FALSE + if(locked) + unbolt() open() - locked = TRUE + bolt() return // gets called when a player uses an airlock painter on this airlock @@ -1816,7 +1817,7 @@ if(istype(mover) && (mover.pass_flags & PASSGLASS)) return !opacity -/obj/structure/fluff/airlock_filler/can_be_pulled(user, grab_state, force) +/obj/structure/fluff/airlock_filler/can_be_pulled(user, force) return FALSE /obj/structure/fluff/airlock_filler/singularity_act() diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 858f2dffeff..36828273f19 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -548,18 +548,20 @@ if(welded || operating) return + var/atom/crowbar_owner = acting_object.loc //catchs mechs and any other non-mob using a crowbar + if(density) being_held_open = TRUE - user.balloon_alert_to_viewers("holding firelock open", "holding firelock open") + crowbar_owner.balloon_alert_to_viewers("holding firelock open", "holding firelock open") COOLDOWN_START(src, activation_cooldown, REACTIVATION_DELAY) open() - if(QDELETED(user)) + if(QDELETED(crowbar_owner)) being_held_open = FALSE return - RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(handle_held_open_adjacency)) - RegisterSignal(user, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(handle_held_open_adjacency)) - RegisterSignal(user, COMSIG_QDELETING, PROC_REF(handle_held_open_adjacency)) - handle_held_open_adjacency(user) + RegisterSignal(crowbar_owner, COMSIG_MOVABLE_MOVED, PROC_REF(handle_held_open_adjacency)) + RegisterSignal(crowbar_owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(handle_held_open_adjacency)) + RegisterSignal(crowbar_owner, COMSIG_QDELETING, PROC_REF(handle_held_open_adjacency)) + handle_held_open_adjacency(crowbar_owner) else close() diff --git a/code/game/machinery/flatpacker.dm b/code/game/machinery/flatpacker.dm index 6c90e45e4f6..4a0e78f5207 100644 --- a/code/game/machinery/flatpacker.dm +++ b/code/game/machinery/flatpacker.dm @@ -165,9 +165,8 @@ qdel(null_comp) return costs -/obj/machinery/flatpacker/item_interaction(mob/living/user, obj/item/attacking_item, params) - . = NONE - if(user.combat_mode || attacking_item.flags_1 & HOLOGRAM_1 || attacking_item.item_flags & ABSTRACT) +/obj/machinery/flatpacker/base_item_interaction(mob/living/user, obj/item/attacking_item, list/modifiers) + if(attacking_item.flags_1 & HOLOGRAM_1 || attacking_item.item_flags & ABSTRACT) return ITEM_INTERACT_SKIP_TO_ATTACK if(istype(attacking_item, /obj/item/circuitboard/machine)) @@ -193,6 +192,8 @@ update_appearance(UPDATE_OVERLAYS) return ITEM_INTERACT_SUCCESS + return ..() + /obj/machinery/flatpacker/screwdriver_act(mob/living/user, obj/item/tool) . = ITEM_INTERACT_BLOCKING if(default_deconstruction_screwdriver(user, "[base_icon_state]_o", base_icon_state, tool)) diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index 4949f53adfb..bbb6f33236d 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -179,7 +179,7 @@ obj_flags |= EMAGGED allow_living = TRUE allow_clothing = TRUE - balloon_alert(!user, "lifesign scanners overloaded") + balloon_alert(user, "lifesign scanners overloaded") return TRUE /obj/machinery/harvester/container_resist_act(mob/living/user) diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index c2fb218d50a..6db5f9d4f15 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -221,7 +221,7 @@ for(var/atom/movable/ROI in source) if(ROI == src) continue - if(!istype(ROI) || isdead(ROI) || iscameramob(ROI) || istype(ROI, /obj/effect/dummy/phased_mob)) + if(!istype(ROI) || isdead(ROI) || iseyemob(ROI) || istype(ROI, /obj/effect/dummy/phased_mob)) continue//don't teleport these var/on_chair = "" if(ROI.anchored)// if it's anchored, don't teleport diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index 47ab476cbca..39fe7ce6cb7 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -230,7 +230,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) data["channelLocked"] = current_channel.locked data["channelCensored"] = current_channel.censored - //We send all the information about all messages in existance. + //We send all the information about all messages in existence. data["messages"] = message_list data["wanted"] = wanted_info @@ -699,15 +699,17 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) * Finally, it submits the message to the network, is logged globally, and clears all message-specific variables from the machine. */ /obj/machinery/newscaster/proc/create_story(channel_name) - for(var/datum/feed_channel/potential_channel as anything in GLOB.news_network.network_channels) - if(channel_name == potential_channel.channel_ID) - current_channel = potential_channel - break var/temp_message = tgui_input_text(usr, "Write your Feed story", "Network Channel Handler", feed_channel_message, max_length = MAX_BROADCAST_LEN, multiline = TRUE) if(length(temp_message) <= 1) return TRUE if(temp_message) feed_channel_message = temp_message + + for(var/datum/feed_channel/potential_channel as anything in GLOB.news_network.network_channels) + if(channel_name == potential_channel.channel_ID) + current_channel = potential_channel + break + GLOB.news_network.submit_article("[parsemarkdown(feed_channel_message, usr)]", newscaster_username, current_channel.channel_name, send_photo_data(), adminMessage = FALSE, allow_comments = TRUE) SSblackbox.record_feedback("amount", "newscaster_stories", 1) feed_channel_message = "" diff --git a/code/game/machinery/newscaster/newspaper.dm b/code/game/machinery/newscaster/newspaper.dm index 6bc1e6c77ff..648b64d58e8 100644 --- a/code/game/machinery/newscaster/newspaper.dm +++ b/code/game/machinery/newscaster/newspaper.dm @@ -89,6 +89,7 @@ return add_fingerprint(user) user.balloon_alert(user, "scribbling...") + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) if(!do_after(user, 2 SECONDS, src)) return user.balloon_alert(user, "scribbled!") diff --git a/code/game/machinery/scanner_gate.dm b/code/game/machinery/scanner_gate.dm index 3d87e4cb60b..ee93f41cf55 100644 --- a/code/game/machinery/scanner_gate.dm +++ b/code/game/machinery/scanner_gate.dm @@ -5,7 +5,6 @@ #define SCANGATE_WANTED "Wanted" #define SCANGATE_SPECIES "Species" #define SCANGATE_NUTRITION "Nutrition" -#define SCANGATE_CONTRABAND "Contraband" /obj/machinery/scanner_gate name = "scanner gate" @@ -39,8 +38,6 @@ var/minus_false_beep = 0 ///Base false positive/negative chance var/base_false_beep = 5 - ///Is an n-spect scanner attached to the gate? Enables contraband scanning. - var/obj/item/inspector/n_spect = null ///List of species that can be scanned by the gate. Supports adding more species' IDs during in-game. var/list/available_species = list( SPECIES_HUMAN, @@ -76,12 +73,6 @@ for(var/datum/stock_part/scanning_module/scanning_module in component_parts) minus_false_beep = scanning_module.tier //The better are scanninning modules - the lower is chance of False Positives -/obj/machinery/scanner_gate/atom_deconstruct(disassembled) - . = ..() - if(n_spect) - n_spect.forceMove(drop_location()) - n_spect = null - /obj/machinery/scanner_gate/examine(mob/user) . = ..() @@ -90,18 +81,6 @@ . += span_notice("The control panel is ID-locked. Swipe a valid ID to unlock it.") else . += span_notice("The control panel is unlocked. Swipe an ID to lock it.") - if(n_spect) - . += span_notice("The scanner is equipped with an N-Spect scanner. Use a [span_boldnotice("crowbar")] to uninstall.") - -/obj/machinery/scanner_gate/add_context(atom/source, list/context, obj/item/held_item, mob/user) - . = ..() - if(n_spect && held_item?.tool_behaviour == TOOL_CROWBAR) - context[SCREENTIP_CONTEXT_LMB] = "Remove N-Spect scanner" - return CONTEXTUAL_SCREENTIP_SET - if(!n_spect && istype(held_item, /obj/item/inspector)) - context[SCREENTIP_CONTEXT_LMB] = "Install N-Spect scanner" - return CONTEXTUAL_SCREENTIP_SET - /obj/machinery/scanner_gate/proc/on_entered(datum/source, atom/movable/thing) SIGNAL_HANDLER @@ -136,19 +115,6 @@ return set_scanline("passive") -/obj/machinery/scanner_gate/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(istype(tool, /obj/item/inspector)) - if(n_spect) - to_chat(user, span_warning("The scanner is already equipped with an N-Spect scanner.")) - return ITEM_INTERACT_BLOCKING - else - to_chat(user, span_notice("You install an N-Spect scanner on [src].")) - n_spect = tool - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - return ITEM_INTERACT_SUCCESS - return NONE - /obj/machinery/scanner_gate/attackby(obj/item/attacking_item, mob/user, params) var/obj/item/card/id/card = attacking_item.GetID() if(card) @@ -171,24 +137,6 @@ wires.interact(user) return ..() -/obj/machinery/scanner_gate/crowbar_act(mob/living/user, obj/item/tool) - . = ..() - if(n_spect) - if(locked) - balloon_alert(user, "locked!") - return ITEM_INTERACT_BLOCKING - - to_chat(user, span_notice("You uninstall [n_spect] from [src].")) - n_spect.forceMove(drop_location()) - return ITEM_INTERACT_SUCCESS - -/obj/machinery/scanner_gate/Exited(atom/gone) - . = ..() - if(gone == n_spect) - n_spect = null - if(scangate_mode == SCANGATE_CONTRABAND) - scangate_mode = SCANGATE_NONE - /obj/machinery/scanner_gate/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) return FALSE @@ -202,6 +150,7 @@ var/beep = FALSE var/color = null var/detected_thing = null + var/bypassed = FALSE playsound(src, SFX_INDUSTRIAL_SCAN, 20, TRUE, -2, TRUE, FALSE) switch(scangate_mode) if(SCANGATE_NONE) @@ -251,7 +200,7 @@ if((!HAS_TRAIT(scanned_human, TRAIT_MINDSHIELD)) && (isnull(idcard) || !(ACCESS_WEAPONS in idcard.access))) // mindshield or ID card with weapons access, like bartender beep = TRUE break - say("[detected_thing] detection bypassed.") + bypassed = TRUE break else for(var/obj/item/content in thing.get_all_contents_skipping_traits(TRAIT_CONTRABAND_BLOCKER)) @@ -267,14 +216,6 @@ if(scanned_human.nutrition >= detect_nutrition && detect_nutrition == NUTRITION_LEVEL_FAT) beep = TRUE detected_thing = "Obesity" - if(SCANGATE_CONTRABAND) - for(var/obj/item/content in thing.get_all_contents_skipping_traits(TRAIT_CONTRABAND_BLOCKER)) - detected_thing = "Contraband" - if(content.is_contraband()) - beep = TRUE - break - if(!n_spect.scans_correctly) - beep = !beep //We do a little trolling if(reverse) beep = !beep @@ -291,6 +232,8 @@ assembly?.activate() else SEND_SIGNAL(src, COMSIG_SCANGATE_PASS_NO_TRIGGER, thing) + if(bypassed) + say("[detected_thing] detection bypassed.") if(!ignore_signals) color = wires.get_color_of_wire(WIRE_DENY) var/obj/item/assembly/assembly = wires.get_attached(color) @@ -338,7 +281,6 @@ data["disease_threshold"] = disease_threshold data["target_species_id"] = detect_species_id data["target_nutrition"] = detect_nutrition - data["contraband_enabled"] = !!n_spect data["target_zombie"] = (detect_species_id == SPECIES_ZOMBIE) return data @@ -385,7 +327,7 @@ /obj/machinery/scanner_gate/preset_guns locked = TRUE - req_access = ACCESS_SECURITY + req_access = list(ACCESS_SECURITY) scangate_mode = SCANGATE_GUNS #undef SCANGATE_NONE @@ -395,4 +337,3 @@ #undef SCANGATE_WANTED #undef SCANGATE_SPECIES #undef SCANGATE_NUTRITION -#undef SCANGATE_CONTRABAND diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index f06ac5d9209..4104367a73e 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -215,23 +215,25 @@ if(target == src) return FALSE - // Check if the target to buckle isn't INSIDE OF A WALL - if(!isopenturf(loc) || !isopenturf(target.loc)) - return FALSE - - // Check if the target to buckle isn't A SOLID OBJECT (not including vehicles) var/turf/ground = get_turf(src) - if(ground.is_blocked_turf(exclude_mobs = TRUE, source_atom = src)) - return FALSE + // If we're not already on the same turf as our target... + if(get_turf(target) != ground) + // Check if the target to buckle isn't INSIDE OF A WALL + if(!isopenturf(loc) || !isopenturf(target.loc)) + return FALSE + + // Check if the target to buckle isn't INSIDE A SOLID OBJECT (not including vehicles) + if(ground.is_blocked_turf(exclude_mobs = TRUE, source_atom = src)) + return FALSE + + // If we're checking the loc, make sure the target is on the thing we're bucking them to. + if(check_loc && !target.Adjacent(src)) + return FALSE // Check if this atom can have things buckled to it. if(!can_buckle && !force) return FALSE - // If we're checking the loc, make sure the target is on the thing we're bucking them to. - if(check_loc && !target.Adjacent(src)) - return FALSE - // Make sure the target isn't already buckled to something. if(target.buckled) return FALSE diff --git a/code/game/objects/effects/anomalies/anomalies_bluespace.dm b/code/game/objects/effects/anomalies/anomalies_bluespace.dm index 440d4961ffb..9cea9929eba 100644 --- a/code/game/objects/effects/anomalies/anomalies_bluespace.dm +++ b/code/game/objects/effects/anomalies/anomalies_bluespace.dm @@ -65,7 +65,7 @@ for (var/atom/movable/A in urange(12, FROM )) // iterate thru list of mobs in the area if(istype(A, /obj/item/beacon)) continue // don't teleport beacons because that's just insanely stupid - if(iscameramob(A)) + if(iseyemob(A)) continue // Don't mess with AI eye, blob eye, xenobio or advanced cameras if(A.anchored) continue diff --git a/code/game/objects/effects/decals/turfdecal/markings.dm b/code/game/objects/effects/decals/turfdecal/markings.dm index 0fe434d0db1..c49fdde9b18 100644 --- a/code/game/objects/effects/decals/turfdecal/markings.dm +++ b/code/game/objects/effects/decals/turfdecal/markings.dm @@ -231,6 +231,9 @@ /obj/effect/turf_decal/siding/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/end icon_state = "siding_plain_end" @@ -240,6 +243,9 @@ /obj/effect/turf_decal/siding/white/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/white/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/white/end icon_state = "siding_plain_end" @@ -249,6 +255,9 @@ /obj/effect/turf_decal/siding/red/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/red/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/red/end icon_state = "siding_plain_end" @@ -258,6 +267,9 @@ /obj/effect/turf_decal/siding/dark_red/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/dark_red/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/dark_red/end icon_state = "siding_plain_end" @@ -267,6 +279,9 @@ /obj/effect/turf_decal/siding/green/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/green/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/green/end icon_state = "siding_plain_end" @@ -276,6 +291,9 @@ /obj/effect/turf_decal/siding/dark_green/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/dark_green/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/dark_green/end icon_state = "siding_plain_end" @@ -285,6 +303,9 @@ /obj/effect/turf_decal/siding/blue/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/blue/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/blue/end icon_state = "siding_plain_end" @@ -294,6 +315,9 @@ /obj/effect/turf_decal/siding/dark_blue/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/dark_blue/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/dark_blue/end icon_state = "siding_plain_end" @@ -303,6 +327,9 @@ /obj/effect/turf_decal/siding/yellow/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/yellow/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/yellow/end icon_state = "siding_plain_end" @@ -312,6 +339,9 @@ /obj/effect/turf_decal/siding/purple/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/purple/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/purple/end icon_state = "siding_plain_end" @@ -321,6 +351,9 @@ /obj/effect/turf_decal/siding/brown/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/brown/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/brown/end icon_state = "siding_plain_end" @@ -330,6 +363,9 @@ /obj/effect/turf_decal/siding/dark/corner icon_state = "siding_plain_corner" +/obj/effect/turf_decal/siding/dark/inner_corner + icon_state = "siding_plain_corner_inner" + /obj/effect/turf_decal/siding/dark/end icon_state = "siding_plain_end" diff --git a/code/game/objects/effects/particle_holder.dm b/code/game/objects/effects/particle_holder.dm index b5b4fa47108..00a99ea967c 100644 --- a/code/game/objects/effects/particle_holder.dm +++ b/code/game/objects/effects/particle_holder.dm @@ -26,7 +26,7 @@ // Mouse opacity can get set to opaque by some objects when placed into the object's contents (storage containers). mouse_opacity = MOUSE_OPACITY_TRANSPARENT src.particle_flags = particle_flags - particles = new particle_path() + particles = get_particle_effect(particle_path) // /atom doesn't have vis_contents, /turf and /atom/movable do var/atom/movable/lie_about_areas = parent lie_about_areas.vis_contents += src @@ -36,6 +36,9 @@ RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) on_move(parent, null, NORTH) +/obj/effect/abstract/particle_holder/proc/get_particle_effect(particle_path) + return new particle_path() + /obj/effect/abstract/particle_holder/Destroy(force) QDEL_NULL(particles) parent = null @@ -67,3 +70,44 @@ /// Sets the particles position to the passed coordinates /obj/effect/abstract/particle_holder/proc/set_particle_position(x = 0, y = 0, z = 0) particles.position = list(x, y, z) + +/** + * A subtype of particle holder that reuses the same particles to reduce client lag + * when rendering certain atoms, usually found in large quantities and close together. + * Since it reuses the same instances, modifying an instance of particles will affect all atoms + * that show it, therefore procs like set_particle_position() shouldn't be used here. + */ +/obj/effect/abstract/particle_holder/cached + ///A static list meant to contain the availables instances of a particle path to use. + var/static/list/particles_by_type + /** + * The length of the pool of particles from which the chosen instance will be picked + * This provides an ever-so-lightly variety to the particles, so they don't all jarringly look EXACTLY the same + */ + var/max_particle_index = 4 + +/obj/effect/abstract/particle_holder/cached/Initialize(mapload, particle_path = /particles/smoke, particle_flags = NONE, max_particle_index) + src.max_particle_index = max_particle_index + return ..() + +/obj/effect/abstract/particle_holder/cached/Destroy(force) + particles = null + return ..() + +/obj/effect/abstract/particle_holder/cached/get_particle_effect(particle_path) + LAZYINITLIST(particles_by_type) + LAZYINITLIST(particles_by_type[particle_path]) + + var/list/particles_list = particles_by_type[particle_path] + var/index = rand(1, max_particle_index) + var/particles/chosen + if(length(particles_list) < index) + chosen = new particle_path() + particles_list += chosen + else + chosen = particles_list[index] + + return chosen + +/obj/effect/abstract/particle_holder/cached/set_particle_position(x = 0, y = 0, z = 0) + CRASH("[type] doesn't support set_particle_position()") diff --git a/code/game/objects/effects/particles/fire.dm b/code/game/objects/effects/particles/fire.dm index 99046858073..481849c00eb 100644 --- a/code/game/objects/effects/particles/fire.dm +++ b/code/game/objects/effects/particles/fire.dm @@ -34,6 +34,9 @@ spin = generator(GEN_NUM, list(-15,15), NORMAL_RAND) scale = generator(GEN_VECTOR, list(0.5,0.5), list(2,2), NORMAL_RAND) +/particles/embers/minor + spawning = 1 + /particles/embers/spark count = 3 spawning = 2 diff --git a/code/game/objects/effects/particles/smoke.dm b/code/game/objects/effects/particles/smoke.dm index 1a4cd5c0e56..651c305d13e 100644 --- a/code/game/objects/effects/particles/smoke.dm +++ b/code/game/objects/effects/particles/smoke.dm @@ -100,3 +100,26 @@ /particles/smoke/cyborg/heavy_damage lifespan = 0.8 SECONDS fade = 0.8 SECONDS + +/particles/hotspring_steam + icon = 'icons/effects/particles/smoke.dmi' + icon_state = list( + "steam_cloud_1" = 1, + "steam_cloud_2" = 1, + "steam_cloud_3" = 1, + "steam_cloud_4" = 1, + "steam_cloud_5" = 1, + ) + color = "#FFFFFFAA" + count = 6 + spawning = 0.5 + lifespan = 3 SECONDS + fade = 1.2 SECONDS + fadein = 0.4 SECONDS + position = generator(GEN_BOX, list(-17,-15,0), list(24,15,0), NORMAL_RAND) + scale = generator(GEN_VECTOR, list(0.9,0.9), list(1.1,1.1), NORMAL_RAND) + drift = generator(GEN_SPHERE, list(-0.01,0), list(0.01,0.01), UNIFORM_RAND) + spin = generator(GEN_NUM, list(-3,3), NORMAL_RAND) + gravity = list(0.05, 0.28) + friction = 0.3 + grow = 0.037 diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 18f63350426..21a51ba9bcb 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -130,29 +130,35 @@ linked = null return ..() -/obj/effect/portal/attack_ghost(mob/dead/observer/O) - if(!teleport(O, TRUE)) +/obj/effect/portal/attack_ghost(mob/dead/observer/ghost) + if(!teleport(ghost, force = TRUE)) return ..() + return BULLET_ACT_FORCE_PIERCE -/obj/effect/portal/proc/teleport(atom/movable/M, force = FALSE) - if(!force && (!istype(M) || iseffect(M) || (ismecha(M) && !mech_sized) || (!isobj(M) && !ismob(M)))) //Things that shouldn't teleport. +/obj/effect/portal/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) + if (!teleport(hitting_projectile, force = TRUE)) + return ..() + return BULLET_ACT_FORCE_PIERCE + +/obj/effect/portal/proc/teleport(atom/movable/moving, force = FALSE) + if(!force && (!istype(moving) || iseffect(moving) || (ismecha(moving) && !mech_sized) || (!isobj(moving) && !ismob(moving)))) //Things that shouldn't teleport. return var/turf/real_target = get_link_target_turf() if(!istype(real_target)) return FALSE - if(!force && (!ismecha(M) && !isprojectile(M) && M.anchored && !allow_anchored)) + if(!force && (!ismecha(moving) && !isprojectile(moving) && moving.anchored && !allow_anchored)) return var/no_effect = FALSE if(last_effect == world.time || sparkless) no_effect = TRUE else last_effect = world.time - var/turf/start_turf = get_turf(M) - if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport)) - if(isprojectile(M)) - var/obj/projectile/P = M + var/turf/start_turf = get_turf(moving) + if(do_teleport(moving, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport)) + if(isprojectile(moving)) + var/obj/projectile/P = moving P.ignore_source_check = TRUE - new /obj/effect/temp_visual/portal_animation(start_turf, src, M) + new /obj/effect/temp_visual/portal_animation(start_turf, src, moving) playsound(start_turf, SFX_PORTAL_ENTER, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) playsound(real_target, SFX_PORTAL_ENTER, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) return TRUE @@ -189,7 +195,7 @@ linked = P break -/obj/effect/portal/permanent/teleport(atom/movable/M, force = FALSE) +/obj/effect/portal/permanent/teleport(atom/movable/moving, force = FALSE) set_linked() // update portal links . = ..() @@ -213,9 +219,9 @@ name = "one-use portal" desc = "This is probably the worst decision you'll ever make in your life." -/obj/effect/portal/permanent/one_way/one_use/teleport(atom/movable/M, force = FALSE) +/obj/effect/portal/permanent/one_way/one_use/teleport(atom/movable/moving, force = FALSE) . = ..() - if (. && !isdead(M)) + if (. && !isdead(moving)) expire() /** diff --git a/code/game/objects/effects/shared_particle_holder.dm b/code/game/objects/effects/shared_particle_holder.dm new file mode 100644 index 00000000000..2fe0c976479 --- /dev/null +++ b/code/game/objects/effects/shared_particle_holder.dm @@ -0,0 +1,89 @@ +#define SHARED_PARTICLE_HOLDER_INDEX 1 +#define SHARED_PARTICLE_USER_NUM_INDEX 2 +// Assoc list of particle type/key -> list(list of particle holders, number of particle users) +GLOBAL_LIST_EMPTY(shared_particles) + +//A more abstract version of particle holder not bound to a specific object +/obj/effect/abstract/shared_particle_holder + name = "shared particle holder" + desc = "How are you reading this? Please make a bug report :)" + appearance_flags = KEEP_APART|KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + vis_flags = VIS_INHERIT_PLANE + layer = ABOVE_ALL_MOB_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + anchored = TRUE + /// Holds info about how this particle emitter works + /// See \code\__DEFINES\particles.dm + var/particle_flags = NONE + +/obj/effect/abstract/shared_particle_holder/Initialize(mapload, particle_path = /particles/smoke, particle_flags = NONE) + . = ..() + // Shouldn't exist outside of nullspace + loc = null + src.particle_flags = particle_flags + particles = new particle_path() + +/obj/effect/abstract/shared_particle_holder/Destroy(force) + QDEL_NULL(particles) + return ..() + +/* Adds (or creates and adds) a shared particle holder + * Shared particle holders are held in nullspace and added to vis_contents of all atoms using it + * in order to save clientside performance by making clients only render 3-5 particle holders + * for 400 objects using them. This should be prioritized over normal particles when possible if it is known + * that there will be a lot of objects using certain particles. + * custom_key can be used to create a new pool of already existing particle type in case you're planning to edit holder's color or properties + * pool_size controls how many particle holders per type are created. Any objects over this cap will pick an existing holder from the pool + */ +/atom/movable/proc/add_shared_particles(particle_type, custom_key = null, particle_flags = NONE, pool_size = 3) + var/particle_key = custom_key || "[particle_type]" + if (!GLOB.shared_particles[particle_key]) + GLOB.shared_particles[particle_key] = list(list(new /obj/effect/abstract/shared_particle_holder(null, particle_type, particle_flags)), 1) + vis_contents += GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX][1] + return GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX][1] + + var/list/type_holders = GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX] + for (var/obj/effect/abstract/shared_particle_holder/particle_holder as anything in type_holders) + if (particle_holder in vis_contents) + return particle_holder + + if (length(type_holders) < pool_size) + var/obj/effect/abstract/shared_particle_holder/new_holder = new(null, particle_type, particle_flags) + type_holders += new_holder + vis_contents += new_holder + GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] += 1 + return new_holder + + var/obj/effect/abstract/shared_particle_holder/particle_holder = pick(type_holders) + vis_contents += particle_holder + GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] += 1 + return particle_holder + +/* Removes shared particles from object's vis_contents and disposes of it if nothing uses that type/key of particle + * particle_key can be either a type (if no custom_key was passed) or said custom_key + */ +/atom/movable/proc/remove_shared_particles(particle_key, delete_on_empty = TRUE) + if (!particle_key) + return + + if (ispath(particle_key)) + particle_key = "[particle_key]" + + if (!GLOB.shared_particles[particle_key]) + return + + var/list/type_holders = GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX] + for (var/obj/effect/abstract/shared_particle_holder/particle_holder as anything in type_holders) + if (!(particle_holder in vis_contents)) + continue + + vis_contents -= particle_holder + GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] -= 1 + + if (delete_on_empty && GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] <= 0) + QDEL_LIST(type_holders) + GLOB.shared_particles -= particle_key + return + +#undef SHARED_PARTICLE_HOLDER_INDEX +#undef SHARED_PARTICLE_USER_NUM_INDEX diff --git a/code/game/objects/effects/spawners/random/contraband.dm b/code/game/objects/effects/spawners/random/contraband.dm index c32d46125c0..5b0c78659d5 100644 --- a/code/game/objects/effects/spawners/random/contraband.dm +++ b/code/game/objects/effects/spawners/random/contraband.dm @@ -23,6 +23,7 @@ /obj/item/storage/box/donkpockets = 10, /obj/effect/spawner/random/contraband/plus = 10, /obj/item/reagent_containers/pill/maintenance = 5, + /obj/item/survivalcapsule/fishing = 5, ) @@ -42,6 +43,7 @@ /obj/item/reagent_containers/cup/blastoff_ampoule = 5, /obj/item/food/drug/moon_rock = 5, /obj/item/grenade/empgrenade = 5, + /obj/item/survivalcapsule/fishing/hacked = 1, /obj/effect/spawner/random/contraband/armory = 1, ) diff --git a/code/game/objects/effects/temporary_visuals/effect_trail.dm b/code/game/objects/effects/temporary_visuals/effect_trail.dm index 9b28dcf909d..5eed9462dfd 100644 --- a/code/game/objects/effects/temporary_visuals/effect_trail.dm +++ b/code/game/objects/effects/temporary_visuals/effect_trail.dm @@ -2,7 +2,7 @@ /obj/effect/temp_visual/effect_trail name = "effect trail" desc = "An invisible effect, how did you examine this?" - icon = 'icons/mob/silicon/cameramob.dmi' + icon = 'icons/mob/eyemob.dmi' icon_state = "marker" duration = 15 SECONDS invisibility = INVISIBILITY_ABSTRACT diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 511547ac0a4..2aa979f9514 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -303,14 +303,6 @@ /obj/effect/temp_visual/gib_animation/animal icon = 'icons/mob/simple/animal.dmi' -/obj/effect/temp_visual/dust_animation - icon = 'icons/mob/simple/mob.dmi' - duration = 15 - -/obj/effect/temp_visual/dust_animation/Initialize(mapload, dust_icon) - icon_state = dust_icon // Before ..() so the correct icon is flick()'d - . = ..() - /obj/effect/temp_visual/mummy_animation icon = 'icons/mob/simple/mob.dmi' icon_state = "mummy_revive" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c35984dc026..9e2ca71eabc 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -449,7 +449,7 @@ .[weight_class_to_text(w_class)] = weight_class_to_tooltip(w_class) if(item_flags & CRUEL_IMPLEMENT) - .[span_red("morbid")] = "It seems quite practical for particularly morbid procedures and experiments." + .[span_red("morbid")] = "It seems quite practical for particularly morbid procedures and experiments." if (siemens_coefficient == 0) .["insulated"] = "It is made from a robust electrical insulator and will block any electricity passing through it!" @@ -1028,36 +1028,93 @@ ///Called BEFORE the object is ground up - use this to change grind results based on conditions. Return "-1" to prevent the grinding from occurring /obj/item/proc/on_grind() + PROTECTED_PROC(TRUE) + return SEND_SIGNAL(src, COMSIG_ITEM_ON_GRIND) ///Grind item, adding grind_results to item's reagents and transfering to target_holder if specified -/obj/item/proc/grind(datum/reagents/target_holder, mob/user) +/obj/item/proc/grind(datum/reagents/target_holder, mob/user, atom/movable/grinder = loc) + SHOULD_NOT_OVERRIDE(TRUE) + . = FALSE - if(on_grind() == -1) + if(on_grind() == -1 || target_holder.holder_full()) return + . = grind_atom(target_holder, user) + + //reccursive grinding to get all them juices + var/result + for(var/obj/item/ingredient as anything in get_all_contents_type(/obj/item)) + if(ingredient == src) + continue + + result = ingredient.grind(target_holder, user) + if(!.) + . = result + + if(. && istype(grinder)) + return grinder.blended(src, grinded = TRUE) + +///Subtypes override his proc for custom grinding +/obj/item/proc/grind_atom(datum/reagents/target_holder, mob/user) + PROTECTED_PROC(TRUE) + + . = FALSE if(length(grind_results)) target_holder.add_reagent_list(grind_results) . = TRUE - if(reagents?.total_volume) - reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) + if(reagents?.trans_to(target_holder, reagents.total_volume, transferred_by = user)) . = TRUE ///Called BEFORE the object is ground up - use this to change grind results based on conditions. Return "-1" to prevent the grinding from occurring /obj/item/proc/on_juice() + PROTECTED_PROC(TRUE) + if(!juice_typepath) return -1 + return SEND_SIGNAL(src, COMSIG_ITEM_ON_JUICE) ///Juice item, converting nutriments into juice_typepath and transfering to target_holder if specified -/obj/item/proc/juice(datum/reagents/target_holder, mob/user) +/obj/item/proc/juice(datum/reagents/target_holder, mob/user, atom/movable/juicer = loc) + SHOULD_NOT_OVERRIDE(TRUE) + + . = FALSE if(on_juice() == -1 || !reagents?.total_volume) - return FALSE + return + + . = juice_atom(target_holder, user) + + //reccursive juicing to get all them juices + var/result + for(var/obj/item/ingredient as anything in get_all_contents_type(/obj/item)) + if(ingredient == src) + continue + + result = ingredient.juice(target_holder, user) + if(!.) + . = result + + if(. && istype(juicer)) + return juicer.blended(src, grinded = FALSE) + +///Subtypes override his proc for custom juicing +/obj/item/proc/juice_atom(datum/reagents/target_holder, mob/user) + PROTECTED_PROC(TRUE) + + . = FALSE if(ispath(juice_typepath)) reagents.convert_reagent(/datum/reagent/consumable/nutriment, juice_typepath, include_source_subtypes = FALSE) reagents.convert_reagent(/datum/reagent/consumable/nutriment/vitamin, juice_typepath, include_source_subtypes = FALSE) - reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) + . = TRUE + + if(!QDELETED(target_holder)) + reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) + +///What should The atom that blended an object do with it afterwards? Default behaviour is to delete it +/atom/movable/proc/blended(obj/item/blended_item, grinded) + qdel(blended_item) return TRUE diff --git a/code/game/objects/items/body_egg.dm b/code/game/objects/items/body_egg.dm index d5769ceb8d7..7a8d70888f0 100644 --- a/code/game/objects/items/body_egg.dm +++ b/code/game/objects/items/body_egg.dm @@ -15,14 +15,14 @@ if(iscarbon(loc)) Insert(loc) -/obj/item/organ/body_egg/mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/body_egg/on_mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags) . = ..() egg_owner.add_traits(list(TRAIT_XENO_HOST, TRAIT_XENO_IMMUNE), ORGAN_TRAIT) egg_owner.med_hud_set_status() INVOKE_ASYNC(src, PROC_REF(AddInfectionImages), egg_owner) -/obj/item/organ/body_egg/mob_remove(mob/living/carbon/egg_owner, special, movement_flags) +/obj/item/organ/body_egg/on_mob_remove(mob/living/carbon/egg_owner, special, movement_flags) . = ..() egg_owner.remove_traits(list(TRAIT_XENO_HOST, TRAIT_XENO_IMMUNE), ORGAN_TRAIT) egg_owner.med_hud_set_status() diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 0f58bc10b8c..e1bb041885e 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -1907,6 +1907,7 @@ var/input_name = sanitize_name(raw_input, allow_numbers = TRUE) if(!after_input_check(user, item, input_name, scribbled_name)) return + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) scribbled_name = input_name var/list/details = item.get_writing_implement_details() details_colors[INDEX_NAME_COLOR] = details["color"] || COLOR_BLACK @@ -1914,6 +1915,7 @@ var/input_assignment = tgui_input_text(user, "What assignment would you like to put on this card?", "Cardboard card job ssignment", scribbled_assignment || "Assistant", max_length = MAX_NAME_LEN) if(!after_input_check(user, item, input_assignment, scribbled_assignment)) return + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) scribbled_assignment = sanitize(input_assignment) var/list/details = item.get_writing_implement_details() details_colors[INDEX_ASSIGNMENT_COLOR] = details["color"] || COLOR_BLACK @@ -1933,6 +1935,7 @@ var/input_trim = tgui_input_list(user, "Select trim to apply to your card.\nNote: This will not grant any trim accesses.", "Forge Trim", possible_trims) if(!input_trim || !after_input_check(user, item, input_trim, scribbled_trim)) return + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) scribbled_trim = "cardboard_[input_trim]" scribbled_trim_icon = possible_trim_icons[input_trim] var/list/details = item.get_writing_implement_details() diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index 5eddcf93d4b..51b38d032c0 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -185,11 +185,6 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/cigarette/Initialize(mapload) . = ..() - create_reagents(chem_volume, INJECTABLE | NO_REACT) - if(list_reagents) - reagents.add_reagent_list(list_reagents) - if(starts_lit) - light() AddComponent(/datum/component/knockoff, 90, list(BODY_ZONE_PRECISE_MOUTH), slot_flags) //90% to knock off when wearing a mask AddElement(/datum/element/update_icon_updates_onmob) RegisterSignal(src, COMSIG_ATOM_TOUCHED_SPARKS, PROC_REF(sparks_touched)) @@ -201,15 +196,17 @@ CIGARETTE PACKETS ARE IN FANCY.DM initial_reagents = list_reagents,\ food_flags = FOOD_NO_EXAMINE,\ foodtypes = JUNKFOOD,\ - volume = 50,\ + volume = chem_volume,\ eat_time = 0 SECONDS,\ - tastes = list("a never before experienced flavour.", "finally sitting down after standing your entire life"),\ + tastes = list("a never before experienced flavour", "finally sitting down after standing your entire life"),\ eatverbs = list("taste"),\ - bite_consumption = 50,\ + bite_consumption = chem_volume,\ junkiness = 0,\ reagent_purity = null,\ on_consume = CALLBACK(src, PROC_REF(on_consume)),\ ) + if(starts_lit) + light() /obj/item/cigarette/Destroy() STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/climbingrope.dm b/code/game/objects/items/climbingrope.dm index 936858bbae7..46bc56e21fe 100644 --- a/code/game/objects/items/climbingrope.dm +++ b/code/game/objects/items/climbingrope.dm @@ -24,7 +24,7 @@ . = ..() var/list/look_binds = user.client.prefs.key_bindings["look up"] . += span_notice("Firstly, look upwards by holding [english_list(look_binds, nothing_text = "(nothing bound)", and_text = " or ", comma_text = ", or ")]!") - . += span_notice("Then, click solid ground adjacent to the hole above you.") + . += span_notice("Then, click solid ground (or lattice/catwalk) adjacent to the hole above you.") . += span_notice("The rope looks like you could use it [uses] times before it falls apart.") /obj/item/climbing_hook/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) @@ -32,26 +32,27 @@ return NONE return ranged_interact_with_atom(interacting_with, user, modifiers) -/obj/item/climbing_hook/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) +/obj/item/climbing_hook/ranged_interact_with_atom(turf/open/interacting_with, mob/living/user, list/modifiers) + interacting_with = get_turf(interacting_with) if(interacting_with.z == user.z) return NONE - var/turf/open/target = interacting_with - if(!istype(target) || isopenspaceturf(target)) + if(!istype(interacting_with) || !isturf(user.loc)) //better safe than sorry return ITEM_INTERACT_BLOCKING var/turf/user_turf = get_turf(user) var/turf/above = GET_TURF_ABOVE(user_turf) - if(target_blocked(target, above)) + if(target_blocked(interacting_with, above)) + balloon_alert(user, "cant get there!") return ITEM_INTERACT_BLOCKING - if(!isopenspaceturf(above) || !above.Adjacent(target)) //are we below a hole, is the target blocked, is the target adjacent to our hole - balloon_alert(user, "blocked!") + if(!above.Adjacent(interacting_with)) //is the target adjacent to our hole + balloon_alert(user, "too far!") return ITEM_INTERACT_BLOCKING - var/away_dir = get_dir(above, target) + var/away_dir = get_dir(above, interacting_with) user.visible_message(span_notice("[user] begins climbing upwards with [src]."), span_notice("You get to work on properly hooking [src] and going upwards.")) - playsound(target, 'sound/effects/pickaxe/picaxe1.ogg', 50) //plays twice so people above and below can hear + playsound(interacting_with, 'sound/effects/pickaxe/picaxe1.ogg', 50) //plays twice so people above and below can hear playsound(user_turf, 'sound/effects/pickaxe/picaxe1.ogg', 50) - var/list/effects = list(new /obj/effect/temp_visual/climbing_hook(target, away_dir), new /obj/effect/temp_visual/climbing_hook(user_turf, away_dir)) + var/list/effects = list(new /obj/effect/temp_visual/climbing_hook(interacting_with, away_dir), new /obj/effect/temp_visual/climbing_hook(user_turf, away_dir)) // Our climbers athletics ability var/fitness_level = user.mind?.get_skill_level(/datum/skill/athletics) @@ -65,8 +66,8 @@ var/final_climb_time = (climb_time - fitness_level) * misc_multiplier - if(do_after(user, final_climb_time, target)) - user.forceMove(target) + if(do_after(user, final_climb_time, interacting_with)) + user.forceMove(interacting_with) uses-- user.mind?.adjust_experience(/datum/skill/athletics, 50) //get some experience for our trouble, especially since this costs us a climbing rope use @@ -80,7 +81,7 @@ // didnt want to mess up is_blocked_turf_ignore_climbable /// checks if our target is blocked, also checks for border objects facing the above turf and climbable stuff /obj/item/climbing_hook/proc/target_blocked(turf/target, turf/above) - if(target.density || above.density) + if(target.density || (isopenspaceturf(target) && target.zPassOut(DOWN)) || !above.zPassOut(DOWN) || above.density) // we check if we would fall down from it additionally return TRUE for(var/atom/movable/atom_content as anything in target.contents) diff --git a/code/game/objects/items/devices/broadcast_camera.dm b/code/game/objects/items/devices/broadcast_camera.dm index 78868844e48..40f8fe0a871 100644 --- a/code/game/objects/items/devices/broadcast_camera.dm +++ b/code/game/objects/items/devices/broadcast_camera.dm @@ -13,7 +13,7 @@ force = 8 throwforce = 12 w_class = WEIGHT_CLASS_NORMAL - obj_flags = INDESTRUCTIBLE | EMP_PROTECT_ALL // No fun police + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF slot_flags = NONE light_system = OVERLAY_LIGHT light_color = COLOR_SOFT_RED @@ -33,6 +33,11 @@ /// The "virtual" radio inside of the the physical camera, a la microphone var/obj/item/radio/entertainment/microphone/internal_radio +/obj/item/broadcast_camera/Initialize(mapload) + . = ..() + + AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) + /obj/item/broadcast_camera/Destroy(force) QDEL_NULL(internal_radio) QDEL_NULL(internal_camera) diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 03e53a87db3..2b6aaebeda7 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -66,7 +66,7 @@ /obj/item/laser_pointer/infinite_range name = "infinite laser pointer" desc = "Used to shine in the eyes of Cyborgs who need a bit of a push, this works through camera consoles." - max_range = INFINITE + max_range = INFINITY /obj/item/laser_pointer/infinite_range/Initialize(mapload) . = ..() @@ -203,7 +203,7 @@ to_chat(user, span_warning("Your fingers can't press the button!")) return - if(max_range != INFINITE) + if(max_range != INFINITY) if(!IN_GIVEN_RANGE(target, user, max_range)) to_chat(user, span_warning("\The [target] is too far away!")) return diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 4838d6c8916..0ea1e867b4c 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -204,7 +204,7 @@ var/turf/our_turf = get_turf(src) detect_state = PROXIMITY_NONE - for(var/mob/camera/ai_eye/AI_eye as anything in GLOB.aiEyes) + for(var/mob/eye/camera/ai/AI_eye as anything in GLOB.camera_eyes) if(!AI_eye.ai_detector_visible) continue @@ -253,7 +253,7 @@ // copied from camera chunks but we are doing a really big edge case here though /obj/item/multitool/ai_detect/proc/surrounding_chunks(turf/epicenter) . = list() - var/static_range = /mob/camera/ai_eye::static_visibility_range + var/static_range = /mob/eye/camera/ai::static_visibility_range var/x1 = max(1, epicenter.x - static_range) var/y1 = max(1, epicenter.y - static_range) var/x2 = min(world.maxx, epicenter.x + static_range) diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 5a108c83e82..c1ed690a314 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -341,7 +341,7 @@ effective or pretty fucking useless. . = ..() register_context() -/atom/movable/screen/alert/give/add_context(atom/source, list/context, obj/item/held_item, mob/user) +/obj/item/jammer/add_context(atom/source, list/context, obj/item/held_item, mob/user) context[SCREENTIP_CONTEXT_LMB] = "Release distruptor wave" context[SCREENTIP_CONTEXT_RMB] = "Toggle" return CONTEXTUAL_SCREENTIP_SET diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm index 40f08e78ffc..e44e989706a 100644 --- a/code/game/objects/items/eightball.dm +++ b/code/game/objects/items/eightball.dm @@ -15,26 +15,33 @@ var/cooldown_time = 10 SECONDS var/static/list/possible_answers = list( - "It is certain", - "It is decidedly so", - "Without a doubt", - "Yes definitely", - "You may rely on it", - "As I see it, yes", - "Most likely", - "Outlook good", - "Yes", - "Signs point to yes", - "Reply hazy try again", - "Ask again later", - "Better not tell you now", - "Cannot predict now", - "Concentrate and ask again", - "Don't count on it", - "My reply is no", - "My sources say no", - "Outlook not so good", - "Very doubtful") + "Yes" = list( + "It is certain", + "It is decidedly so", + "Without a doubt", + "Yes definitely", + "You may rely on it", + "As I see it, yes", + "Most likely", + "Outlook good", + "Yes", + "Signs point to yes", + ), + "Maybe" = list( + "Reply hazy try again", + "Ask again later", + "Better not tell you now", + "Cannot predict now", + "Concentrate and ask again", + ), + "No" = list( + "Don't count on it", + "My reply is no", + "My sources say no", + "Outlook not so good", + "Very doubtful" + ), + ) /obj/item/toy/eightball/Initialize(mapload) . = ..() @@ -77,8 +84,14 @@ /obj/item/toy/eightball/proc/start_shaking(mob/user) return TRUE +/// Different from get_answer(). +/obj/item/toy/eightball/proc/pick_from_answer_list() + //! This is for grabbing an answer from the answer matrix. + var/key = pick(possible_answers) + return pick(possible_answers[key]) + /obj/item/toy/eightball/proc/get_answer() - return pick(possible_answers) + return pick_from_answer_list() // A broken magic eightball, it only says "YOU SUCK" over and over again. @@ -89,7 +102,7 @@ /obj/item/toy/eightball/broken/Initialize(mapload) . = ..() - fixed_answer = pick(possible_answers) + fixed_answer = pick_from_answer_list() /obj/item/toy/eightball/broken/get_answer() return fixed_answer @@ -104,38 +117,10 @@ //these kind of store the same thing but one is easier to work with. var/list/votes = list() var/list/voted = list() - var/static/list/haunted_answers = list( - "yes" = list( - "It is certain", - "It is decidedly so", - "Without a doubt", - "Yes definitely", - "You may rely on it", - "As I see it, yes", - "Most likely", - "Outlook good", - "Yes", - "Signs point to yes" - ), - "maybe" = list( - "Reply hazy try again", - "Ask again later", - "Better not tell you now", - "Cannot predict now", - "Concentrate and ask again" - ), - "no" = list( - "Don't count on it", - "My reply is no", - "My sources say no", - "Outlook not so good", - "Very doubtful" - ) - ) /obj/item/toy/eightball/haunted/Initialize(mapload) . = ..() - for (var/answer in haunted_answers) + for (var/answer in possible_answers) votes[answer] = 0 SSpoints_of_interest.make_point_of_interest(src) @@ -153,7 +138,7 @@ /obj/item/toy/eightball/haunted/start_shaking(mob/user) // notify ghosts that someone's shaking a haunted eightball // and inform them of the message, (hopefully a yes/no question) - selected_message = tgui_input_text(user, "What is your question?", "Eightball", max_length = MAX_MESSAGE_LEN) || initial(selected_message) + selected_message = tgui_input_text(user, "What is your question?", "Eightball", max_length = CHAT_MESSAGE_MAX_LENGTH) || initial(selected_message) if (!(src in user.held_items)) return FALSE notify_ghosts( @@ -186,7 +171,7 @@ voted.Cut() - var/list/top_options = haunted_answers[top_vote] + var/list/top_options = possible_answers[top_vote] return pick(top_options) // Only ghosts can interact because only ghosts can open the ui @@ -210,11 +195,11 @@ data["question"] = selected_message data["answers"] = list() - for(var/vote in haunted_answers) + for(var/vote in possible_answers) var/list/answer_data = list() answer_data["answer"] = vote answer_data["amount"] = votes[vote] - answer_data["selected"] = voted[user.ckey] + answer_data["selected"] = (voted[user.ckey] == vote) data["answers"] += list(answer_data) return data @@ -229,7 +214,7 @@ switch(action) if("vote") var/selected_answer = params["answer"] - if(!(selected_answer in haunted_answers)) + if(!(selected_answer in possible_answers)) return var/oldvote = voted[user.ckey] if(oldvote) diff --git a/code/game/objects/items/food/lizard.dm b/code/game/objects/items/food/lizard.dm index 2048c997ef9..f8d8dbec8f5 100644 --- a/code/game/objects/items/food/lizard.dm +++ b/code/game/objects/items/food/lizard.dm @@ -125,7 +125,8 @@ /obj/item/food/moonfish_eggs name = "moonfish eggs" - desc = "The moonfish lays large, transparent white eggs which are prized in lizard cooking. Their flavour is similar to caviar, but generally is described as deeper and more complex." + gender = PLURAL + desc = "The moonfish lays large, translucent blue eggs which are prized in lizard cooking. Their flavour is similar to caviar, but generally is described as deeper and more complex." icon = 'icons/obj/food/lizard.dmi' icon_state = "moonfish_eggs" food_reagents = list( @@ -137,6 +138,35 @@ w_class = WEIGHT_CLASS_SMALL crafting_complexity = FOOD_COMPLEXITY_1 +/obj/item/food/moonfish_eggs/Initialize(mapload) + . = ..() + //Moonfish can lay eggs (unaffected by breeding, so think of them as unfertilizard) + RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(generate_aquarium_appearance)) + RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) + AddComponent(/datum/component/aquarium_content) + RegisterSignal(src, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, PROC_REF(get_aquarium_beauty)) + +/obj/item/food/moonfish_eggs/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual) + SIGNAL_HANDLER + visual.icon = icon + visual.icon_state = "moonfish_eggs_aquarium" + visual.layer_mode = AQUARIUM_LAYER_MODE_BOTTOM + +/obj/item/food/moonfish_eggs/proc/randomize_aquarium_position(datum/source, obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) + SIGNAL_HANDLER + var/sprite_width = 5 + var/sprite_height = 4 + var/px_min = visual.aquarium_zone_min_px + var/px_max = visual.aquarium_zone_max_px - sprite_width + var/py_min = visual.aquarium_zone_min_py - sprite_height + + visual.pixel_x = rand(px_min, px_max) + visual.pixel_y = py_min + rand(-1, 1) + +/obj/item/food/moonfish_eggs/proc/get_aquarium_beauty(datum/source, list/beauty_holder) + SIGNAL_HANDLER + beauty_holder += 100 //moonfish eggs are kinda eye candy + /obj/item/food/moonfish_caviar name = "moonfish caviar paste" desc = "A rich paste made from moonfish eggs. Generally the only way most lizards can get them, and used fairly heavily in coastal cooking." @@ -551,7 +581,7 @@ /datum/reagent/consumable/nutriment/protein = 10, ) tastes = list("bread" = 1, "meat" = 1) - foodtypes = MEAT | NUTS | RAW | GORE + foodtypes = MEAT | NUTS | GORE crafting_complexity = FOOD_COMPLEXITY_3 /obj/item/food/pizza/flatbread/stinging diff --git a/code/game/objects/items/food/misc.dm b/code/game/objects/items/food/misc.dm index 6da965e4757..379169e6f73 100644 --- a/code/game/objects/items/food/misc.dm +++ b/code/game/objects/items/food/misc.dm @@ -195,7 +195,12 @@ . = ..() RegisterSignal(src, COMSIG_ITEM_GRILL_PROCESS, PROC_REF(OnGrill)) if(stink_particles) - particles = new stink_particles + add_shared_particles(stink_particles) + +/obj/item/food/badrecipe/Destroy(force) + if (stink_particles) + remove_shared_particles(stink_particles) + return ..() // We override the parent procs here to prevent burned messes from cooking into burned messes. /obj/item/food/badrecipe/make_grillable() diff --git a/code/game/objects/items/food/packaged.dm b/code/game/objects/items/food/packaged.dm index 13b8295cd1c..7c7faa80a62 100644 --- a/code/game/objects/items/food/packaged.dm +++ b/code/game/objects/items/food/packaged.dm @@ -218,7 +218,7 @@ /obj/item/food/ready_donk name = "\improper Ready-Donk: Bachelor Chow" desc = "A quick Donk-dinner: now with flavour!" - icon_state = "ready_donk" + icon_state = "ready_donk_bachelor" trash_type = /obj/item/trash/ready_donk food_reagents = list(/datum/reagent/consumable/nutriment = 5) tastes = list("food?" = 2, "laziness" = 1) @@ -249,7 +249,7 @@ /obj/item/food/ready_donk/warm name = "warm Ready-Donk: Bachelor Chow" desc = "A quick Donk-dinner, now with flavour! And it's even hot!" - icon_state = "ready_donk_warm" + icon_state = "ready_donk_bachelor_warm" food_reagents = list( /datum/reagent/consumable/nutriment = 5, /datum/reagent/medicine/omnizine = 3, @@ -262,6 +262,7 @@ /obj/item/food/ready_donk/mac_n_cheese name = "\improper Ready-Donk: Donk-a-Roni" desc = "Neon-orange mac n' cheese in seconds!" + icon_state = "ready_donk_mac" tastes = list("cheesy pasta" = 2, "laziness" = 1) foodtypes = GRAIN | DAIRY | JUNKFOOD @@ -270,13 +271,14 @@ /obj/item/food/ready_donk/warm/mac_n_cheese name = "warm Ready-Donk: Donk-a-Roni" desc = "Neon-orange mac n' cheese, ready to eat!" - icon_state = "ready_donk_warm_mac" + icon_state = "ready_donk_mac_warm" tastes = list("cheesy pasta" = 2, "laziness" = 1) foodtypes = GRAIN | DAIRY | JUNKFOOD /obj/item/food/ready_donk/donkhiladas name = "\improper Ready-Donk: Donkhiladas" desc = "Donk Co's signature Donkhiladas with Donk sauce, for an 'authentic' taste of Mexico." + icon_state = "ready_donk_mex" tastes = list("enchiladas" = 2, "laziness" = 1) foodtypes = GRAIN | DAIRY | MEAT | VEGETABLES | JUNKFOOD @@ -285,13 +287,14 @@ /obj/item/food/ready_donk/warm/donkhiladas name = "warm Ready-Donk: Donkhiladas" desc = "Donk Co's signature Donkhiladas with Donk sauce, served as hot as the Mexican sun." - icon_state = "ready_donk_warm_mex" + icon_state = "ready_donk_mex_warm" tastes = list("enchiladas" = 2, "laziness" = 1) foodtypes = GRAIN | DAIRY | MEAT | VEGETABLES | JUNKFOOD /obj/item/food/ready_donk/nachos_grandes //which translates to... big nachos name = "\improper Ready-Donk: Donk Sol Series Boritos Nachos Grandes" desc = "Get ready for game day with Donk's classic Nachos Grandes, sponsors of the Donk Sol Series! Boritos chips loaded with cheese, spicy meat and beans, alongside separate guac, pico and donk sauce. Batter up!" + icon_state = "ready_donk_nachos" tastes = list("nachos" = 2, "laziness" = 1) foodtypes = GRAIN | DAIRY | MEAT | VEGETABLES | JUNKFOOD @@ -300,25 +303,58 @@ /obj/item/food/ready_donk/warm/nachos_grandes name = "warm Ready-Donk: Donk Sol Series Boritos Nachos Grandes" desc = "Get ready for game day with Donk's classic Nachos Grandes, sponsors of the Donk Sol Series! Boritos chips loaded with cheese, spicy meat and beans, alongside separate guac, pico and donk sauce. Served hotter than Sakamoto's fastball!" - icon_state = "ready_donk_warm_nachos" + icon_state = "ready_donk_nachos_warm" tastes = list("nachos" = 2, "laziness" = 1) foodtypes = GRAIN | DAIRY | MEAT | VEGETABLES | JUNKFOOD /obj/item/food/ready_donk/donkrange_chicken name = "\improper Ready-Donk: Donk-range Chicken" desc = "A Chinese classic, it's Donk's original spicy orange chicken with stir-fried peppers and onions, all over steamed rice." + icon_state = "ready_donk_orange" tastes = list("orange chicken" = 2, "laziness" = 1) foodtypes = GRAIN | MEAT | VEGETABLES | JUNKFOOD warm_type = /obj/item/food/ready_donk/warm/donkrange_chicken /obj/item/food/ready_donk/warm/donkrange_chicken - name = "warm Ready-Donk: Ready-Donk: Donk-range Chicken" + name = "warm Ready-Donk: Donk-range Chicken" desc = "A Chinese classic, it's Donk's original spicy orange chicken with stir-fried peppers and onions, all over steamed rice and served hotter than a dragon's breath." - icon_state = "ready_donk_warm_orange" + icon_state = "ready_donk_orange_warm" tastes = list("orange chicken" = 2, "laziness" = 1) foodtypes = GRAIN | MEAT | VEGETABLES | JUNKFOOD +/obj/item/food/ready_donk/salisbury_steak + name = "\improper Ready-Donk Donkriginals: Salisbury Steak" + desc = "The original and best: it's a slab of moulded beef, drenched in brown gravy, with a side of mashed potatoes. Better find a TV to eat this in front of." + icon_state = "ready_donk_salisbury" + tastes = list("salisbury steak" = 2, "laziness" = 1) + foodtypes = MEAT | VEGETABLES | JUNKFOOD + + warm_type = /obj/item/food/ready_donk/warm/salisbury_steak + +/obj/item/food/ready_donk/warm/salisbury_steak + name = "warm Ready-Donk Donkriginals: Salisbury Steak" + desc = "The original and best: it's a slab of moulded beef, drenched in brown gravy, with a side of mashed potatoes. It's almost as hot as a season finale." + icon_state = "ready_donk_salisbury_warm" + tastes = list("salisbury steak" = 2, "laziness" = 1) + foodtypes = MEAT | VEGETABLES | JUNKFOOD + +/obj/item/food/ready_donk/country_chicken + name = "\improper Ready-Donk Donkriginals: Country-Fried Chicken" + desc = "A TV dinner classic: \"crispy\" fried chicken in country gravy, mashed potatoes, and green beans." + icon_state = "ready_donk_chicken" + tastes = list("country-fried chicken" = 2, "laziness" = 1) + foodtypes = MEAT | DAIRY | VEGETABLES | JUNKFOOD + + warm_type = /obj/item/food/ready_donk/warm/country_chicken + +/obj/item/food/ready_donk/warm/country_chicken + name = "warm Ready-Donk Donkriginals: Country-Fried Chicken" + desc = "A TV dinner classic: \"crispy\" fried chicken in country gravy, mashed potatoes, and green beans. Get it while it's hot!" + icon_state = "ready_donk_chicken_warm" + tastes = list("country-fried chicken" = 2, "laziness" = 1) + foodtypes = MEAT | DAIRY | VEGETABLES | JUNKFOOD + // Rations /obj/item/food/rationpack name = "ration pack" diff --git a/code/game/objects/items/grenades/flashbang.dm b/code/game/objects/items/grenades/flashbang.dm index c83801d81fc..2300d2c6717 100644 --- a/code/game/objects/items/grenades/flashbang.dm +++ b/code/game/objects/items/grenades/flashbang.dm @@ -53,7 +53,7 @@ /obj/item/grenade/stingbang name = "stingbang" - icon_state = "timeg" + icon_state = "timeg_locked" inhand_icon_state = "flashbang" lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' @@ -121,7 +121,7 @@ /obj/item/grenade/primer name = "rotfrag grenade" desc = "A grenade that generates more shrapnel the more you rotate it in your hand after pulling the pin. This one releases shrapnel shards." - icon_state = "timeg" + icon_state = "timeg_locked" inhand_icon_state = "flashbang" lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' diff --git a/code/game/objects/items/holosign_creator.dm b/code/game/objects/items/holosign_creator.dm index 049ea8928fe..efe1d9e31f1 100644 --- a/code/game/objects/items/holosign_creator.dm +++ b/code/game/objects/items/holosign_creator.dm @@ -35,6 +35,11 @@ return . += span_notice("It is currently maintaining [signs.len]/[max_signs] projections.") +/obj/item/holosign_creator/check_allowed_items(atom/target, not_inside, target_self) + if(HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return FALSE + return ..() + /obj/item/holosign_creator/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!check_allowed_items(interacting_with, not_inside = TRUE)) return NONE diff --git a/code/game/objects/items/hot_potato.dm b/code/game/objects/items/hot_potato.dm index 20233db522b..8bede8813a2 100644 --- a/code/game/objects/items/hot_potato.dm +++ b/code/game/objects/items/hot_potato.dm @@ -18,7 +18,7 @@ var/stimulant = TRUE var/detonate_explosion = TRUE var/detonate_dev_range = 0 - var/detonate_heavy_range = 0 + var/detonate_heavy_range = 1 var/detonate_light_range = 2 var/detonate_flash_range = 5 var/detonate_fire_range = 5 @@ -51,7 +51,10 @@ /obj/item/hot_potato/proc/detonate() var/atom/location = loc location.visible_message(span_userdanger("[src] [detonate_explosion? "explodes" : "activates"]!"), span_userdanger("[src] activates! You've ran out of time!")) - if(detonate_explosion) + if(detonate_explosion && isliving(loc)) + var/mob/living/victim_mob = loc + if(victim_mob.is_holding(src)) + victim_mob.gib(DROP_ALL_REMAINS) explosion(src, detonate_dev_range, detonate_heavy_range, detonate_light_range, detonate_fire_range, detonate_flash_range) deactivate() if(!reusable) diff --git a/code/game/objects/items/implants/implantcase.dm b/code/game/objects/items/implants/implantcase.dm index ffef74de3c1..1a3ec50eba2 100644 --- a/code/game/objects/items/implants/implantcase.dm +++ b/code/game/objects/items/implants/implantcase.dm @@ -43,6 +43,7 @@ if((user.get_active_held_item() != used_item) || !user.can_perform_action(src)) return if(new_name) + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) name = "implant case - '[new_name]'" else name = "implant case" diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/inducer.dm index 0f66cd4b6d1..0e430589981 100644 --- a/code/game/objects/items/inducer.dm +++ b/code/game/objects/items/inducer.dm @@ -61,8 +61,9 @@ /obj/item/inducer/examine(mob/living/user) . = ..() - if(!QDELETED(powerdevice)) - . += span_notice("Its display shows: [display_energy(powerdevice.charge)].") + var/obj/item/stock_parts/power_store/our_cell = get_cell(src, user) + if(!QDELETED(our_cell)) + . += span_notice("Its display shows: [display_energy(our_cell.charge)].") if(opened) . += span_notice("The cell can be removed with an empty hand.") . += span_notice("Plasma sheets can be used to recharge the cell.") @@ -99,14 +100,15 @@ /obj/item/inducer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = NONE + if(user.combat_mode || !istype(tool) || tool.flags_1 & HOLOGRAM_1 || tool.item_flags & ABSTRACT) return ITEM_INTERACT_SKIP_TO_ATTACK - if(!opened) - balloon_alert(user, "open first!") - return ITEM_INTERACT_FAILURE - if(istype(tool, /obj/item/stock_parts/power_store)) + if(!opened) + balloon_alert(user, "open first!") + return ITEM_INTERACT_FAILURE + if(!QDELETED(powerdevice)) balloon_alert(user, "cell already installed!") return ITEM_INTERACT_FAILURE @@ -118,7 +120,7 @@ powerdevice = tool return ITEM_INTERACT_SUCCESS - if(istype(tool, /obj/item/stack/sheet/mineral/plasma) && !QDELETED(powerdevice)) + else if(istype(tool, /obj/item/stack/sheet/mineral/plasma) && !QDELETED(powerdevice)) if(!powerdevice.used_charge()) balloon_alert(user, "fully charged!") return ITEM_INTERACT_FAILURE @@ -131,6 +133,10 @@ /obj/item/inducer/interact_with_atom(atom/movable/interacting_with, mob/living/user, list/modifiers) . = NONE + + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return + if(user.combat_mode || !istype(interacting_with) || interacting_with.flags_1 & HOLOGRAM_1) return ITEM_INTERACT_SKIP_TO_ATTACK @@ -146,11 +152,13 @@ to_chat(user, span_warning("You don't have the dexterity to use [src]!")) return ITEM_INTERACT_FAILURE - if(QDELETED(powerdevice)) + var/obj/item/stock_parts/power_store/our_cell = get_cell(src, user) + + if(QDELETED(our_cell)) balloon_alert(user, "no cell installed!") return ITEM_INTERACT_FAILURE - if(!powerdevice.charge) + if(!our_cell.charge) balloon_alert(user, "no charge!") return ITEM_INTERACT_FAILURE @@ -173,13 +181,13 @@ break //transfer of charge - var/transferred = min(powerdevice.charge, target_cell.used_charge(), (target_cell.rating_base * target_cell.rating * power_transfer_multiplier)) + var/transferred = min(our_cell.charge, target_cell.used_charge(), target_cell.rating_base * target_cell.rating * power_transfer_multiplier) if(!transferred) break - powerdevice.use(target_cell.give(transferred)) + our_cell.use(target_cell.give(transferred)) //update all appearances - powerdevice.update_appearance() + our_cell.update_appearance() target_cell.update_appearance() interacting_with.update_appearance() diff --git a/code/game/objects/items/inspector.dm b/code/game/objects/items/inspector.dm index d4abe4c1d1a..7783dcff072 100644 --- a/code/game/objects/items/inspector.dm +++ b/code/game/objects/items/inspector.dm @@ -20,7 +20,6 @@ interaction_flags_click = NEED_DEXTERITY throw_range = 1 throw_speed = 1 - COOLDOWN_DECLARE(scanning_person) //Cooldown for scanning a carbon ///How long it takes to print on time each mode, ordered NORMAL, FAST, HONK var/list/time_list = list(5 SECONDS, 1 SECONDS, 0.1 SECONDS) ///Which print time mode we're on. @@ -108,17 +107,8 @@ balloon_alert(user, "check cell!") return ITEM_INTERACT_BLOCKING - if(iscarbon(interacting_with)) //Prevents insta scanning people - if(!COOLDOWN_FINISHED(src, scanning_person)) - return ITEM_INTERACT_BLOCKING - - visible_message(span_warning("[user] starts scanning [interacting_with] with [src]")) - to_chat(interacting_with, span_userdanger("[user] is trying to scan you for contraband!")) - balloon_alert_to_viewers("scanning...") - playsound(src, SFX_INDUSTRIAL_SCAN, 20, TRUE, -2, TRUE, FALSE) - COOLDOWN_START(src, scanning_person, 4 SECONDS) - if(!do_after(user, 4 SECONDS, interacting_with)) - return ITEM_INTERACT_BLOCKING + if(iscarbon(interacting_with)) // Prevents scanning people + return if(contraband_scan(interacting_with, user)) playsound(src, 'sound/machines/uplink/uplinkerror.ogg', 40) diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm index 16afd6713df..6689ec41a9a 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/game/objects/items/kitchen.dm @@ -12,6 +12,7 @@ icon = 'icons/obj/service/kitchen.dmi' lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi' + worn_icon_state = "kitchen_tool" /obj/item/kitchen/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm index b4697664e85..961e0fff88a 100644 --- a/code/game/objects/items/rcd/RCD.dm +++ b/code/game/objects/items/rcd/RCD.dm @@ -207,9 +207,6 @@ * * [mob][user]- the user building this structure */ /obj/item/construction/rcd/proc/rcd_create(atom/target, mob/user) - if(HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) - return NONE - var/list/rcd_results = target.rcd_vals(user, src) // does this atom allow for rcd actions? if(!rcd_results) // nope return NONE diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 95430048764..b49e0c215e3 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -12,7 +12,7 @@ var/require_model = FALSE var/list/model_type = null /// Bitflags listing model compatibility. Used in the exosuit fabricator for creating sub-categories. - var/list/model_flags = NONE + var/model_flags = NONE /// List of items to add with the module, if any var/list/items_to_add @@ -665,6 +665,9 @@ /obj/item/inducer/cyborg/screwdriver_act(mob/living/user, obj/item/tool) return NONE +/obj/item/inducer/cyborg/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_FAILURE + /obj/item/borg/upgrade/pinpointer name = "medical cyborg crew pinpointer" desc = "A crew pinpointer module for the medical cyborg. Permits remote access to the crew monitor." diff --git a/code/game/objects/items/signs.dm b/code/game/objects/items/signs.dm index 85a71dc0e8f..d0ed3425f97 100644 --- a/code/game/objects/items/signs.dm +++ b/code/game/objects/items/signs.dm @@ -25,6 +25,7 @@ return var/txt = tgui_input_text(user, "What would you like to write on the sign?", "Sign Label", max_length = 30) if(txt && user.can_perform_action(src)) + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) label = txt name = "[label] sign" desc = "It reads: [label]" diff --git a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm index 95c87cbb5da..330cd86ff1d 100644 --- a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm +++ b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm @@ -418,7 +418,7 @@ return owner.body_position == LYING_DOWN /datum/status_effect/golem/bananium/on_remove() - owner.remove_traits(owner, list(TRAIT_WADDLING, TRAIT_NO_SLIP_WATER), TRAIT_STATUS_EFFECT(id)) + owner.remove_traits(list(TRAIT_WADDLING, TRAIT_NO_SLIP_WATER), TRAIT_STATUS_EFFECT(id)) QDEL_NULL(slipperiness) return ..() diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 0d4393efea6..ef3ddb77c0e 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -41,6 +41,8 @@ GLOBAL_LIST_INIT(sandstone_recipes, list ( \ merge_type = /obj/item/stack/sheet/mineral/sandstone walltype = /turf/closed/wall/mineral/sandstone material_type = /datum/material/sandstone + drop_sound = SFX_STONE_DROP + pickup_sound = SFX_STONE_PICKUP /obj/item/stack/sheet/mineral/sandstone/get_main_recipes() . = ..() diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 18891ebdd93..999fbd5608d 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -135,14 +135,10 @@ return return TRUE -/obj/item/stack/grind(datum/reagents/target_holder, mob/user) +/obj/item/stack/grind_atom(datum/reagents/target_holder, mob/user) var/current_amount = get_amount() if(current_amount <= 0 || QDELETED(src)) //just to get rid of this 0 amount/deleted stack we return success return TRUE - if(on_grind() == -1) - return FALSE - if(isnull(target_holder)) - return TRUE if(reagents) reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) @@ -595,7 +591,7 @@ return FALSE if(is_cyborg) // No merging cyborg stacks into other stacks return FALSE - if(ismob(loc) && !inhand) // no merging with items that are on the mob + if(ismob(loc) && !inhand && !HAS_TRAIT(loc, TRAIT_MOB_MERGE_STACKS)) // no merging with items that are on the mob return FALSE if(istype(loc, /obj/machinery)) // no merging items in machines that aren't both in componentparts var/obj/machinery/machine = loc diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 2fc14e86a42..378c127b87c 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -336,6 +336,9 @@ /obj/item/stack/tile/carpet/fifty amount = 50 +/obj/item/stack/tile/iron/fifty + amount = 50 + /obj/item/stack/tile/carpet/black/fifty amount = 50 diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index 5f08747e60c..709476e8881 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -273,9 +273,9 @@ new /obj/item/gun_maintenance_supplies(src) new /obj/item/gun_maintenance_supplies(src) -//floorbot assembly +//repairbot assembly /obj/item/storage/toolbox/tool_act(mob/living/user, obj/item/tool, list/modifiers) - if(!istype(tool, /obj/item/stack/tile/iron)) + if(!istype(tool, /obj/item/assembly/prox_sensor)) return ..() var/static/list/allowed_toolbox = list( /obj/item/storage/toolbox/artistic, @@ -290,26 +290,22 @@ if(contents.len >= 1) balloon_alert(user, "not empty!") return ITEM_INTERACT_BLOCKING - if(tool.use(10)) - var/obj/item/bot_assembly/floorbot/B = new - B.toolbox = type - switch(B.toolbox) - if(/obj/item/storage/toolbox) - B.toolbox_color = "r" - if(/obj/item/storage/toolbox/emergency) - B.toolbox_color = "r" - if(/obj/item/storage/toolbox/electrical) - B.toolbox_color = "y" - if(/obj/item/storage/toolbox/artistic) - B.toolbox_color = "g" - if(/obj/item/storage/toolbox/syndicate) - B.toolbox_color = "s" - user.put_in_hands(B) - B.update_appearance() - B.balloon_alert(user, "tiles added") - qdel(src) - return ITEM_INTERACT_BLOCKING - balloon_alert(user, "needs 10 tiles!") + var/static/list/toolbox_colors = list( + /obj/item/storage/toolbox = "#445eb3", + /obj/item/storage/toolbox/emergency = "#445eb3", + /obj/item/storage/toolbox/electrical = "#b77931", + /obj/item/storage/toolbox/artistic = "#378752", + /obj/item/storage/toolbox/syndicate = "#3d3d3d", + ) + var/obj/item/bot_assembly/repairbot/repair = new + repair.toolbox = type + var/new_color = toolbox_colors[type] || "#445eb3" + repair.set_color(new_color) + user.put_in_hands(repair) + repair.update_appearance() + repair.balloon_alert(user, "sensor added!") + qdel(tool) + qdel(src) return ITEM_INTERACT_SUCCESS /obj/item/storage/toolbox/haunted diff --git a/code/game/objects/items/theft_tools.dm b/code/game/objects/items/theft_tools.dm index a2efbe2d4be..34176864492 100644 --- a/code/game/objects/items/theft_tools.dm +++ b/code/game/objects/items/theft_tools.dm @@ -158,10 +158,22 @@ pulseicon = "supermatter_sliver_pulse" layer = ABOVE_MOB_LAYER +/obj/item/nuke_core/supermatter_sliver/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_FISHING_ROD_CAST, PROC_REF(on_hook)) + +/obj/item/nuke_core/supermatter_sliver/proc/on_hook(obj/item/nuke_core/supermatter_sliver/source, obj/item/fishing_rod/rod, mob/user) + SIGNAL_HANDLER + + //hook gets dusted but the rod remains intact + attackby(rod.hook, user) + + return FISHING_ROD_CAST_HANDLED + /obj/item/nuke_core/supermatter_sliver/attack_tk(mob/user) // no TK dusting memes return -/obj/item/nuke_core/supermatter_sliver/can_be_pulled(user) // no drag memes +/obj/item/nuke_core/supermatter_sliver/can_be_pulled(user, force) // no drag memes return FALSE /obj/item/nuke_core/supermatter_sliver/attackby(obj/item/W, mob/living/user, params) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index fa3aadbae95..dc0375ac04b 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -64,7 +64,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/balloon_mallet/examine(mob/user) . = ..() if(HAS_TRAIT(user,TRAIT_BALLOON_SUTRA)) - . = "A sacred weapon of the higher castes from the clown planet, used to strike fear into the hearts of their foes. Wield it with care." + . += "A sacred weapon of the higher castes from the clown planet, used to strike fear into the hearts of their foes. Wield it with care." /obj/item/balloon_mallet/attack(mob/living/target, mob/living/user) playsound(loc, 'sound/mobs/non-humanoids/clown/hehe.ogg', 20) @@ -432,6 +432,42 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 pain_mult = 0 jostle_pain_mult = 0 +/obj/item/carpenter_hammer + name = "carpenter hammer" + icon = 'icons/obj/weapons/hammer.dmi' + icon_state = "carpenter_hammer" + inhand_icon_state = "carpenter_hammer" + lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi' + desc = "Uncanny looking hammer." + force = 20 + throwforce = 20 + throw_range = 4 + w_class = WEIGHT_CLASS_NORMAL + wound_bonus = 20 + demolition_mod = 1.25 + +/obj/item/carpenter_hammer/Initialize(mapload) + . = ..() + AddComponent(/datum/component/item_killsound, \ + allowed_mobs = list(/mob/living/carbon/human), \ + killsound = 'sound/items/weapons/hammer_death_scream.ogg', \ + replace_default_death_sound = TRUE, \ + ) + +/obj/item/carpenter_hammer/examine(mob/user) + . = ..() + . += "" + . += "Real World Tip:" + . += pick( + "Every building, from hospitals to homes, has a room that serves as the heart of the building \ + and carries blood and nutrients to its extremities. Try to find the heart of your home!", + "All the food you've tried is rotten. You've never eaten fresh food.", + "Viruses do not exist. Illness is simply your body punishing you for what you have done wrong.", + "Space stations must have at least 50 mammalian teeth embedded in the north walls for structural safety reasons.", + "Queen dragonfly sleeps and smiles.", + ) + /obj/item/switchblade name = "switchblade" icon = 'icons/obj/weapons/sword.dmi' diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 50d06bafef0..38aadbb2662 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -271,11 +271,16 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool, 0) return if(!item_chair || has_buckled_mobs()) return + if(flags_1 & HOLOGRAM_1) + to_chat(user, span_notice("You try to pick up \the [src], but it fades away!")) + qdel(src) + return + user.visible_message(span_notice("[user] grabs \the [src.name]."), span_notice("You grab \the [src.name].")) - var/obj/item/C = new item_chair(loc) - C.set_custom_materials(custom_materials) - TransferComponents(C) - user.put_in_hands(C) + var/obj/item/chair_item = new item_chair(loc) + chair_item.set_custom_materials(custom_materials) + TransferComponents(chair_item) + user.put_in_hands(chair_item) qdel(src) /obj/structure/chair/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) @@ -344,6 +349,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0) if(isgroundlessturf(T)) to_chat(user, span_warning("You need ground to plant this on!")) return + if(flags_1 & HOLOGRAM_1) + to_chat(user, span_notice("You try to place down \the [src], but it fades away!")) + qdel(src) + return + for(var/obj/A in T) if(istype(A, /obj/structure/chair)) to_chat(user, span_warning("There is already a chair here!")) @@ -353,10 +363,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0) return user.visible_message(span_notice("[user] rights \the [src.name]."), span_notice("You right \the [name].")) - var/obj/structure/chair/C = new origin_type(get_turf(loc)) - C.set_custom_materials(custom_materials) - TransferComponents(C) - C.setDir(user.dir) + var/obj/structure/chair/chair = new origin_type(get_turf(loc)) + chair.set_custom_materials(custom_materials) + TransferComponents(chair) + chair.setDir(user.dir) qdel(src) /obj/item/chair/proc/smash(mob/living/user) diff --git a/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm b/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm index f0fa9e27d78..fa49fe0a106 100644 --- a/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm +++ b/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm @@ -22,7 +22,7 @@ ///Accepted "ammo" type var/obj/item/ammo_type = /obj/item/ammo_casing/strilka310 ///Projectile from said gun. Doesnt automatically inherit said ammo's projectile in case you wanted to make a gun that shoots floor tiles or something. - var/obj/item/projectile_type = /obj/projectile/bullet/strilka310 + var/obj/projectile/projectile_type = /obj/projectile/bullet/strilka310 ///If the gun has anything in it. var/loaded_gun = TRUE ///If the gun is currently loaded with its maximum capacity. diff --git a/code/game/objects/structures/construction_console/construction_actions.dm b/code/game/objects/structures/construction_console/construction_actions.dm index 1a6b5deeeae..cce7d2ee516 100644 --- a/code/game/objects/structures/construction_console/construction_actions.dm +++ b/code/game/objects/structures/construction_console/construction_actions.dm @@ -5,7 +5,7 @@ /datum/action/innate/construction button_icon = 'icons/mob/actions/actions_construction.dmi' ///Console's eye mob - var/mob/camera/ai_eye/remote/base_construction/remote_eye + var/mob/eye/camera/remote/base_construction/remote_eye ///Console itself var/obj/machinery/computer/camera_advanced/base_construction/base_console ///Is this used to build only on the station z level? diff --git a/code/game/objects/structures/construction_console/construction_console.dm b/code/game/objects/structures/construction_console/construction_console.dm index f13dd1d78c6..69275d0e951 100644 --- a/code/game/objects/structures/construction_console/construction_console.dm +++ b/code/game/objects/structures/construction_console/construction_console.dm @@ -1,7 +1,7 @@ /** * Camera console used to control a base building drone * - * Using this console will put the user in control of a [base building drone][/mob/camera/ai_eye/remote/base_construction]. + * Using this console will put the user in control of a [base building drone][/mob/eye/camera/remote/base_construction]. * The drone will appear somewhere within the allowed_area var, or if no area is specified, at the location of the console.area * Upon interacting, the user will be granted a set of base building actions that will generally be carried out at the drone's location. * To create a new base builder system, this class should be the only thing that needs to be subtyped. @@ -61,8 +61,7 @@ var/turf/spawn_spot = find_spawn_spot() if (!spawn_spot) return FALSE - eyeobj = new /mob/camera/ai_eye/remote/base_construction(spawn_spot, src) - eyeobj.origin = src + eyeobj = new /mob/eye/camera/remote/base_construction(spawn_spot, src) return TRUE /obj/machinery/computer/camera_advanced/base_construction/attackby(obj/item/W, mob/user, params) @@ -95,7 +94,7 @@ * The mob is constrained to a given area defined by the base construction console. * */ -/mob/camera/ai_eye/remote/base_construction +/mob/eye/camera/remote/base_construction name = "construction holo-drone" //Allows any curious crew to watch the base after it leaves. (This is safe as the base cannot be modified once it leaves) move_on_shuttle = TRUE @@ -105,20 +104,20 @@ ///Reference to the camera console controlling this drone var/obj/machinery/computer/camera_advanced/base_construction/linked_console -/mob/camera/ai_eye/remote/base_construction/Initialize(mapload, obj/machinery/computer/camera_advanced/console_link) +/mob/eye/camera/remote/base_construction/Initialize(mapload, obj/machinery/computer/camera_advanced/console_link) linked_console = console_link if(!linked_console) stack_trace("A base consturuction drone was created with no linked console") return INITIALIZE_HINT_QDEL return ..() -/mob/camera/ai_eye/remote/base_construction/setLoc(turf/destination, force_update = FALSE) +/mob/eye/camera/remote/base_construction/setLoc(turf/destination, force_update = FALSE) var/area/curr_area = get_area(destination) //Only move if we're in the allowed area. If no allowed area is defined, then we're free to move wherever. if(!linked_console.allowed_area || istype(curr_area, linked_console.allowed_area)) return ..() -/mob/camera/ai_eye/remote/base_construction/relaymove(mob/living/user, direction) +/mob/eye/camera/remote/base_construction/relaymove(mob/living/user, direction) //This camera eye is visible, and as such needs to keep its dir updated dir = direction return ..() diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 7a3fcef368a..6046d356892 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -518,7 +518,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /obj/structure/closet/proc/insertion_allowed(atom/movable/AM) if(ismob(AM)) - if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets... + if(!isliving(AM)) //let's not put ghosts or eye mobs inside closets... return FALSE var/mob/living/L = AM if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs()) diff --git a/code/game/objects/structures/crates_lockers/closets/bodybag.dm b/code/game/objects/structures/crates_lockers/closets/bodybag.dm index 0bb1b564ece..c4aa7391dd6 100644 --- a/code/game/objects/structures/crates_lockers/closets/bodybag.dm +++ b/code/game/objects/structures/crates_lockers/closets/bodybag.dm @@ -65,6 +65,7 @@ ///Handles renaming of the bodybag's examine tag. /obj/structure/closet/body_bag/proc/handle_tag(new_name) + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) tag_name = new_name name = tag_name ? "[initial(name)] - [tag_name]" : initial(name) update_appearance() diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index 5fc9bf674c1..40037944dad 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -297,7 +297,7 @@ if(electronics.shell) door.AddComponent( \ /datum/component/shell, \ - unremovable_circuit_components = list(new /obj/item/circuit_component/airlock, new /obj/item/circuit_component/airlock_access_event), \ + unremovable_circuit_components = list(new /obj/item/circuit_component/airlock, new /obj/item/circuit_component/airlock_access_event, new /obj/item/circuit_component/remotecam/airlock), \ capacity = SHELL_CAPACITY_LARGE, \ shell_flags = SHELL_FLAG_ALLOW_FAILURE_ACTION|SHELL_FLAG_REQUIRE_ANCHOR \ ) diff --git a/code/game/objects/structures/fireplace.dm b/code/game/objects/structures/fireplace.dm index 4e568a56f48..7e94805045f 100644 --- a/code/game/objects/structures/fireplace.dm +++ b/code/game/objects/structures/fireplace.dm @@ -166,7 +166,7 @@ fuel_added = 0 update_appearance() adjust_light() - particles = new /particles/smoke/burning() + add_shared_particles(/particles/smoke/burning) switch(dir) if(SOUTH) @@ -185,7 +185,7 @@ update_appearance() adjust_light() desc = initial(desc) - QDEL_NULL(particles) + remove_shared_particles(/particles/smoke/burning) #undef LOG_BURN_TIMER #undef PAPER_BURN_TIMER diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index a6933d53468..d53935bda29 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -207,10 +207,10 @@ /** * This confirms that the user wants to start the wave defense event, and that they can start it. */ -/obj/structure/ore_vent/proc/pre_wave_defense(mob/user, spawn_drone = TRUE) +/obj/structure/ore_vent/proc/pre_wave_defense(mob/user, spawn_drone = TRUE, mech_scan = FALSE) if(tgui_alert(user, excavation_warning, "Begin defending ore vent?", list("Yes", "No")) != "Yes") return FALSE - if(!can_interact(user)) + if(!can_interact(user) && !mech_scan) return FALSE if(!COOLDOWN_FINISHED(src, wave_cooldown) || node) return FALSE @@ -224,7 +224,7 @@ addtimer(CALLBACK(node, TYPE_PROC_REF(/atom, update_appearance)), wave_timer * 0.25) addtimer(CALLBACK(node, TYPE_PROC_REF(/atom, update_appearance)), wave_timer * 0.5) addtimer(CALLBACK(node, TYPE_PROC_REF(/atom, update_appearance)), wave_timer * 0.75) - particles = new /particles/smoke/ash() + add_shared_particles(/particles/smoke/ash) for(var/i in 1 to 5) // Clears the surroundings of the ore vent before starting wave defense. for(var/turf/closed/mineral/rock in oview(i)) if(istype(rock, /turf/open/misc/asteroid) && prob(35)) // so it's too common @@ -272,7 +272,7 @@ SEND_SIGNAL(src, COMSIG_VENT_WAVE_CONCLUDED) COOLDOWN_RESET(src, wave_cooldown) - particles = null + remove_shared_particles(/particles/smoke/ash) if(force) initiate_wave_win() @@ -337,24 +337,16 @@ * Gives a readout of the ores available in the vent that gets added to the description, * then asks the user if they want to start wave defense if it's already been discovered. * @params user The user who tapped the vent. - * @params scan_only If TRUE, the vent will only scan, and not prompt to start wave defense. Used by the mech mineral scanner. + * @params mech_scan If TRUE, will bypass interaction checks to allow mechs to be able to begin the wave defense. */ -/obj/structure/ore_vent/proc/scan_and_confirm(mob/living/user, scan_only = FALSE) +/obj/structure/ore_vent/proc/scan_and_confirm(mob/living/user, mech_scan = FALSE) if(tapped) balloon_alert_to_viewers("vent tapped!") return if(!COOLDOWN_FINISHED(src, wave_cooldown) || node) //We're already defending the vent, so don't scan it again. - if(!scan_only) - balloon_alert_to_viewers("protect the node drone!") + balloon_alert_to_viewers("protect the node drone!") return if(!discovered) - if(scan_only) - discovered = TRUE - generate_description(user) - balloon_alert_to_viewers("vent scanned!") - AddComponent(/datum/component/gps, name) - return - if(DOING_INTERACTION_WITH_TARGET(user, src)) balloon_alert(user, "already scanning!") return @@ -374,10 +366,8 @@ user_id_card.registered_account.mining_points += (MINER_POINT_MULTIPLIER) user_id_card.registered_account.bank_card_talk("You've been awarded [MINER_POINT_MULTIPLIER] mining points for discovery of an ore vent.") return - if(scan_only) - return - if(!pre_wave_defense(user, spawn_drone_on_tap)) + if(!pre_wave_defense(user, spawn_drone_on_tap, mech_scan)) return start_wave_defense() diff --git a/code/game/objects/structures/maintenance.dm b/code/game/objects/structures/maintenance.dm index d3eb552c46b..bf96397507a 100644 --- a/code/game/objects/structures/maintenance.dm +++ b/code/game/objects/structures/maintenance.dm @@ -80,12 +80,11 @@ at the cost of risking a vicious bite.**/ return if(critter_infested && prob(50) && iscarbon(user)) var/mob/living/carbon/bite_victim = user - var/obj/item/bodypart/affecting = bite_victim.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm") - to_chat(user, span_danger("You feel a sharp pain as an unseen creature sinks its [pick("fangs", "beak", "proboscis")] into your arm!")) - if(affecting?.receive_damage(30)) - bite_victim.update_damage_overlays() - playsound(src,'sound/items/weapons/bite.ogg', 70, TRUE) - return + var/obj/item/bodypart/affecting = bite_victim.get_active_hand() + to_chat(user, span_danger("You feel a sharp pain as an unseen creature sinks its [pick("fangs", "beak", "proboscis")] into your [affecting.plaintext_zone]!")) + bite_victim.apply_damage(30, BRUTE, affecting) + playsound(src,'sound/items/weapons/bite.ogg', 70, TRUE) + return to_chat(user, span_warning("You find nothing of value...")) /obj/structure/moisture_trap/attackby(obj/item/I, mob/user, params) diff --git a/code/game/objects/structures/mystery_box.dm b/code/game/objects/structures/mystery_box.dm index 0a0c9ca0a10..3a1844aa222 100644 --- a/code/game/objects/structures/mystery_box.dm +++ b/code/game/objects/structures/mystery_box.dm @@ -272,10 +272,10 @@ GLOBAL_LIST_INIT(mystery_fishing, list( /obj/structure/mystery_box/wands/generate_valid_types() valid_types = GLOB.mystery_magic -///One of a kind, rarely found by fishing in the ocean. +///A fishing and pirate-themed mystery box, rarely found by fishing in the ocean, then another cannot be caught for the next 30 minutes. /obj/structure/mystery_box/fishing name = "treasure chest" - desc = "A pirate-y chest that seems equally magial and mysterious, capable of granting the user different pieces of gear." + desc = "A piratey coffer equally magical and mysterious, capable of granting different pieces of gear to whoever opens it." icon_state = "treasure" uses_left = 18 max_integrity = 100 diff --git a/code/game/objects/structures/ore_containers.dm b/code/game/objects/structures/ore_containers.dm index 6bc6f680116..75c7a03cfcf 100644 --- a/code/game/objects/structures/ore_containers.dm +++ b/code/game/objects/structures/ore_containers.dm @@ -23,25 +23,16 @@ ui.open() /obj/structure/ore_container/ui_data(mob/user) - var/list/data = list() - data["ores"] = list() + var/list/ores = list() for(var/obj/item/stack/ore/ore_item in contents) - data["ores"] += list(list( + ores += list(list( "id" = REF(ore_item), "name" = ore_item.name, "amount" = ore_item.amount, + "icon" = ore_item::icon, + "icon_state" = ore_item::icon_state, )) - return data - -/obj/structure/ore_container/ui_static_data(mob/user) - var/list/data = list() - data["ore_images"] = list() - for(var/obj/item/stack/ore_item as anything in subtypesof(/obj/item/stack/ore)) - data["ore_images"] += list(list( - "name" = initial(ore_item.name), - "icon" = icon2base64(getFlatIcon(image(icon = initial(ore_item.icon), icon_state = initial(ore_item.icon_state)), no_anim=TRUE)) - )) - return data + return list("ores" = ores) /obj/structure/ore_container/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm index 6cae493e57b..65a23c9a88b 100644 --- a/code/game/objects/structures/shower.dm +++ b/code/game/objects/structures/shower.dm @@ -250,7 +250,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) if(locate_new_shower && isturf(exiter.loc)) return var/mob/living/take_his_status_effect = exiter - take_his_status_effect.remove_status_effect(/datum/status_effect/shower_regen) + take_his_status_effect.remove_status_effect(/datum/status_effect/washing_regen) /obj/machinery/shower/proc/wash_atom(atom/target) target.wash(CLEAN_RAD | CLEAN_WASH) @@ -259,7 +259,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) return var/mob/living/living_target = target check_heat(living_target) - living_target.apply_status_effect(/datum/status_effect/shower_regen) + living_target.apply_status_effect(/datum/status_effect/washing_regen) if(!HAS_TRAIT(target, TRAIT_WATER_HATER) || HAS_TRAIT(target, TRAIT_WATER_ADAPTATION)) living_target.add_mood_event("shower", /datum/mood_event/nice_shower) else @@ -294,7 +294,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) if(isopenturf(loc)) var/turf/open/tile = loc tile.MakeSlippery(TURF_WET_WATER, min_wet_time = 5 SECONDS, wet_time_to_add = 1 SECONDS) - + for(var/mob/living/showerer in loc) + showerer.remove_status_effect(/datum/status_effect/washing_regen) return TRUE /obj/machinery/shower/process(seconds_per_tick) @@ -336,18 +337,15 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) if(has_water_reclaimer) new /obj/item/stock_parts/water_recycler(drop_location()) -/obj/machinery/shower/proc/check_heat(mob/living/L) - var/mob/living/carbon/C = L +/obj/machinery/shower/proc/check_heat(mob/living/living) if(current_temperature == SHOWER_FREEZING) - if(iscarbon(L)) - C.adjust_bodytemperature(-80, 80) - to_chat(L, span_warning("[src] is freezing!")) + living.adjust_bodytemperature(-80, 80) + to_chat(living, span_warning("[src] is freezing!")) else if(current_temperature == SHOWER_BOILING) - if(iscarbon(L)) - C.adjust_bodytemperature(35, 0, 500) - L.adjustFireLoss(5) - to_chat(L, span_danger("[src] is searing!")) + living.adjust_bodytemperature(35, 0, 500) + living.adjustFireLoss(5) + to_chat(living, span_danger("[src] is searing!")) /obj/structure/showerframe diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index db4981aeac7..6a0bc3123d1 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -299,5 +299,5 @@ proteon.add_filter("sentient_proteon", 3, list("type" = "outline", "color" = COLOR_CULT_RED, "size" = 2, "alpha" = 40)) /obj/structure/spawner/sentient/proteon_spawner/handle_deconstruct(disassembled) - playsound('sound/effects/hallucinations/veryfar_noise.ogg', 125) + playsound(src, 'sound/effects/hallucinations/veryfar_noise.ogg', 75) visible_message(span_cult_bold("[src] completely falls apart, the screams of the damned reaching a feverous pitch before slowly fading away into nothing.")) diff --git a/code/game/sound.dm b/code/game/sound.dm index 306a32de7e6..f460847bbcd 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -753,4 +753,14 @@ 'sound/mobs/humanoids/human/sigh/female_sigh2.ogg', 'sound/mobs/humanoids/human/sigh/female_sigh3.ogg', ) + if(SFX_WRITING_PEN) + soundin = pick( + 'sound/effects/writing_pen/writing_pen1.ogg', + 'sound/effects/writing_pen/writing_pen2.ogg', + 'sound/effects/writing_pen/writing_pen3.ogg', + 'sound/effects/writing_pen/writing_pen4.ogg', + 'sound/effects/writing_pen/writing_pen5.ogg', + 'sound/effects/writing_pen/writing_pen6.ogg', + 'sound/effects/writing_pen/writing_pen7.ogg', + ) return soundin diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index 3a6546f6eaa..967c13f3372 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -25,7 +25,7 @@ transform = MAP_SWITCH(TRANSLATE_MATRIX(-4, -4), matrix()) temperature = TCMB - var/turf/open/floor/plating/turf_type = /turf/open/misc/asteroid/airless + var/turf/turf_type = /turf/open/misc/asteroid/airless /// The path of the ore stack we spawn when we're mined. var/obj/item/stack/ore/mineralType = null /// If we spawn a boulder like on the gulag, we use this in lou of mineralType diff --git a/code/game/turfs/open/ashplanet.dm b/code/game/turfs/open/ashplanet.dm index 31369a2e5ce..e6703c04eb5 100644 --- a/code/game/turfs/open/ashplanet.dm +++ b/code/game/turfs/open/ashplanet.dm @@ -69,3 +69,6 @@ /turf/open/misc/ashplanet/wateryrock/Initialize(mapload) icon_state = "[icon_state][rand(1, 9)]" . = ..() + +/turf/open/misc/ashplanet/wateryrock/lavaland_atmos + initial_gas_mix = LAVALAND_DEFAULT_ATMOS diff --git a/code/game/turfs/open/ice.dm b/code/game/turfs/open/ice.dm index 39abe6ebfee..7d0a37ff847 100644 --- a/code/game/turfs/open/ice.dm +++ b/code/game/turfs/open/ice.dm @@ -60,13 +60,16 @@ if(!do_after(user, 5 SECONDS, src)) return FALSE balloon_alert(user, "dug hole") + spawn_hole() + return TRUE + +/turf/open/misc/ice/proc/spawn_hole() AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/datum/fish_source/ice_fishing]) ADD_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT) add_overlay(mutable_appearance('icons/turf/overlays.dmi', "ice_hole")) can_make_hole = FALSE RemoveElement(/datum/element/contextual_screentip_tools, tool_screentips) flags_1 &= ~HAS_CONTEXTUAL_SCREENTIPS_1 - return TRUE /turf/open/misc/ice/smooth icon_state = "ice_turf-255" @@ -82,13 +85,18 @@ /turf/open/misc/ice/icemoon/no_planet_atmos planetary_atmos = FALSE - can_make_hole = FALSE + +///Ice turf with a fishing spot already dug +/turf/open/misc/ice/icemoon/no_planet_atmos/holed + +/turf/open/misc/ice/icemoon/no_planet_atmos/holed/Initialize(mapload) + . = ..() + spawn_hole() /turf/open/misc/ice/temperate baseturfs = /turf/open/misc/ice/temperate desc = "Somehow, it is not melting under these conditions. Must be some very thick ice. Just as slippery too." initial_gas_mix = COLD_ATMOS //it works with /turf/open/misc/asteroid/snow/temperatre - can_make_hole = FALSE //For when you want real, genuine ice in your kitchen's cold room. /turf/open/misc/ice/coldroom diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index fa0b0c4b128..06fb2d88bde 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -22,6 +22,7 @@ barefootstep = FOOTSTEP_LAVA clawfootstep = FOOTSTEP_LAVA heavyfootstep = FOOTSTEP_LAVA + rust_resistance = RUST_RESISTANCE_ABSOLUTE /// How much fire damage we deal to living mobs stepping on us var/lava_damage = 20 /// How many firestacks we add to living mobs stepping on us @@ -42,7 +43,8 @@ var/fish_source_type = /datum/fish_source/lavaland /// The color we use for our immersion overlay var/immerse_overlay_color = "#a15e1b" - rust_resistance = RUST_RESISTANCE_ABSOLUTE + /// Whether the immerse element has been added yet or not + var/immerse_added = FALSE /turf/open/lava/Initialize(mapload) . = ..() @@ -53,14 +55,38 @@ refresh_light() if(!smoothing_flags) update_appearance() - AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color) + RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_atom_inited)) /turf/open/lava/Destroy() + UnregisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON) for(var/mob/living/leaving_mob in contents) leaving_mob.RemoveElement(/datum/element/perma_fire_overlay) REMOVE_TRAIT(leaving_mob, TRAIT_NO_EXTINGUISH, TURF_TRAIT) return ..() +///We lazily add the immerse element when something is spawned or crosses this turf and not before. +/turf/open/lava/proc/on_atom_inited(datum/source, atom/movable/movable) + SIGNAL_HANDLER + if(burn_stuff(movable)) + START_PROCESSING(SSobj, src) + if(immerse_added || is_type_in_typecache(movable, GLOB.immerse_ignored_movable)) + return + AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color) + immerse_added = TRUE + +/** + * turf/Initialize() calls Entered on its contents too, however + * we need to wait for movables that still need to be initialized + * before we add the immerse element. + */ +/turf/open/lava/Entered(atom/movable/arrived) + . = ..() + if(!immerse_added && !is_type_in_typecache(arrived, GLOB.immerse_ignored_movable)) + AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color) + immerse_added = TRUE + if(burn_stuff(arrived)) + START_PROCESSING(SSobj, src) + /turf/open/lava/update_overlays() . = ..() . += emissive_appearance(mask_icon, mask_state, src) @@ -139,14 +165,6 @@ /turf/open/lava/MakeDry(wet_setting = TURF_WET_WATER) return -/turf/open/lava/airless - initial_gas_mix = AIRLESS_ATMOS - -/turf/open/lava/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - if(burn_stuff(arrived)) - START_PROCESSING(SSobj, src) - /turf/open/lava/Exited(atom/movable/gone, direction) . = ..() if(isliving(gone) && !islava(gone.loc)) @@ -322,6 +340,9 @@ /turf/open/lava/can_cross_safely(atom/movable/crossing) return HAS_TRAIT(src, TRAIT_LAVA_STOPPED) || HAS_TRAIT(crossing, immunity_trait ) || HAS_TRAIT(crossing, TRAIT_MOVE_FLYING) +/turf/open/lava/airless + initial_gas_mix = AIRLESS_ATMOS + /turf/open/lava/smooth name = "lava" baseturfs = /turf/open/lava/smooth diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm index 1af42be3071..9b9c739f397 100644 --- a/code/game/turfs/open/openspace.dm +++ b/code/game/turfs/open/openspace.dm @@ -165,7 +165,7 @@ place_on_top(new_floor_path, flags = flags) /turf/open/openspace/can_cross_safely(atom/movable/crossing) - return HAS_TRAIT(crossing, TRAIT_MOVE_FLYING) + return HAS_TRAIT(crossing, TRAIT_MOVE_FLYING) || !crossing.can_z_move(DOWN, src, z_move_flags = ZMOVE_FALL_FLAGS) /turf/open/openspace/icemoon name = "ice chasm" diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index 835d2908918..b70d7801635 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -4,7 +4,7 @@ desc = "Shallow water." icon = 'icons/turf/floors.dmi' icon_state = "riverwater_motion" - baseturfs = /turf/open/chasm/lavaland + baseturfs = /turf/open/water planetary_atmos = TRUE slowdown = 1 bullet_sizzle = TRUE @@ -14,26 +14,61 @@ barefootstep = FOOTSTEP_WATER clawfootstep = FOOTSTEP_WATER heavyfootstep = FOOTSTEP_WATER + underfloor_accessibility = UNDERFLOOR_INTERACTABLE /** * Used as the color arg/var for the immerse element. It should be kept more or less in line with * the hue of the turf, as semi-transparent vis overlays can opacify the semi-transparent bits of an icon, * and we're kinda trying to offset that issue. */ var/immerse_overlay_color = "#5AAA88" + ///The transparency of the immerse element's overlay + var/immerse_overlay_alpha = 180 /// Fishing element for this specific water tile var/datum/fish_source/fishing_datum = /datum/fish_source/river + /// Whether the immerse element has been added yet or not + var/immerse_added = FALSE + /turf/open/water/Initialize(mapload) . = ..() - AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color) + RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_atom_inited)) AddElement(/datum/element/watery_tile) if(!isnull(fishing_datum)) AddElement(/datum/element/lazy_fishing_spot, fishing_datum) ADD_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT) +///We lazily add the immerse element when something is spawned or crosses this turf and not before. +/turf/open/water/proc/on_atom_inited(datum/source, atom/movable/movable) + SIGNAL_HANDLER + UnregisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON) + if(immerse_added || is_type_in_typecache(movable, GLOB.immerse_ignored_movable)) + return + AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color, alpha = immerse_overlay_alpha) + immerse_added = TRUE + +/turf/open/water/Destroy() + UnregisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON) + return ..() + +/** + * turf/Initialize() calls Entered on its contents too, however + * we need to wait for movables that still need to be initialized + * before we add the immerse element. + */ +/turf/open/water/Entered(atom/movable/arrived) + . = ..() + if(immerse_added || is_type_in_typecache(arrived, GLOB.immerse_ignored_movable)) + return + AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color, alpha = immerse_overlay_alpha) + immerse_added = TRUE + /turf/open/water/jungle +/turf/open/water/no_planet_atmos + baseturfs = /turf/open/water/no_planet_atmos + planetary_atmos = FALSE + /turf/open/water/beach planetary_atmos = FALSE gender = PLURAL @@ -51,4 +86,112 @@ /turf/open/water/lavaland_atmos initial_gas_mix = LAVALAND_DEFAULT_ATMOS - planetary_atmos = TRUE + +/turf/open/water/beach/tizira + desc = "Shallow water. It somehow reminds you of lizardfolk." + icon_state = "tizira_water" + base_icon_state = "tizira_water" + baseturfs = /turf/open/water/beach/tizira + +/** + * A special subtype of water with steam particles and a status effect similar to showers, that's however only applied if + * the living mob inside the turf is actually immersed in it (eg. not flying, not floating). + */ +/turf/open/water/hot_spring + name = "hot spring" + icon_state = "pool_1" + desc = "Water kept warm through some unknown heat source, possibly a geothermal heat source far underground. \ + Whatever it is, it feels pretty damn nice to swim, and you can even catch a glimpse of \ + the odd fish darting through the water." + baseturfs = /turf/open/water/hot_spring + planetary_atmos = FALSE + immerse_overlay_color = "#A0E2DE" + immerse_overlay_alpha = 190 + fishing_datum = /datum/fish_source/hot_spring + /// Holder for the steam particles + var/obj/effect/abstract/particle_holder/cached/particle_effect + +/turf/open/water/hot_spring/Initialize(mapload) + . = ..() + icon_state = "pool_[rand(1, 4)]" + particle_effect = new(src, /particles/hotspring_steam, 4) + //render the steam over mobs and objects on the game plane + particle_effect.vis_flags &= ~VIS_INHERIT_PLANE + //And be unaffected by ambient occlusions, which would render the steam grey + particle_effect.plane = MUTATE_PLANE(MASSIVE_OBJ_PLANE, src) + add_filter("hot_spring_waves", 1, wave_filter(y = 1, size = 1, offset = 0, flags = WAVE_BOUNDED)) + var/filter = get_filter("hot_spring_waves") + animate(filter, offset = 1, time = 3 SECONDS, loop = -1, easing = SINE_EASING|EASE_IN|EASE_OUT) + animate(offset = -1, time = 3 SECONDS, easing = SINE_EASING|EASE_IN|EASE_OUT) + + +/turf/open/water/hot_spring/Destroy() + QDEL_NULL(particle_effect) + remove_filter("hot_spring_waves") + for(var/atom/movable/movable as anything in contents) + exit_hot_spring(movable) + return ..() + +/turf/open/water/hot_spring/Entered(atom/movable/arrived, atom/old_loc) + . = ..() + if(!(flags_1 & INITIALIZED_1)) + return + enter_hot_spring(arrived) + +/turf/open/water/hot_spring/proc/enter_initialized_movable(datum/source, atom/movable/movable) + SIGNAL_HANDLER + if(!immerse_added && !is_type_in_typecache(movable, GLOB.immerse_ignored_movable)) + AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color, alpha = immerse_overlay_alpha) + immerse_added = TRUE + enter_hot_spring(movable) + +///Registers the signals from the immerse element and calls dip_in if the movable has the required trait. +/turf/open/water/hot_spring/proc/enter_hot_spring(atom/movable/movable) + RegisterSignal(movable, SIGNAL_ADDTRAIT(TRAIT_IMMERSED), PROC_REF(dip_in)) + if(isliving(movable)) //so far, exiting a hot spring only has effects on living mobs. + RegisterSignal(movable, SIGNAL_REMOVETRAIT(TRAIT_IMMERSED), PROC_REF(dip_out)) + + if(HAS_TRAIT(movable, TRAIT_IMMERSED)) + dip_in(movable) + +///Handles washing the movable and adding a status effect plus mood event to living mobs. +/turf/open/water/hot_spring/proc/dip_in(atom/movable/movable) + SIGNAL_HANDLER + movable.wash(CLEAN_RAD | CLEAN_WASH) + if(!isliving(movable)) + return + + var/mob/living/living = movable + if(living.has_status_effect(/datum/status_effect/washing_regen/hot_spring)) + return + living.apply_status_effect(/datum/status_effect/washing_regen/hot_spring) + if(!HAS_TRAIT(living, TRAIT_WATER_HATER) || HAS_TRAIT(living, TRAIT_WATER_ADAPTATION)) + living.add_mood_event("hot_spring", /datum/mood_event/hot_spring) + else + living.add_mood_event("hot_spring", /datum/mood_event/hot_spring_hater) + +/turf/open/water/hot_spring/Exited(atom/movable/gone, atom/new_loc) + . = ..() + exit_hot_spring(gone) + +//Unregister the signals and remove the status effect from mobs unless they're moving to another hot spring turf. +/turf/open/water/hot_spring/proc/exit_hot_spring(atom/movable/movable) + UnregisterSignal(movable, list(SIGNAL_ADDTRAIT(TRAIT_IMMERSED), SIGNAL_REMOVETRAIT(TRAIT_IMMERSED))) + if(!isliving(movable)) + return + var/mob/living/living = movable + if(!living.has_status_effect(/datum/status_effect/washing_regen/hot_spring) || istype(living.loc, /turf/open/water/hot_spring)) + return + dip_out(living) + +///Handles removing the status effect from mobs. +/turf/open/water/hot_spring/proc/dip_out(mob/living/living) + SIGNAL_HANDLER + living.remove_status_effect(/datum/status_effect/washing_regen/hot_spring) + if(!HAS_TRAIT(living, TRAIT_WATER_HATER) || HAS_TRAIT(living, TRAIT_WATER_ADAPTATION)) + living.add_mood_event("hot_spring", /datum/mood_event/hot_spring_left) + else + living.add_mood_event("hot_spring", /datum/mood_event/hot_spring_hater_left) + +/turf/open/water/hot_spring/lavaland_atmos + initial_gas_mix = LAVALAND_DEFAULT_ATMOS diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 8b30634ac9f..fdbc1e04b8e 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -270,7 +270,7 @@ GLOBAL_LIST_EMPTY(station_turfs) * * type_list - are we checking for types of atoms to ignore and not physical atoms */ /turf/proc/is_blocked_turf(exclude_mobs = FALSE, source_atom = null, list/ignore_atoms, type_list = FALSE) - if((!isnull(source_atom) && !CanPass(source_atom, get_dir(src, source_atom))) || density) + if(density) return TRUE for(var/atom/movable/movable_content as anything in contents) diff --git a/code/game/world.dm b/code/game/world.dm index a17143da738..4239469b18d 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -8,6 +8,13 @@ GLOBAL_VAR(restart_counter) GLOBAL_VAR(tracy_log) +GLOBAL_PROTECT(tracy_log) +GLOBAL_VAR(tracy_initialized) +GLOBAL_PROTECT(tracy_initialized) +GLOBAL_VAR(tracy_init_error) +GLOBAL_PROTECT(tracy_init_error) +GLOBAL_VAR(tracy_init_reason) +GLOBAL_PROTECT(tracy_init_reason) /** * WORLD INITIALIZATION @@ -66,15 +73,30 @@ GLOBAL_VAR(tracy_log) /world/proc/Genesis(tracy_initialized = FALSE) RETURN_TYPE(/datum/controller/master) + if(!tracy_initialized) + GLOB.tracy_initialized = FALSE +#ifndef OPENDREAM + if(!tracy_initialized) #ifdef USE_BYOND_TRACY #warn USE_BYOND_TRACY is enabled - if(!tracy_initialized) + var/should_init_tracy = TRUE + GLOB.tracy_init_reason = "USE_BYOND_TRACY defined" #else - if(!tracy_initialized && (USE_TRACY_PARAMETER in params)) + var/should_init_tracy = FALSE + if(USE_TRACY_PARAMETER in params) + should_init_tracy = TRUE + GLOB.tracy_init_reason = "world.params" + if(fexists(TRACY_ENABLE_PATH)) + GLOB.tracy_init_reason ||= "enabled for round" + SEND_TEXT(world.log, "[TRACY_ENABLE_PATH] exists, initializing byond-tracy!") + should_init_tracy = TRUE + fdel(TRACY_ENABLE_PATH) +#endif + if(should_init_tracy) + init_byond_tracy() + Genesis(tracy_initialized = TRUE) + return #endif - GLOB.tracy_log = init_byond_tracy() - Genesis(tracy_initialized = TRUE) - return Profile(PROFILE_RESTART) Profile(PROFILE_RESTART, type = "sendmaps") @@ -333,6 +355,7 @@ GLOBAL_VAR(tracy_log) if(do_hard_reboot) log_world("World hard rebooted at [time_stamp()]") shutdown_logging() // See comment below. + shutdown_byond_tracy() auxcleanup() TgsEndProcess() return ..() @@ -340,6 +363,7 @@ GLOBAL_VAR(tracy_log) log_world("World rebooted at [time_stamp()]") shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss. + shutdown_byond_tracy() auxcleanup() TgsReboot() // TGS can decide to kill us right here, so it's important to do it last @@ -353,6 +377,7 @@ GLOBAL_VAR(tracy_log) call_ext(debug_server, "auxtools_shutdown")() /world/Del() + shutdown_byond_tracy() auxcleanup() . = ..() @@ -480,21 +505,31 @@ GLOBAL_VAR(tracy_log) DREAMLUAU_SET_EXECUTION_LIMIT_MILLIS(tick_lag * 100) /world/proc/init_byond_tracy() - var/library - - switch (system_type) - if (MS_WINDOWS) - library = "prof.dll" - if (UNIX) - library = "libprof.so" - else - CRASH("Unsupported platform: [system_type]") + if(!fexists(TRACY_DLL_PATH)) + SEND_TEXT(world.log, "Error initializing byond-tracy: [TRACY_DLL_PATH] not found!") + CRASH("Error initializing byond-tracy: [TRACY_DLL_PATH] not found!") - var/init_result = call_ext(library, "init")("block") + var/init_result = call_ext(TRACY_DLL_PATH, "init")("block") if(length(init_result) != 0 && init_result[1] == ".") // if first character is ., then it returned the output filename - return init_result + SEND_TEXT(world.log, "byond-tracy initialized (logfile: [init_result])") + GLOB.tracy_initialized = TRUE + return GLOB.tracy_log = init_result + else if(init_result == "already initialized") // not gonna question it. + GLOB.tracy_initialized = TRUE + SEND_TEXT(world.log, "byond-tracy already initialized ([GLOB.tracy_log ? "logfile: [GLOB.tracy_log]" : "no logfile"])") else if(init_result != "0") + GLOB.tracy_init_error = init_result + SEND_TEXT(world.log, "Error initializing byond-tracy: [init_result]") CRASH("Error initializing byond-tracy: [init_result]") + else + GLOB.tracy_initialized = TRUE + SEND_TEXT(world.log, "byond-tracy initialized (no logfile)") + +/world/proc/shutdown_byond_tracy() + if(GLOB.tracy_initialized) + SEND_TEXT(world.log, "Shutting down byond-tracy") + GLOB.tracy_initialized = FALSE + call_ext(TRACY_DLL_PATH, "destroy")() /world/proc/init_debugger() var/dll = GetConfig("env", "AUXTOOLS_DEBUG_DLL") diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 91e6287a3fb..ae22eb74823 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -214,7 +214,7 @@ if (ban["fromdb"]) if(SSdbcore.Connect()) - INVOKE_ASYNC(SSdbcore, /datum/controller/subsystem/dbcore/proc.QuerySelect, list( + INVOKE_ASYNC(SSdbcore, TYPE_PROC_REF(/datum/controller/subsystem/dbcore, QuerySelect), list( SSdbcore.NewQuery( "INSERT INTO [format_table_name("stickyban_matched_ckey")] (matched_ckey, stickyban) VALUES (:ckey, :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()", list("ckey" = ckey, "bannedckey" = bannedckey) diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 160a187d61e..bc4247f8c41 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -1,6 +1,8 @@ /atom/proc/investigate_log(message, subject) - if(!message || !subject) + if(!message) return + if(!subject) + CRASH("No subject provided for investigate_log") var/F = file("[GLOB.log_directory]/[subject].html") var/source = "[src]" diff --git a/code/modules/admin/smites/imaginary_friend_special.dm b/code/modules/admin/smites/imaginary_friend_special.dm index 37425faf3b1..b9621f51ba2 100644 --- a/code/modules/admin/smites/imaginary_friend_special.dm +++ b/code/modules/admin/smites/imaginary_friend_special.dm @@ -138,8 +138,8 @@ if(isliving(client_mob)) client_mob.ghostize() - var/mob/camera/imaginary_friend/friend_mob = client_mob.change_mob_type( - new_type = /mob/camera/imaginary_friend, + var/mob/eye/imaginary_friend/friend_mob = client_mob.change_mob_type( + new_type = /mob/eye/imaginary_friend, location = get_turf(client_mob), delete_old_mob = TRUE, ) diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm index 4da97d2c447..1305e5a660d 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -117,9 +117,6 @@ /proc/_text2num(T) return text2num(T) -/proc/_trimtext(Text) - return trimtext(Text) - /proc/_ohearers(Dist, Center = usr) return ohearers(Dist, Center) diff --git a/code/modules/admin/verbs/admin_newscaster.dm b/code/modules/admin/verbs/admin_newscaster.dm index b1be5560d69..7cac42e0b99 100644 --- a/code/modules/admin/verbs/admin_newscaster.dm +++ b/code/modules/admin/verbs/admin_newscaster.dm @@ -128,7 +128,7 @@ ADMIN_VERB(access_news_network, R_ADMIN, "Access Newscaster Network", "Allows yo data["channelLocked"] = current_channel.locked data["channelCensored"] = current_channel.censored - //We send all the information about all channels and all messages in existance. + //We send all the information about all channels and all messages in existence. data["channels"] = channel_list data["messages"] = message_list data["wanted"] = wanted_info diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 1f3f8a073bf..57cde88eb8d 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -514,7 +514,7 @@ ADMIN_VERB(cmd_admin_pm_panel, R_ADMIN, "Admin PM", "Show a list of clients to P return TRUE -/// Notifies all admins about the existance of an admin pm, then logs the pm +/// Notifies all admins about the existence of an admin pm, then logs the pm /// message_target here can be either [EXTERNAL_PM_USER], indicating that this message is intended for some external chat channel /// or a /client, in which case we send in the standard form /// log_message is the raw message to send, it will be filtered and treated to ensure we do not break any text handling diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index b6c5e10ca1d..13f1995c9ba 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -852,3 +852,42 @@ ADMIN_VERB(check_missing_sprites, R_DEBUG, "Debug Worn Item Sprites", "We're can actual_file_name = 'icons/mob/clothing/belt_mirror.dmi' if(!(sprite.icon_state in icon_states(actual_file_name))) to_chat(user, span_warning("ERROR sprites for [sprite.type]. Suit Storage slot."), confidential = TRUE) + +#ifndef OPENDREAM +ADMIN_VERB(start_tracy, R_DEBUG, "Run Tracy Now", "Start running the byond-tracy profiler immediately", ADMIN_CATEGORY_DEBUG) + if(GLOB.tracy_initialized) + to_chat(user, span_warning("byond-tracy is already running!"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + return + else if(GLOB.tracy_init_error) + to_chat(user, span_danger("byond-tracy failed to initialize during an earlier attempt: [GLOB.tracy_init_error]"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + return + message_admins(span_adminnotice("[key_name_admin(user)] is trying to start the byond-tracy profiler.")) + log_admin("[key_name(user)] is trying to start the byond-tracy profiler.") + GLOB.tracy_initialized = FALSE + GLOB.tracy_init_reason = "[user.ckey]" + world.init_byond_tracy() + if(GLOB.tracy_init_error) + to_chat(user, span_danger("byond-tracy failed to initialize: [GLOB.tracy_init_error]"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + message_admins(span_adminnotice("[key_name_admin(user)] tried to start the byond-tracy profiler, but it failed to initialize ([GLOB.tracy_init_error])")) + log_admin("[key_name(user)] tried to start the byond-tracy profiler, but it failed to initialize ([GLOB.tracy_init_error])") + return + to_chat(user, span_notice("byond-tracy successfully started!"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + message_admins(span_adminnotice("[key_name_admin(user)] started the byond-tracy profiler.")) + log_admin("[key_name(user)] started the byond-tracy profiler.") + if(GLOB.tracy_log) + rustg_file_write("[GLOB.tracy_log]", "[GLOB.log_directory]/tracy.loc") + +ADMIN_VERB_CUSTOM_EXIST_CHECK(start_tracy) + return CONFIG_GET(flag/allow_tracy_start) && fexists(TRACY_DLL_PATH) + +ADMIN_VERB(queue_tracy, R_DEBUG, "Toggle Tracy Next Round", "Toggle running the byond-tracy profiler next round", ADMIN_CATEGORY_DEBUG) + if(fexists(TRACY_ENABLE_PATH)) + fdel(TRACY_ENABLE_PATH) + else + rustg_file_write("[user.ckey]", TRACY_ENABLE_PATH) + message_admins(span_adminnotice("[key_name_admin(user)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round.")) + log_admin("[key_name(user)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round.") + +ADMIN_VERB_CUSTOM_EXIST_CHECK(queue_tracy) + return CONFIG_GET(flag/allow_tracy_queue) && fexists(TRACY_DLL_PATH) +#endif diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm index 71722eb6d64..09903e76429 100644 --- a/code/modules/admin/verbs/ert.dm +++ b/code/modules/admin/verbs/ert.dm @@ -157,7 +157,7 @@ spawn_turfs += get_turf(spawner) if(!brief_spawn) - brief_spawn = locate(/obj/effect/landmark/ert_shuttle_brief_spawn) in affected_turf + brief_spawn = get_turf(locate(/obj/effect/landmark/ert_shuttle_brief_spawn) in affected_turf) if(!length(spawn_turfs)) stack_trace("ERT shuttle loaded but found no spawnpoints, placing the ERT at wherever inside the shuttle instead.") @@ -206,7 +206,7 @@ candidate_living_exps = sort_list(candidate_living_exps, cmp=/proc/cmp_numeric_dsc) if(candidate_living_exps.len > ERT_EXPERIENCED_LEADER_CHOOSE_TOP) - candidate_living_exps = candidate_living_exps.Cut(ERT_EXPERIENCED_LEADER_CHOOSE_TOP+1) // pick from the top ERT_EXPERIENCED_LEADER_CHOOSE_TOP contenders in playtime + candidate_living_exps.Cut(ERT_EXPERIENCED_LEADER_CHOOSE_TOP+1) // pick from the top ERT_EXPERIENCED_LEADER_CHOOSE_TOP contenders in playtime earmarked_leader = pick(candidate_living_exps) else earmarked_leader = pick(candidates) diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index b70465666f3..d66e7f777c3 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -41,7 +41,10 @@ ADMIN_VERB(play_sound, R_SOUND, "Play Global Sound", "Play a sound to all connec ADMIN_VERB(play_local_sound, R_SOUND, "Play Local Sound", "Plays a sound only you can hear.", ADMIN_CATEGORY_FUN, sound as sound) log_admin("[key_name(user)] played a local sound [sound]") message_admins("[key_name_admin(user)] played a local sound [sound]") - playsound(get_turf(user.mob), sound, 50, FALSE, FALSE) + var/volume = tgui_input_number(user, "What volume would you like the sound to play at?", max_value = 100) + var/sound/admin_sound = sound(sound) + admin_sound.volume = volume || 50 + playsound(get_turf(user.mob), sound, FALSE, FALSE) BLACKBOX_LOG_ADMIN_VERB("Play Local Sound") ADMIN_VERB(play_direct_mob_sound, R_SOUND, "Play Direct Mob Sound", "Play a sound directly to a mob.", ADMIN_CATEGORY_FUN, sound as sound, mob/target in world) @@ -51,6 +54,10 @@ ADMIN_VERB(play_direct_mob_sound, R_SOUND, "Play Direct Mob Sound", "Play a soun return log_admin("[key_name(user)] played a direct mob sound [sound] to [key_name_admin(target)].") message_admins("[key_name_admin(user)] played a direct mob sound [sound] to [ADMIN_LOOKUPFLW(target)].") + var/volume = tgui_input_number(user, "What volume would you like the sound to play at?", max_value = 100) + var/sound/admin_sound = sound(sound) + if(volume) + admin_sound.volume = volume SEND_SOUND(target, sound) BLACKBOX_LOG_ADMIN_VERB("Play Direct Mob Sound") @@ -175,6 +182,10 @@ ADMIN_VERB(play_web_sound, R_SOUND, "Play Internet Sound", "Play a given interne web_sound(user.mob, null) ADMIN_VERB(set_round_end_sound, R_SOUND, "Set Round End Sound", "Set the sound that plays on round end.", ADMIN_CATEGORY_FUN, sound as sound) + var/volume = tgui_input_number(user, "What volume would you like this sound to play at?", max_value = 100) + var/sound/admin_sound = sound(sound) + if(volume) + admin_sound.volume = volume SSticker.SetRoundEndSound(sound) log_admin("[key_name(user)] set the round end sound to [sound]") diff --git a/code/modules/admin/view_variables/admin_delete.dm b/code/modules/admin/view_variables/admin_delete.dm index 5ef04b351cc..b9a11b50e65 100644 --- a/code/modules/admin/view_variables/admin_delete.dm +++ b/code/modules/admin/view_variables/admin_delete.dm @@ -16,6 +16,7 @@ log_admin("[key_name(usr)] deleted [D] [coords]") message_admins("[key_name_admin(usr)] deleted [D] [jmp_coords]") BLACKBOX_LOG_ADMIN_VERB("Delete") + SEND_SIGNAL(D, COMSIG_ADMIN_DELETING, src) if(isturf(D)) var/turf/T = D T.ScrapeAway() diff --git a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm index 296eef07e81..790dd0f8e53 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm @@ -60,5 +60,5 @@ user.visible_message(span_notice("[user] inserts [tool] into [target]."), span_notice("You insert [tool] into [target].")) user.temporarilyRemoveItemFromInventory(tool, TRUE) var/obj/item/organ/heart/gland/gland = tool - gland.Insert(target, 2) - return 1 + gland.Insert(target, special = TRUE, movement_flags = FROM_ABDUCTOR_SURGERY) + return TRUE diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm index b991a16b831..9f5bae7aad5 100644 --- a/code/modules/antagonists/abductor/equipment/gland.dm +++ b/code/modules/antagonists/abductor/equipment/gland.dm @@ -84,7 +84,7 @@ active_mind_control = FALSE return TRUE -/obj/item/organ/heart/gland/mob_remove(mob/living/carbon/gland_owner, special, movement_flags) +/obj/item/organ/heart/gland/on_mob_remove(mob/living/carbon/gland_owner, special, movement_flags) . = ..() active = FALSE if(initial(uses) == 1) @@ -93,10 +93,10 @@ hud.remove_atom_from_hud(gland_owner) clear_mind_control() -/obj/item/organ/heart/gland/mob_insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/heart/gland/on_mob_insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags) . = ..() - if(special != 2 && uses) // Special 2 means abductor surgery + if(!(movement_flags & FROM_ABDUCTOR_SURGERY) && uses) Start() var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR] hud.add_atom_to_hud(gland_owner) diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm index 09a8fdefa31..644e3a827bf 100644 --- a/code/modules/antagonists/abductor/machinery/camera.dm +++ b/code/modules/antagonists/abductor/machinery/camera.dm @@ -21,11 +21,12 @@ return ..() /obj/machinery/computer/camera_advanced/abductor/CreateEye() - ..() - eyeobj.visible_icon = TRUE - eyeobj.icon = 'icons/mob/silicon/cameramob.dmi' + . = ..() + //For observers + eyeobj.icon = 'icons/mob/eyemob.dmi' eyeobj.icon_state = "abductor_camera" - eyeobj.SetInvisibility(INVISIBILITY_OBSERVER) + //For the user + eyeobj.set_user_icon(eyeobj.icon, eyeobj.icon_state) /obj/machinery/computer/camera_advanced/abductor/GrantActions(mob/living/carbon/user) if(!abduct_created) @@ -57,7 +58,7 @@ to_chat(owner, span_warning("You must wait [DisplayTimeText(use_delay - world.time)] to use the [target] again!")) return var/mob/living/carbon/human/C = owner - var/mob/camera/ai_eye/remote/remote_eye = C.remote_control + var/mob/eye/camera/remote/remote_eye = C.remote_control var/obj/machinery/abductor/pad/P = target var/area/target_area = get_area(remote_eye) @@ -101,7 +102,7 @@ to_chat(owner, span_warning("You can only teleport to one place at a time!")) return var/mob/living/carbon/human/C = owner - var/mob/camera/ai_eye/remote/remote_eye = C.remote_control + var/mob/eye/camera/remote/remote_eye = C.remote_control var/obj/machinery/abductor/pad/P = target var/area/target_area = get_area(remote_eye) @@ -151,7 +152,7 @@ return var/mob/living/carbon/human/C = owner - var/mob/camera/ai_eye/remote/remote_eye = C.remote_control + var/mob/eye/camera/remote/remote_eye = C.remote_control var/obj/machinery/abductor/console/console = target console.SetDroppoint(remote_eye.loc,owner) diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm index 3dcdaf5a5b0..4c5a30c0d9e 100644 --- a/code/modules/antagonists/abductor/machinery/console.dm +++ b/code/modules/antagonists/abductor/machinery/console.dm @@ -94,10 +94,19 @@ "items" = (category == selected_cat ? list() : null)) for(var/gear in possible_gear[category]) var/datum/abductor_gear/AG = possible_gear[category][gear] + + var/atom/gear_path + if(!length(AG.build_path)) + continue + + gear_path = AG.build_path[1] + cat["items"] += list(list( "name" = AG.name, "cost" = AG.cost, "desc" = AG.description, + "icon" = gear_path::icon, + "icon_state" = gear_path::icon_state, )) data["categories"] += list(cat) return data diff --git a/code/modules/antagonists/blob/blob_antag.dm b/code/modules/antagonists/blob/blob_antag.dm index 9f9d97fac8d..25bea4b083e 100644 --- a/code/modules/antagonists/blob/blob_antag.dm +++ b/code/modules/antagonists/blob/blob_antag.dm @@ -18,7 +18,7 @@ var/basic_report = ..() //Display max blobpoints for blebs that lost if(isovermind(owner.current)) //embarrasing if not - var/mob/camera/blob/overmind = owner.current + var/mob/eye/blob/overmind = owner.current if(!overmind.victory_in_progress) //if it won this doesn't really matter var/point_report = "
[owner.name] took over [overmind.max_count] tiles at the height of its growth." return basic_report+point_report @@ -58,7 +58,7 @@ if(!isovermind(user)) return data - var/mob/camera/blob/blob = user + var/mob/eye/blob/blob = user var/datum/blobstrain/reagent/blobstrain = blob.blobstrain if(!blobstrain) @@ -129,7 +129,7 @@ placement_override = BLOB_RANDOM_PLACEMENT to_chat(owner, span_warning("Because your current location is an invalid starting spot and you need to pop, you've been moved to a random location!")) - var/mob/camera/blob/blob_cam = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob) + var/mob/eye/blob/blob_cam = new /mob/eye/blob(get_turf(old_body), blobtag.starting_points_human_blob) owner.mind.transfer_to(blob_cam) old_body.gib() blob_cam.place_blob_core(placement_override, pop_override = TRUE) @@ -147,7 +147,7 @@ /datum/antagonist/blob/antag_listing_status() . = ..() if(owner?.current) - var/mob/camera/blob/blob_cam = owner.current + var/mob/eye/blob/blob_cam = owner.current if(istype(blob_cam)) . += "(Progress: [length(blob_cam.blobs_legit)]/[blob_cam.blobwincount])" diff --git a/code/modules/antagonists/blob/blob_minion.dm b/code/modules/antagonists/blob/blob_minion.dm index 9bf37e961d5..e0ff3beb9f9 100644 --- a/code/modules/antagonists/blob/blob_minion.dm +++ b/code/modules/antagonists/blob/blob_minion.dm @@ -7,7 +7,7 @@ /// The blob core that this minion is attached to var/datum/weakref/overmind -/datum/antagonist/blob_minion/New(mob/camera/blob/overmind) +/datum/antagonist/blob_minion/New(mob/eye/blob/overmind) . = ..() src.overmind = WEAKREF(overmind) @@ -24,7 +24,7 @@ var/datum/weakref/overmind /datum/objective/blob_minion/check_completion() - var/mob/camera/blob/resolved_overmind = overmind.resolve() + var/mob/eye/blob/resolved_overmind = overmind.resolve() if(!resolved_overmind) return FALSE return resolved_overmind.stat != DEAD diff --git a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm index 27d177dde68..54d393780b2 100644 --- a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm +++ b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm @@ -24,7 +24,7 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/ var/resource_delay = 0 /// For blob-mobs and extinguishing-based effects var/fire_based = FALSE - var/mob/camera/blob/overmind + var/mob/eye/blob/overmind /// The amount of health regenned on core_process var/base_core_regen = BLOB_CORE_HP_REGEN /// The amount of points gained on core_process @@ -63,7 +63,7 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/ /// Makes blobbernauts inject a bonus amount of reagents, making their attacks more powerful var/blobbernaut_reagentatk_bonus = 0 -/datum/blobstrain/New(mob/camera/blob/new_overmind) +/datum/blobstrain/New(mob/eye/blob/new_overmind) if (!istype(new_overmind)) stack_trace("blobstrain created without overmind") overmind = new_overmind @@ -155,7 +155,7 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/ /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 +/datum/blobstrain/proc/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/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 diff --git a/code/modules/antagonists/blob/blobstrains/_reagent.dm b/code/modules/antagonists/blob/blobstrains/_reagent.dm index 65a50621b17..bc9c61dd4b1 100644 --- a/code/modules/antagonists/blob/blobstrains/_reagent.dm +++ b/code/modules/antagonists/blob/blobstrains/_reagent.dm @@ -1,7 +1,7 @@ /datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones // what do you mean "legacy" you never added an alternative var/datum/reagent/reagent -/datum/blobstrain/reagent/New(mob/camera/blob/new_overmind) +/datum/blobstrain/reagent/New(mob/eye/blob/new_overmind) . = ..() reagent = new reagent() @@ -42,12 +42,12 @@ description = "[name] is the reagent created by that type of blob." /// Used by blob reagents to calculate the reaction volume they should use when exposing mobs. -/datum/reagent/blob/proc/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/proc/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) if(exposed_mob.stat == DEAD || HAS_TRAIT(exposed_mob, TRAIT_BLOB_ALLY)) 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. /// Exists to earmark the new overmind arg used by blob reagents. -/datum/reagent/blob/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) return ..() diff --git a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm index ded3be1458e..f01f2c2faad 100644 --- a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm +++ b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm @@ -32,7 +32,7 @@ taste_description = "burning oil" color = "#B68D00" -/datum/reagent/blob/blazing_oil/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/blazing_oil/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.adjust_fire_stacks(round(reac_volume/10)) diff --git a/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm b/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm index acb4d96c23a..64aa9c26f75 100644 --- a/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm +++ b/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm @@ -16,7 +16,7 @@ color = "#8BA6E9" taste_description = "brain freeze" -/datum/reagent/blob/cryogenic_poison/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/cryogenic_poison/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) if(exposed_mob.reagents) diff --git a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm index 352d7c230a4..1a2cb9fe854 100644 --- a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm +++ b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm @@ -25,7 +25,7 @@ I.forceMove(get_turf(spore)) I.throw_at(get_edge_target_turf(spore,pick(GLOB.alldirs)), 6, 5, spore, TRUE, FALSE, null, 3) -/datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this +/datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/blob/O, coefficient = 1) //when the blob expands, do this for (var/obj/item/I in T) I.forceMove(overmind.blob_core) diff --git a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm index ea2bf54d769..cb5f565ef55 100644 --- a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm +++ b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm @@ -22,7 +22,7 @@ color = "#E88D5D" taste_description = "fizzing" -/datum/reagent/blob/distributed_neurons/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/distributed_neurons/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.apply_damage(0.6*reac_volume, TOX) diff --git a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm index 4a5c49d851a..d4c9da7e0e4 100644 --- a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm +++ b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm @@ -23,7 +23,7 @@ taste_description = "pop rocks" color = "#83ECEC" -/datum/reagent/blob/electromagnetic_web/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/electromagnetic_web/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) if(prob(reac_volume*2)) diff --git a/code/modules/antagonists/blob/blobstrains/energized_jelly.dm b/code/modules/antagonists/blob/blobstrains/energized_jelly.dm index 9fa5ed9ab96..43c18fc8090 100644 --- a/code/modules/antagonists/blob/blobstrains/energized_jelly.dm +++ b/code/modules/antagonists/blob/blobstrains/energized_jelly.dm @@ -26,7 +26,7 @@ taste_description = "gelatin" color = "#EFD65A" -/datum/reagent/blob/energized_jelly/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/energized_jelly/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.losebreath += round(0.2*reac_volume) diff --git a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm index e1ae8294df3..d068373e86b 100644 --- a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm +++ b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm @@ -31,7 +31,7 @@ taste_description = "the bomb" color = "#8B2500" -/datum/reagent/blob/explosive_lattice/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/explosive_lattice/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() var/brute_loss = 0 var/burn_loss = 0 @@ -51,7 +51,7 @@ brute_loss = brute_loss*(2 - round(bomb_armor*0.01, 0.05)) burn_loss = brute_loss - + exposed_mob.take_overall_damage(brute_loss, burn_loss) for(var/mob/living/nearby_mob in orange(epicenter_turf, 1)) @@ -69,6 +69,6 @@ burn_loss = brute_loss nearby_mob.take_overall_damage(brute_loss, burn_loss) - + else exposed_mob.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND) diff --git a/code/modules/antagonists/blob/blobstrains/multiplex.dm b/code/modules/antagonists/blob/blobstrains/multiplex.dm index aaebf1d0526..aedb571c6b6 100644 --- a/code/modules/antagonists/blob/blobstrains/multiplex.dm +++ b/code/modules/antagonists/blob/blobstrains/multiplex.dm @@ -2,7 +2,7 @@ var/list/blobstrains var/typeshare -/datum/blobstrain/multiplex/New(mob/camera/blob/new_overmind, list/blobstrains) +/datum/blobstrain/multiplex/New(mob/eye/blob/new_overmind, list/blobstrains) . = ..() for (var/bt in blobstrains) if (ispath(bt, /datum/blobstrain)) @@ -21,7 +21,7 @@ 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 +/datum/blobstrain/multiplex/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/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) diff --git a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm index 4c84eb639d7..1dfb0bf4077 100644 --- a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm +++ b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm @@ -11,7 +11,7 @@ reagent = /datum/reagent/blob/networked_fibers core_regen_bonus = 3 -/datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/spawning_blob, obj/structure/blob/new_blob, turf/chosen_turf, mob/camera/blob/overmind) +/datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/spawning_blob, obj/structure/blob/new_blob, turf/chosen_turf, mob/eye/blob/overmind) if(!overmind && new_blob.overmind) new_blob.overmind.add_points(1) qdel(new_blob) @@ -33,7 +33,7 @@ taste_description = "efficiency" color = "#4F4441" -/datum/reagent/blob/networked_fibers/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/networked_fibers/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND) diff --git a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm index d035319219d..40019beb013 100644 --- a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm +++ b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm @@ -39,7 +39,7 @@ taste_description = "a sponge" color = "#AAAABB" -/datum/reagent/blob/pressurized_slime/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/pressurized_slime/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) var/turf/open/location_turf = get_turf(exposed_mob) diff --git a/code/modules/antagonists/blob/blobstrains/reactive_spines.dm b/code/modules/antagonists/blob/blobstrains/reactive_spines.dm index 1c8cb893df8..778b1a22784 100644 --- a/code/modules/antagonists/blob/blobstrains/reactive_spines.dm +++ b/code/modules/antagonists/blob/blobstrains/reactive_spines.dm @@ -34,12 +34,12 @@ taste_description = "rock" color = "#9ACD32" -/datum/reagent/blob/reactive_spines/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/reactive_spines/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) if(exposed_mob.stat == DEAD || HAS_TRAIT(exposed_mob, TRAIT_BLOB_ALLY)) return 0 //the dead, and blob mobs, don't cause reactions return reac_volume -/datum/reagent/blob/reactive_spines/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/reactive_spines/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.adjustBruteLoss(reac_volume) diff --git a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm index d9010a96537..101995f23d5 100644 --- a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm +++ b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm @@ -16,7 +16,7 @@ taste_description = "heaven" color = "#A88FB7" -/datum/reagent/blob/regenerative_materia/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/regenerative_materia/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) if(iscarbon(exposed_mob)) diff --git a/code/modules/antagonists/blob/blobstrains/replicating_foam.dm b/code/modules/antagonists/blob/blobstrains/replicating_foam.dm index 83d84618da5..949c945e9c6 100644 --- a/code/modules/antagonists/blob/blobstrains/replicating_foam.dm +++ b/code/modules/antagonists/blob/blobstrains/replicating_foam.dm @@ -21,7 +21,7 @@ return ..() -/datum/blobstrain/reagent/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O) +/datum/blobstrain/reagent/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/blob/O) if(prob(30)) newB.expand(null, null, 0) //do it again! @@ -30,7 +30,7 @@ taste_description = "duplication" color = "#7B5A57" -/datum/reagent/blob/replicating_foam/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/replicating_foam/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.apply_damage(0.7*reac_volume, BRUTE, wound_bonus=CANT_WOUND) diff --git a/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm b/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm index 3db0041b310..8dfe798c57e 100644 --- a/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm +++ b/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm @@ -9,7 +9,7 @@ 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) +/datum/blobstrain/reagent/shifting_fragments/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/blob/O) if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield))) newB.forceMove(get_turf(B)) B.forceMove(T) @@ -31,7 +31,7 @@ name = "Shifting Fragments" color = "#C8963C" -/datum/reagent/blob/shifting_fragments/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/shifting_fragments/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.apply_damage(0.7*reac_volume, BRUTE, wound_bonus=CANT_WOUND) diff --git a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm index 825104ddcc1..1030e447106 100644 --- a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm +++ b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm @@ -30,7 +30,7 @@ taste_description = "toxic mold" color = "#65ADA2" -/datum/reagent/blob/synchronous_mesh/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) +/datum/reagent/blob/synchronous_mesh/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message, touch_protection, mob/eye/blob/overmind) . = ..() reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) exposed_mob.apply_damage(0.2*reac_volume, BRUTE, wound_bonus=CANT_WOUND) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index a20db024cf6..acd0737537e 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -5,11 +5,11 @@ GLOBAL_LIST_EMPTY(overminds) GLOBAL_LIST_EMPTY(blob_nodes) -/mob/camera/blob +/mob/eye/blob name = "Blob Overmind" real_name = "Blob Overmind" desc = "The overmind. It controls the blob." - icon = 'icons/mob/silicon/cameramob.dmi' + icon = 'icons/mob/eyemob.dmi' icon_state = "marker" mouse_opacity = MOUSE_OPACITY_ICON move_on_shuttle = TRUE @@ -53,7 +53,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) /// The list of strains the blob can reroll for. var/list/strain_choices -/mob/camera/blob/Initialize(mapload, starting_points = OVERMIND_STARTING_POINTS) +/mob/eye/blob/Initialize(mapload, starting_points = OVERMIND_STARTING_POINTS) ADD_TRAIT(src, TRAIT_BLOB_ALLY, INNATE_TRAIT) validate_location() blob_points = starting_points @@ -74,7 +74,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) START_PROCESSING(SSobj, src) GLOB.blob_telepathy_mobs |= src -/mob/camera/blob/proc/validate_location() +/mob/eye/blob/proc/validate_location() var/turf/T = get_turf(src) if(is_valid_turf(T)) return @@ -96,7 +96,7 @@ 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) +/mob/eye/blob/proc/set_strain(datum/blobstrain/new_strain) if (!ispath(new_strain)) return FALSE @@ -116,7 +116,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) to_chat(src, span_notice("The [blobstrain.name] strain [blobstrain.effectdesc]")) SEND_SIGNAL(src, COMSIG_BLOB_SELECTED_STRAIN, blobstrain) -/mob/camera/blob/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) +/mob/eye/blob/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) if(placed) // The blob can't expand vertically (yet) return FALSE . = ..() @@ -128,13 +128,13 @@ GLOBAL_LIST_EMPTY(blob_nodes) to_chat(src, span_warning("Your destination is invalid. Move somewhere else and try again.")) return null -/mob/camera/blob/proc/is_valid_turf(turf/tile) +/mob/eye/blob/proc/is_valid_turf(turf/tile) var/area/area = get_area(tile) if((area && !(area.area_flags & BLOBS_ALLOWED)) || !tile || !is_station_level(tile.z) || isgroundlessturf(tile)) return FALSE return TRUE -/mob/camera/blob/process() +/mob/eye/blob/process() if(!blob_core) if(!placed) if(manualplace_min_time && world.time >= manualplace_min_time) @@ -166,27 +166,27 @@ GLOBAL_LIST_EMPTY(blob_nodes) has_announced = TRUE /// Create a blob spore and link it to us -/mob/camera/blob/proc/create_spore(turf/spore_turf, spore_type = /mob/living/basic/blob_minion/spore/minion) +/mob/eye/blob/proc/create_spore(turf/spore_turf, spore_type = /mob/living/basic/blob_minion/spore/minion) var/mob/living/basic/blob_minion/spore/spore = new spore_type(spore_turf) assume_direct_control(spore) return spore /// Give our new minion the properties of a minion -/mob/camera/blob/proc/assume_direct_control(mob/living/minion) +/mob/eye/blob/proc/assume_direct_control(mob/living/minion) minion.AddComponent(/datum/component/blob_minion, src) /// Add something to our list of mobs and wait for it to die -/mob/camera/blob/proc/register_new_minion(mob/living/minion) +/mob/eye/blob/proc/register_new_minion(mob/living/minion) blob_mobs |= minion if (!istype(minion, /mob/living/basic/blob_minion/blobbernaut)) RegisterSignal(minion, COMSIG_LIVING_DEATH, PROC_REF(on_minion_death)) /// When a spore (or zombie) dies then we do this -/mob/camera/blob/proc/on_minion_death(mob/living/spore) +/mob/eye/blob/proc/on_minion_death(mob/living/spore) SIGNAL_HANDLER blobstrain.on_sporedeath(spore) -/mob/camera/blob/proc/victory() +/mob/eye/blob/proc/victory() sound_to_playing_players('sound/announcer/alarm/nuke_alarm.ogg', 70) sleep(10 SECONDS) for(var/mob/living/live_guy as anything in GLOB.mob_living_list) @@ -232,7 +232,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) SSticker.news_report = BLOB_WIN SSticker.force_ending = FORCE_END_ROUND -/mob/camera/blob/Destroy() +/mob/eye/blob/Destroy() QDEL_NULL(blobstrain) for(var/BL in GLOB.blobs) var/obj/structure/blob/B = BL @@ -255,7 +255,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) return ..() -/mob/camera/blob/Login() +/mob/eye/blob/Login() . = ..() if(!. || !client) return FALSE @@ -266,12 +266,12 @@ GLOBAL_LIST_EMPTY(blob_nodes) update_health_hud() add_points(0) -/mob/camera/blob/examine(mob/user) +/mob/eye/blob/examine(mob/user) . = ..() if(blobstrain) . += "Its strain is [blobstrain.name]." -/mob/camera/blob/update_health_hud() +/mob/eye/blob/update_health_hud() if(!blob_core) return FALSE var/current_health = round((blob_core.get_integrity() / blob_core.max_integrity) * 100) @@ -282,11 +282,11 @@ GLOBAL_LIST_EMPTY(blob_nodes) continue using_hud.blobpwrdisplay.maptext = MAPTEXT("
[current_health]%
") -/mob/camera/blob/proc/add_points(points) +/mob/eye/blob/proc/add_points(points) blob_points = clamp(blob_points + points, 0, max_blob_points) hud_used.blobpwrdisplay.maptext = MAPTEXT("
[round(blob_points)]
") -/mob/camera/blob/say( +/mob/eye/blob/say( message, bubble_type, list/spans = list(), @@ -314,7 +314,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) blob_talk(message) -/mob/camera/blob/proc/blob_talk(message) +/mob/eye/blob/proc/blob_talk(message) message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) @@ -327,10 +327,10 @@ GLOBAL_LIST_EMPTY(blob_nodes) var/rendered = span_big(span_blob("\[Blob Telepathy\] [name]([blobstrain.name]) [message_a]")) relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, src) -/mob/camera/blob/blob_act(obj/structure/blob/B) +/mob/eye/blob/blob_act(obj/structure/blob/B) return -/mob/camera/blob/get_status_tab_items() +/mob/eye/blob/get_status_tab_items() . = ..() if(blob_core) . += "Core Health: [blob_core.get_integrity()]" @@ -343,7 +343,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) . += "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]" . += "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]" -/mob/camera/blob/Move(NewLoc, Dir = 0) +/mob/eye/blob/Move(NewLoc, Dir = 0) if(placed) var/obj/structure/blob/B = locate() in range(OVERMIND_MAX_CAMERA_STRAY, NewLoc) if(B) @@ -357,7 +357,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) forceMove(NewLoc) return TRUE -/mob/camera/blob/mind_initialize() +/mob/eye/blob/mind_initialize() . = ..() var/datum/antagonist/blob/blob = mind.has_antag_datum(/datum/antagonist/blob) if(!blob) diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index 2f3b51741f9..58e25cdd620 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -1,7 +1,7 @@ #define BLOB_REROLL_RADIUS 60 /** Simple price check */ -/mob/camera/blob/proc/can_buy(cost = 15) +/mob/eye/blob/proc/can_buy(cost = 15) if(blob_points < cost) to_chat(src, span_warning("You cannot afford this, you need at least [cost] resources!")) balloon_alert(src, "need [cost-blob_points] more resource\s!") @@ -10,7 +10,7 @@ return TRUE /** Places the core itself */ -/mob/camera/blob/proc/place_blob_core(placement_override = BLOB_NORMAL_PLACEMENT, pop_override = FALSE) +/mob/eye/blob/proc/place_blob_core(placement_override = BLOB_NORMAL_PLACEMENT, pop_override = FALSE) if(placed && placement_override != BLOB_FORCE_PLACEMENT) return TRUE @@ -50,7 +50,7 @@ return TRUE /** Checks proximity for mobs */ -/mob/camera/blob/proc/check_core_visibility() +/mob/eye/blob/proc/check_core_visibility() for(var/mob/living/player in range(7, src)) if(ROLE_BLOB in player.faction) continue @@ -69,7 +69,7 @@ /** Checks for previous blobs or denose objects on the tile. */ -/mob/camera/blob/proc/check_objects_tile(turf/placement) +/mob/eye/blob/proc/check_objects_tile(turf/placement) for(var/obj/object in placement) if(istype(object, /obj/structure/blob)) if(istype(object, /obj/structure/blob/normal)) @@ -85,12 +85,12 @@ return TRUE /** Moves the core elsewhere. */ -/mob/camera/blob/proc/transport_core() +/mob/eye/blob/proc/transport_core() if(blob_core) forceMove(blob_core.drop_location()) /** Jumps to a node */ -/mob/camera/blob/proc/jump_to_node() +/mob/eye/blob/proc/jump_to_node() if(!length(GLOB.blob_nodes)) return FALSE @@ -108,7 +108,7 @@ forceMove(chosen_node.loc) /** Places important blob structures */ -/mob/camera/blob/proc/create_special(price, blobstrain, min_separation, needs_node, turf/tile) +/mob/eye/blob/proc/create_special(price, blobstrain, min_separation, needs_node, turf/tile) if(!tile) tile = get_turf(src) var/obj/structure/blob/blob = (locate(/obj/structure/blob) in tile) @@ -142,7 +142,7 @@ return node /** Toggles requiring nodes */ -/mob/camera/blob/proc/toggle_node_req() +/mob/eye/blob/proc/toggle_node_req() nodes_required = !nodes_required if(nodes_required) to_chat(src, span_warning("You now require a nearby node or core to place factory and resource blobs.")) @@ -150,7 +150,7 @@ to_chat(src, span_warning("You no longer require a nearby node or core to place factory and resource blobs.")) /** Creates a shield to reflect projectiles */ -/mob/camera/blob/proc/create_shield(turf/tile) +/mob/eye/blob/proc/create_shield(turf/tile) var/obj/structure/blob/shield/shield = locate(/obj/structure/blob/shield) in tile if(!shield) shield = create_special(BLOB_UPGRADE_STRONG_COST, /obj/structure/blob/shield, 0, FALSE, tile) @@ -170,7 +170,7 @@ shield.balloon_alert(src, "upgraded to [shield.name]!") /** Preliminary check before polling ghosts. */ -/mob/camera/blob/proc/create_blobbernaut() +/mob/eye/blob/proc/create_blobbernaut() var/turf/current_turf = get_turf(src) var/obj/structure/blob/special/factory/factory = locate(/obj/structure/blob/special/factory) in current_turf if(!factory) @@ -190,7 +190,7 @@ pick_blobbernaut_candidate(factory) /// Polls ghosts to get a blobbernaut candidate. -/mob/camera/blob/proc/pick_blobbernaut_candidate(obj/structure/blob/special/factory/factory) +/mob/eye/blob/proc/pick_blobbernaut_candidate(obj/structure/blob/special/factory/factory) if(isnull(factory)) return var/icon/blobbernaut_icon = icon(icon, "blobbernaut") @@ -209,7 +209,7 @@ on_poll_concluded(factory, chosen_one) /// Called when the ghost poll concludes -/mob/camera/blob/proc/on_poll_concluded(obj/structure/blob/special/factory/factory, mob/dead/observer/ghost) +/mob/eye/blob/proc/on_poll_concluded(obj/structure/blob/special/factory/factory, mob/dead/observer/ghost) if(isnull(ghost)) to_chat(src, span_warning("You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.")) add_points(BLOBMOB_BLOBBERNAUT_RESOURCE_COST) @@ -223,14 +223,14 @@ RegisterSignal(blobber, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(on_blobbernaut_attacked)) /// When one of our boys attacked something, we sometimes want to perform extra effects -/mob/camera/blob/proc/on_blobbernaut_attacked(mob/living/basic/blobbynaut, atom/target, success) +/mob/eye/blob/proc/on_blobbernaut_attacked(mob/living/basic/blobbynaut, atom/target, success) SIGNAL_HANDLER if (!success) return blobstrain.blobbernaut_attack(target, blobbynaut) /** Moves the core */ -/mob/camera/blob/proc/relocate_core() +/mob/eye/blob/proc/relocate_core() var/turf/tile = get_turf(src) var/obj/structure/blob/special/node/blob = locate(/obj/structure/blob/special/node) in tile @@ -258,7 +258,7 @@ blob.setDir(old_dir) /** Searches the tile for a blob and removes it. */ -/mob/camera/blob/proc/remove_blob(turf/tile) +/mob/eye/blob/proc/remove_blob(turf/tile) var/obj/structure/blob/blob = locate() in tile if(!blob) @@ -283,7 +283,7 @@ return TRUE /** Expands to nearby tiles */ -/mob/camera/blob/proc/expand_blob(turf/tile) +/mob/eye/blob/proc/expand_blob(turf/tile) if(world.time < last_attack) return FALSE var/list/possible_blobs = list() @@ -327,7 +327,7 @@ /** Finds cardinal and diagonal attack directions */ -/mob/camera/blob/proc/directional_attack(turf/tile, list/possible_blobs, attack_success = FALSE) +/mob/eye/blob/proc/directional_attack(turf/tile, list/possible_blobs, attack_success = FALSE) var/list/cardinal_blobs = list() var/list/diagonal_blobs = list() @@ -353,7 +353,7 @@ return TRUE /** Rally spores to a location */ -/mob/camera/blob/proc/rally_spores(turf/tile) +/mob/eye/blob/proc/rally_spores(turf/tile) to_chat(src, "You rally your spores.") var/list/surrounding_turfs = TURF_NEIGHBORS(tile) if(!length(surrounding_turfs)) @@ -365,7 +365,7 @@ blob_mob.ai_controller.set_blackboard_key(BB_TRAVEL_DESTINATION, pick(surrounding_turfs)) /** Opens the reroll menu to change strains */ -/mob/camera/blob/proc/strain_reroll() +/mob/eye/blob/proc/strain_reroll() if (!free_strain_rerolls && blob_points < BLOB_POWER_REROLL_COST) to_chat(src, span_warning("You need at least [BLOB_POWER_REROLL_COST] resources to reroll your strain again!")) return FALSE @@ -373,7 +373,7 @@ open_reroll_menu() /** Controls changing strains */ -/mob/camera/blob/proc/open_reroll_menu() +/mob/eye/blob/proc/open_reroll_menu() if (!strain_choices) strain_choices = list() diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index ce1b016dcb0..4d401a52189 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -29,7 +29,7 @@ var/ignore_syncmesh_share = 0 /// If the blob blocks atmos and heat spread var/atmosblock = FALSE - var/mob/camera/blob/overmind + var/mob/eye/blob/overmind /datum/armor/structure_blob @@ -144,7 +144,7 @@ O.setDir(dir) var/area/my_area = get_area(src) if(controller) - var/mob/camera/blob/BO = controller + var/mob/eye/blob/BO = controller O.color = BO.blobstrain.color if(!(my_area.area_flags & BLOBS_ALLOWED)) O.color = BlendRGB(O.color, COLOR_WHITE, 0.5) //lighten it to indicate an off-station blob @@ -416,7 +416,7 @@ if(SPT_PROB(BLOB_REINFORCE_CHANCE, seconds_per_tick)) B.change_to(/obj/structure/blob/shield/reflective/core, overmind) -/obj/structure/blob/special/proc/pulse_area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2) +/obj/structure/blob/special/proc/pulse_area(mob/eye/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2) if(QDELETED(pulsing_overmind)) pulsing_overmind = overmind Be_Pulsed() diff --git a/code/modules/antagonists/changeling/headslug_eggs.dm b/code/modules/antagonists/changeling/headslug_eggs.dm index e2238d9d7e7..5c237b11560 100644 --- a/code/modules/antagonists/changeling/headslug_eggs.dm +++ b/code/modules/antagonists/changeling/headslug_eggs.dm @@ -11,11 +11,11 @@ /// When this egg last got removed from a body. If -1, the egg hasn't been removed from a body. var/removal_time = -1 -/obj/item/organ/body_egg/changeling_egg/mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/body_egg/changeling_egg/on_mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags) . = ..() hatch_time = world.time + (removal_time == -1 ? EGG_INCUBATION_TIME : (hatch_time - removal_time)) -/obj/item/organ/body_egg/changeling_egg/mob_remove(mob/living/carbon/egg_owner, special, movement_flags) +/obj/item/organ/body_egg/changeling_egg/on_mob_remove(mob/living/carbon/egg_owner, special, movement_flags) . = ..() removal_time = world.time diff --git a/code/modules/antagonists/changeling/powers/absorb.dm b/code/modules/antagonists/changeling/powers/absorb.dm index 71b1509ec81..167371b4d67 100644 --- a/code/modules/antagonists/changeling/powers/absorb.dm +++ b/code/modules/antagonists/changeling/powers/absorb.dm @@ -106,12 +106,12 @@ var/list/recent_speech = target.copy_recent_speech() if(recent_speech.len) - changeling.antag_memory += "Some of [target]'s speech patterns, we should study these to better impersonate [target.p_them()]!
" + changeling.antag_memory += "Some of [target]'s speech patterns, we should study these to better impersonate [target.p_them()]: " to_chat(owner, span_boldnotice("Some of [target]'s speech patterns, we should study these to better impersonate [target.p_them()]!")) for(var/spoken_memory in recent_speech) - changeling.antag_memory += "\"[spoken_memory]\"
" + changeling.antag_memory += " \"[spoken_memory]\"" to_chat(owner, span_notice("\"[spoken_memory]\"")) - changeling.antag_memory += "We have no more knowledge of [target]'s speech patterns.
" + changeling.antag_memory += ". We have no more knowledge of [target]'s speech patterns. " to_chat(owner, span_boldnotice("We have no more knowledge of [target]'s speech patterns.")) diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 158b32df216..874141237a9 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -71,7 +71,7 @@ if(HAS_TRAIT_FROM_ONLY(user, TRAIT_PARALYSIS_L_ARM, CHANGELING_TRAIT) || HAS_TRAIT_FROM_ONLY(user, TRAIT_PARALYSIS_R_ARM, CHANGELING_TRAIT)) user.balloon_alert(user, "not enough muscle!") // no cheesing repuprosed glands return - if(user.active_hand_index % 2 == 0) //we regen the arm before changing it into the weapon + if(IS_RIGHT_INDEX(user.active_hand_index)) //we regen the arm before changing it into the weapon limb_regen = user.regenerate_limb(BODY_ZONE_R_ARM, 1) else limb_regen = user.regenerate_limb(BODY_ZONE_L_ARM, 1) diff --git a/code/modules/antagonists/cult/cult_turf_overlay.dm b/code/modules/antagonists/cult/cult_turf_overlay.dm index df92d7aec90..e5e70bac1f7 100644 --- a/code/modules/antagonists/cult/cult_turf_overlay.dm +++ b/code/modules/antagonists/cult/cult_turf_overlay.dm @@ -10,7 +10,8 @@ /obj/effect/cult_turf/examine(mob/user) if(linked) - linked.examine(user) + return linked.examine(user) + return list() /obj/effect/cult_turf/singularity_act() return diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index 0af01ec5df1..98257cf658f 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -60,19 +60,6 @@ var/rust_strength = 0 /// Wether we are allowed to ascend var/feast_of_owls = FALSE - /// Static list of what each path converts to in the UI (colors are TGUI colors) - var/static/list/path_to_ui_bgr = list( - PATH_START = "node_side", - PATH_SIDE = "node_side", - PATH_RUST = "node_rust", - PATH_FLESH = "node_flesh", - PATH_ASH = "node_ash", - PATH_VOID = "node_void", - PATH_BLADE = "node_blade", - PATH_COSMIC = "node_cosmos", - PATH_LOCK = "node_lock", - PATH_MOON = "node_moon", - ) /// List that keeps track of which items have been gifted to the heretic after a cultist was sacrificed. Used to alter drop chances to reduce dupes. var/list/unlocked_heretic_items = list( @@ -106,9 +93,9 @@ //if the knowledge is a spell, use the spell's button else if(ispath(knowledge,/datum/heretic_knowledge/spell)) var/datum/heretic_knowledge/spell/spell_knowledge = knowledge - var/datum/action/cooldown/spell/result_spell = spell_knowledge.spell_to_add - icon_path = result_spell.button_icon - icon_state = result_spell.button_icon_state + var/datum/action/result_action = spell_knowledge.action_to_add + icon_path = result_action.button_icon + icon_state = result_action.button_icon_state //if the knowledge is a summon, use the mob sprite else if(ispath(knowledge,/datum/heretic_knowledge/summon)) @@ -150,7 +137,7 @@ knowledge_data["gainFlavor"] = initial(knowledge.gain_text) knowledge_data["cost"] = initial(knowledge.cost) knowledge_data["disabled"] = (!done) && (initial(knowledge.cost) > knowledge_points) - knowledge_data["bgr"] = (path_to_ui_bgr[initial(knowledge.route)] || "side") + knowledge_data["bgr"] = GLOB.heretic_research_tree[knowledge][HKT_UI_BGR] knowledge_data["finished"] = done knowledge_data["ascension"] = ispath(knowledge,/datum/heretic_knowledge/ultimate) @@ -178,10 +165,10 @@ for(var/datum/heretic_knowledge/knowledge as anything in researched_knowledge) var/list/knowledge_data = get_knowledge_data(knowledge,TRUE) - while(initial(knowledge.depth) > tiers.len) + while(GLOB.heretic_research_tree[knowledge][HKT_DEPTH] > tiers.len) tiers += list(list("nodes"=list())) - tiers[initial(knowledge.depth)]["nodes"] += list(knowledge_data) + tiers[GLOB.heretic_research_tree[knowledge][HKT_DEPTH]]["nodes"] += list(knowledge_data) for(var/datum/heretic_knowledge/knowledge as anything in get_researchable_knowledge()) var/list/knowledge_data = get_knowledge_data(knowledge,FALSE) @@ -190,10 +177,10 @@ if(ispath(knowledge, /datum/heretic_knowledge/ultimate)) knowledge_data["disabled"] ||= !can_ascend() - while(initial(knowledge.depth) > tiers.len) + while(GLOB.heretic_research_tree[knowledge][HKT_DEPTH] > tiers.len) tiers += list(list("nodes"=list())) - tiers[initial(knowledge.depth)]["nodes"] += list(knowledge_data) + tiers[GLOB.heretic_research_tree[knowledge][HKT_DEPTH]]["nodes"] += list(knowledge_data) data["knowledge_tiers"] = tiers @@ -272,6 +259,9 @@ return ..() /datum/antagonist/heretic/on_gain() + if(!GLOB.heretic_research_tree) + GLOB.heretic_research_tree = generate_heretic_research_tree() + if(give_objectives) forge_primary_objectives() @@ -822,8 +812,8 @@ var/list/banned_knowledge = list() for(var/knowledge_index in researched_knowledge) var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index] - researchable_knowledge |= knowledge.next_knowledge - banned_knowledge |= knowledge.banned_knowledge + researchable_knowledge |= GLOB.heretic_research_tree[knowledge_index][HKT_NEXT] + banned_knowledge |= GLOB.heretic_research_tree[knowledge_index][HKT_BAN] banned_knowledge |= knowledge.type researchable_knowledge -= banned_knowledge return researchable_knowledge @@ -937,7 +927,7 @@ // (All the main paths are (should be) the same length, so it doesn't matter.) var/rust_paths_found = 0 for(var/datum/heretic_knowledge/knowledge as anything in subtypesof(/datum/heretic_knowledge)) - if(initial(knowledge.route) == PATH_RUST) + if(GLOB.heretic_research_tree[knowledge][HKT_ROUTE] == PATH_RUST) rust_paths_found++ main_path_length = rust_paths_found diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index 79c7197df40..0e445c862f4 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -17,12 +17,6 @@ var/gain_text /// The abstract parent type of the knowledge, used in determine mutual exclusivity in some cases var/datum/heretic_knowledge/abstract_parent_type = /datum/heretic_knowledge - /// If TRUE, populates the banned_knowledge list of every other subtype of this knowledge's abstract_parent_type - var/mutually_exclusive = FALSE - /// The knowledge this unlocks next after learning. - var/list/next_knowledge = list() - /// What knowledge is incompatible with this. Knowledge in this list cannot be researched with this current knowledge. - var/list/banned_knowledge = list() /// Assoc list of [typepaths we need] to [amount needed]. /// If set, this knowledge allows the heretic to do a ritual on a transmutation rune with the components set. /// If one of the items in the list is a list, it's treated as 'any of these items will work' @@ -36,30 +30,18 @@ /// The priority of the knowledge. Higher priority knowledge appear higher in the ritual list. /// Number itself is completely arbitrary. Does not need to be set for non-ritual knowledge. var/priority = 0 - /// What path is this on. If set to "null", assumed to be unreachable (or abstract). - var/route + ///If this is considered starting knowledge, TRUE if yes + var/is_starting_knowledge = FALSE /// In case we want to override the default UI icon getter and plug in our own icon instead. /// if research_tree_icon_path is not null, research_tree_icon_state must also be specified or things may break var/research_tree_icon_path var/research_tree_icon_state var/research_tree_icon_frame = 1 var/research_tree_icon_dir = SOUTH - /// Level of knowledge tree where this knowledge should be in the UI - var/depth = 1 ///Determines what kind of monster ghosts will ignore from here on out. Defaults to POLL_IGNORE_HERETIC_MONSTER, but we define other types of monsters for more granularity. var/poll_ignore_define = POLL_IGNORE_HERETIC_MONSTER -/datum/heretic_knowledge/New() - if(!mutually_exclusive) - return - - for(var/knowledge_type in subtypesof(abstract_parent_type)) - if(knowledge_type == type) - continue - banned_knowledge += knowledge_type - -/** - * Called when the knowledge is first researched. +/** Called when the knowledge is first researched. * This is only ever called once per heretic. * * Arguments @@ -205,26 +187,26 @@ /datum/heretic_knowledge/spell abstract_parent_type = /datum/heretic_knowledge/spell /// Spell path we add to the heretic. Type-path. - var/datum/action/cooldown/spell/spell_to_add + var/datum/action/action_to_add /// The spell we actually created. - var/datum/weakref/created_spell_ref + var/datum/weakref/created_action_ref /datum/heretic_knowledge/spell/Destroy() - QDEL_NULL(created_spell_ref) + QDEL_NULL(created_action_ref) return ..() /datum/heretic_knowledge/spell/on_gain(mob/user, datum/antagonist/heretic/our_heretic) // Added spells are tracked on the body, and not the mind, // because we handle heretic mind transfers // via the antag datum (on_gain and on_lose). - var/datum/action/cooldown/spell/created_spell = created_spell_ref?.resolve() || new spell_to_add(user) - created_spell.Grant(user) - created_spell_ref = WEAKREF(created_spell) + var/datum/action/created_action = created_action_ref?.resolve() || new action_to_add(user) + created_action.Grant(user) + created_action_ref = WEAKREF(created_action) /datum/heretic_knowledge/spell/on_lose(mob/user, datum/antagonist/heretic/our_heretic) - var/datum/action/cooldown/spell/created_spell = created_spell_ref?.resolve() - if(created_spell?.owner == user) - created_spell.Remove(user) + var/datum/action/cooldown/spell/created_action = created_action_ref?.resolve() + if(created_action?.owner == user) + created_action.Remove(user) /** * A knowledge subtype for knowledge that can only @@ -269,24 +251,14 @@ */ /datum/heretic_knowledge/limited_amount/starting abstract_parent_type = /datum/heretic_knowledge/limited_amount/starting - mutually_exclusive = TRUE limit = 2 cost = 1 priority = MAX_KNOWLEDGE_PRIORITY - 5 - depth = 2 - -/datum/heretic_knowledge/limited_amount/starting/New() - . = ..() - // Starting path also determines the final knowledge we're limited too - for(var/datum/heretic_knowledge/final_knowledge_type as anything in subtypesof(/datum/heretic_knowledge/ultimate)) - if(initial(final_knowledge_type.route) == route) - continue - banned_knowledge += final_knowledge_type /datum/heretic_knowledge/limited_amount/starting/on_research(mob/user, datum/antagonist/heretic/our_heretic) . = ..() - our_heretic.heretic_path = route - SSblackbox.record_feedback("tally", "heretic_path_taken", 1, route) + our_heretic.heretic_path = GLOB.heretic_research_tree[type][HKT_ROUTE] + SSblackbox.record_feedback("tally", "heretic_path_taken", 1, our_heretic.heretic_path) /** * A knowledge subtype for heretic knowledge @@ -296,9 +268,7 @@ */ /datum/heretic_knowledge/mark abstract_parent_type = /datum/heretic_knowledge/mark - mutually_exclusive = TRUE cost = 2 - depth = 5 /// The status effect typepath we apply on people on mansus grasp. var/datum/status_effect/eldritch/mark_type @@ -364,9 +334,7 @@ */ /datum/heretic_knowledge/blade_upgrade abstract_parent_type = /datum/heretic_knowledge/blade_upgrade - mutually_exclusive = TRUE cost = 2 - depth = 9 /datum/heretic_knowledge/blade_upgrade/on_gain(mob/user, datum/antagonist/heretic/our_heretic) RegisterSignal(user, COMSIG_HERETIC_BLADE_ATTACK, PROC_REF(on_eldritch_blade)) @@ -603,10 +571,8 @@ desc = "A randomly generated transmutation ritual that rewards knowledge points and can only be completed once." gain_text = "Everything can be a key to unlocking the secrets behind the Gates. I must be wary and wise." abstract_parent_type = /datum/heretic_knowledge/knowledge_ritual - mutually_exclusive = TRUE cost = 1 priority = MAX_KNOWLEDGE_PRIORITY - 10 // A pretty important midgame ritual. - depth = 6 research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "book_open" /// Whether we've done the ritual. Only doable once. @@ -696,11 +662,9 @@ */ /datum/heretic_knowledge/ultimate abstract_parent_type = /datum/heretic_knowledge/ultimate - mutually_exclusive = TRUE // I guess, but it doesn't really matter by this point cost = 2 priority = MAX_KNOWLEDGE_PRIORITY + 1 // Yes, the final ritual should be ABOVE the max priority. required_atoms = list(/mob/living/carbon/human = 3) - depth = 11 //use this to store the achievement typepath var/datum/award/achievement/misc/ascension_achievement @@ -756,7 +720,7 @@ human_user.physiology.brute_mod *= 0.5 human_user.physiology.burn_mod *= 0.5 - SSblackbox.record_feedback("tally", "heretic_ascended", 1, route) + SSblackbox.record_feedback("tally", "heretic_ascended", 1, GLOB.heretic_research_tree[type][HKT_ROUTE]) log_heretic_knowledge("[key_name(user)] completed their final ritual at [worldtime2text()].") notify_ghosts( "[user] has completed an ascension ritual!", diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm index 06aad5fed3d..68eecb356ae 100644 --- a/code/modules/antagonists/heretic/heretic_living_heart.dm +++ b/code/modules/antagonists/heretic/heretic_living_heart.dm @@ -80,6 +80,8 @@ var/last_tracked_name /// Whether the target radial is currently opened. var/radial_open = FALSE + /// Navigator to our target that we have. + var/datum/status_effect/agent_pinpointer/scan/heretic/heretic_pinpointer /datum/action/cooldown/track_target/Grant(mob/granted) if(!IS_HERETIC(granted)) @@ -144,6 +146,7 @@ playsound(owner, 'sound/effects/singlebeat.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) owner.balloon_alert(owner, get_balloon_message(tracked_mob)) + // Let them know how to sacrifice people if they're able to be sac'd if(tracked_mob.stat == DEAD) to_chat(owner, span_hierophant("[tracked_mob] is dead. Bring them to a transmutation rune \ @@ -152,6 +155,26 @@ StartCooldown() return TRUE +/datum/action/cooldown/track_target/proc/make_navigate_arrow(turf/tracked_turf, arrow_color) + var/datum/hud/user_hud = owner.hud_used + if(!user_hud) + return + var/atom/movable/screen/heretic_arrow/arrow = new /atom/movable/screen/heretic_arrow(null, user_hud) + animate(arrow, transform = matrix(dir2angle(get_dir(owner, tracked_turf)), MATRIX_ROTATE), 0.2 SECONDS) + arrow.screen_loc = around_player + arrow.color = arrow_color + user_hud.infodisplay += arrow + user_hud.show_hud(user_hud.hud_version) + addtimer(CALLBACK(src, PROC_REF(end_effect), user_hud, arrow), 1.6 SECONDS) + +/datum/action/cooldown/track_target/proc/end_effect(datum/hud/user_hud, atom/movable/screen/heretic_arrow/arrow) + arrow.icon_state = "heretic_arrow_disappear" + addtimer(CALLBACK(src, PROC_REF(null_arrow), user_hud, arrow), 0.4 SECONDS) + +/datum/action/cooldown/track_target/proc/null_arrow(datum/hud/user_hud, atom/movable/screen/heretic_arrow/arrow) + user_hud.infodisplay -= arrow + user_hud.show_hud(user_hud.hud_version) + /// Callback for the radial to ensure it's closed when not allowed. /datum/action/cooldown/track_target/proc/check_menu() if(QDELETED(src)) @@ -204,17 +227,32 @@ var/dist = get_dist(our_turf, their_turf) var/dir = get_dir(our_turf, their_turf) + var/arrow_color + switch(dist) if(0 to 15) balloon_message = "very near, [dir2text(dir)]!" + arrow_color = COLOR_GREEN if(16 to 31) balloon_message = "near, [dir2text(dir)]!" + arrow_color = COLOR_YELLOW if(32 to 127) balloon_message = "far, [dir2text(dir)]!" + arrow_color = COLOR_ORANGE else balloon_message = "very far!" + arrow_color = COLOR_RED + + make_navigate_arrow(their_turf, arrow_color) if(tracked_mob.stat == DEAD) balloon_message = "they're dead, " + balloon_message return balloon_message + +/atom/movable/screen/heretic_arrow + icon = 'icons/effects/96x96.dmi' + name = "heretic arrow" + icon_state = "heretic_arrow_appear" + pixel_x = -32 + pixel_y = -32 diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm index 7b316d6cdb9..494a8d30521 100644 --- a/code/modules/antagonists/heretic/influences.dm +++ b/code/modules/antagonists/heretic/influences.dm @@ -114,7 +114,7 @@ their_poor_arm.dismember() qdel(their_poor_arm) else - to_chat(human_user,span_danger("You pull your hand away from the hole as the eldritch energy flails, trying to latch onto existance itself!")) + to_chat(human_user,span_danger("You pull your hand away from the hole as the eldritch energy flails, trying to latch onto existence itself!")) return TRUE /obj/effect/visible_heretic_influence/attack_tk(mob/user) diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm index fec25094bb7..0e8699f6771 100644 --- a/code/modules/antagonists/heretic/items/corrupted_organs.dm +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -28,7 +28,7 @@ if (LAZYLEN(hallucinations)) organ_owner.client.images |= hallucinations -/obj/item/organ/eyes/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/eyes/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if (!LAZYLEN(hallucinations)) return @@ -51,7 +51,7 @@ . = ..() RegisterSignal(organ_owner, COMSIG_MOB_SAY, PROC_REF(on_spoken)) -/obj/item/organ/tongue/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/tongue/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_MOB_SAY) @@ -87,11 +87,11 @@ . = ..() AddElement(/datum/element/corrupted_organ) -/obj/item/organ/liver/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/liver/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank)) -/obj/item/organ/liver/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/liver/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS) @@ -124,11 +124,11 @@ AddElement(/datum/element/corrupted_organ) AddElement(/datum/element/noticable_organ, "%PRONOUN_They %PRONOUN_have an unhealthy pallor.") -/obj/item/organ/stomach/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/stomach/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank)) -/obj/item/organ/stomach/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/stomach/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS) diff --git a/code/modules/antagonists/heretic/knife_effect.dm b/code/modules/antagonists/heretic/knife_effect.dm index 1bd44921cf9..e11fd79ab9f 100644 --- a/code/modules/antagonists/heretic/knife_effect.dm +++ b/code/modules/antagonists/heretic/knife_effect.dm @@ -12,3 +12,7 @@ AddElement(/datum/element/movetype_handler) ADD_TRAIT(src, TRAIT_MOVE_FLYING, INNATE_TRAIT) add_filter("dio_knife", 2, list("type" = "outline", "color" = glow_color, "size" = 1)) + +/obj/effect/floating_blade/haunted + icon = 'icons/obj/weapons/khopesh.dmi' + icon_state = "render" diff --git a/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm b/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm new file mode 100644 index 00000000000..5e3583e14b3 --- /dev/null +++ b/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm @@ -0,0 +1,199 @@ +//Global typecache of all heretic knowledges -> instantiate the tree columns -> make them link themselves -> replace the old heretic stuff + +//heretic research tree is a directional graph so we can use some basic graph stuff to make internally handling it easier +GLOBAL_LIST(heretic_research_tree) + +//HKT = Heretic Knowledge Tree (Heretic Research Tree :3) these objects really only exist for a short period of time at startup and then get deleted +/datum/heretic_knowledge_tree_column + ///Route that symbolizes what path this is + var/route + ///Used to determine if this is a side path or a main path + var/abstract_parent_type = /datum/heretic_knowledge_tree_column + ///IDs od neighbours (to left and right) + var/neighbour_type_left + var/neighbour_type_right + ///Tier1 knowledge (or knowledges) + var/tier1 + ///Tier2 knowledge (or knowledges) + var/tier2 + ///Tier3 knowledge (or knowledges) + var/tier3 + ///UI background + var/ui_bgr = "node_side" + +/datum/heretic_knowledge_tree_column/main + abstract_parent_type = /datum/heretic_knowledge_tree_column/main + + ///Starting knowledge - first thing you pick + var/start + ///Grasp upgrade + var/grasp + ///Mark upgrade + var/mark + ///Unique ritual of knoweldge + var/ritual_of_knowledge + ///Path specific unique ability + var/unique_ability + ///Blade upgrade + var/blade + ///Ascension + var/ascension + +/proc/generate_heretic_research_tree() + var/list/heretic_research_tree = list() + + //Initialize the data structure + for(var/type in subtypesof(/datum/heretic_knowledge)) + heretic_research_tree[type] = list() + heretic_research_tree[type][HKT_NEXT] = list() + heretic_research_tree[type][HKT_BAN] = list() + heretic_research_tree[type][HKT_DEPTH] = 1 + heretic_research_tree[type][HKT_UI_BGR] = "node_side" + + var/datum/heretic_knowledge/knowledge = type + if(initial(knowledge.is_starting_knowledge)) + heretic_research_tree[type][HKT_ROUTE] = PATH_START + continue + + heretic_research_tree[type][HKT_ROUTE] = null + + var/list/paths = list() + for(var/type in subtypesof(/datum/heretic_knowledge_tree_column)) + var/datum/heretic_knowledge_tree_column/column_path = type + if(initial(column_path.abstract_parent_type) == column_path) + continue + + var/datum/heretic_knowledge_tree_column/column = new type() + paths[column.type] = column + + var/list/start_blacklist = list() + var/list/grasp_blacklist = list() + var/list/mark_blacklist = list() + var/list/blade_blacklist = list() + var/list/asc_blacklist = list() + + for(var/id in paths) + if(!istype(paths[id],/datum/heretic_knowledge_tree_column/main)) + continue + var/datum/heretic_knowledge_tree_column/main/column = paths[id] + + start_blacklist += column.start + grasp_blacklist += column.grasp + mark_blacklist += column.mark + blade_blacklist += column.blade + asc_blacklist += column.ascension + + heretic_research_tree[/datum/heretic_knowledge/spell/basic][HKT_NEXT] += start_blacklist + + for(var/id in paths) + var/datum/heretic_knowledge_tree_column/this_column = paths[id] + var/datum/heretic_knowledge_tree_column/neighbour_0 = paths[this_column.neighbour_type_left] + var/datum/heretic_knowledge_tree_column/neighbour_1 = paths[this_column.neighbour_type_right] + //horizontal (two way) + var/list/tier1 = this_column.tier1 + var/list/tier2 = this_column.tier2 + var/list/tier3 = this_column.tier3 + + //Tier1, 2 and 3 can technically be lists so we handle them here + if(!islist(this_column.tier1)) + tier1 = list(this_column.tier1) + + if(!islist(this_column.tier2)) + tier2 = list(this_column.tier2) + + if(!islist(this_column.tier3)) + tier3 = list(this_column.tier3) + + for(var/t1_knowledge in tier1) + heretic_research_tree[t1_knowledge][HKT_NEXT] += neighbour_0.tier1 + heretic_research_tree[t1_knowledge][HKT_NEXT] += neighbour_1.tier1 + heretic_research_tree[t1_knowledge][HKT_ROUTE] = this_column.route + heretic_research_tree[t1_knowledge][HKT_UI_BGR] = this_column.ui_bgr + heretic_research_tree[t1_knowledge][HKT_DEPTH] = 4 + + for(var/t2_knowledge in tier2) + heretic_research_tree[t2_knowledge][HKT_NEXT] += neighbour_0.tier2 + heretic_research_tree[t2_knowledge][HKT_NEXT] += neighbour_1.tier2 + heretic_research_tree[t2_knowledge][HKT_ROUTE] = this_column.route + heretic_research_tree[t2_knowledge][HKT_UI_BGR] = this_column.ui_bgr + heretic_research_tree[t2_knowledge][HKT_DEPTH] = 8 + + for(var/t3_knowledge in tier3) + heretic_research_tree[t3_knowledge][HKT_NEXT] += neighbour_0.tier3 + heretic_research_tree[t3_knowledge][HKT_NEXT] += neighbour_1.tier3 + heretic_research_tree[t3_knowledge][HKT_ROUTE] = this_column.route + heretic_research_tree[t3_knowledge][HKT_UI_BGR] = this_column.ui_bgr + heretic_research_tree[t3_knowledge][HKT_DEPTH] = 10 + + //Everything below this line is considered to be a "main path" and not a side path + //Since we are handling the heretic research tree column by column this is required + if(this_column.abstract_parent_type != /datum/heretic_knowledge_tree_column/main) + continue + + var/datum/heretic_knowledge_tree_column/main/main_column = this_column + //vertical (one way) + heretic_research_tree[/datum/heretic_knowledge/spell/basic] += main_column.start + heretic_research_tree[main_column.start][HKT_NEXT] += main_column.grasp + heretic_research_tree[main_column.grasp][HKT_NEXT] += main_column.tier1 + //t1 handling + for(var/t1_knowledge in tier1) + heretic_research_tree[t1_knowledge][HKT_NEXT] += main_column.mark + + heretic_research_tree[main_column.mark][HKT_NEXT] += main_column.ritual_of_knowledge + heretic_research_tree[main_column.ritual_of_knowledge][HKT_NEXT] += main_column.unique_ability + heretic_research_tree[main_column.unique_ability][HKT_NEXT] += main_column.tier2 + //t2 handling + for(var/t2_knowledge in tier2) + heretic_research_tree[t2_knowledge][HKT_NEXT] += main_column.blade + + heretic_research_tree[main_column.blade][HKT_NEXT] += main_column.tier3 + //t3 handling + for(var/t3_knowledge in tier3) + heretic_research_tree[t3_knowledge][HKT_NEXT] += main_column.ascension + + //blacklist + heretic_research_tree[main_column.start][HKT_BAN] += (start_blacklist - main_column.start) + (asc_blacklist - main_column.ascension) + heretic_research_tree[main_column.grasp][HKT_BAN] += (grasp_blacklist - main_column.grasp) + heretic_research_tree[main_column.mark][HKT_BAN] += (mark_blacklist - main_column.mark) + heretic_research_tree[main_column.blade][HKT_BAN] += (blade_blacklist - main_column.blade) + + //route stuff + heretic_research_tree[main_column.start][HKT_ROUTE] = main_column.route + heretic_research_tree[main_column.grasp][HKT_ROUTE] = main_column.route + heretic_research_tree[main_column.mark][HKT_ROUTE] = main_column.route + heretic_research_tree[main_column.ritual_of_knowledge][HKT_ROUTE] = main_column.route + heretic_research_tree[main_column.unique_ability][HKT_ROUTE] = main_column.route + heretic_research_tree[main_column.blade][HKT_ROUTE] = main_column.route + heretic_research_tree[main_column.ascension][HKT_ROUTE] = main_column.route + + heretic_research_tree[main_column.start][HKT_UI_BGR] = main_column.ui_bgr + heretic_research_tree[main_column.grasp][HKT_UI_BGR] = main_column.ui_bgr + heretic_research_tree[main_column.mark][HKT_UI_BGR] = main_column.ui_bgr + heretic_research_tree[main_column.ritual_of_knowledge][HKT_UI_BGR] = main_column.ui_bgr + heretic_research_tree[main_column.unique_ability][HKT_UI_BGR] = main_column.ui_bgr + heretic_research_tree[main_column.blade][HKT_UI_BGR] = main_column.ui_bgr + heretic_research_tree[main_column.ascension][HKT_UI_BGR] = main_column.ui_bgr + //depth stuff + heretic_research_tree[main_column.start][HKT_DEPTH] = 2 + heretic_research_tree[main_column.grasp][HKT_DEPTH] = 3 + heretic_research_tree[main_column.mark][HKT_DEPTH] = 5 + heretic_research_tree[main_column.ritual_of_knowledge][HKT_DEPTH] = 6 + heretic_research_tree[main_column.unique_ability][HKT_DEPTH] = 7 + heretic_research_tree[main_column.blade][HKT_DEPTH] = 9 + heretic_research_tree[main_column.ascension][HKT_DEPTH] = 11 + + //Per path bullshit goes here \/\/\/ + for(var/t2_knowledge in tier2) + heretic_research_tree[t2_knowledge][HKT_NEXT] += /datum/heretic_knowledge/reroll_targets + + // If you want to do any custom bullshit put it here \/\/\/ + heretic_research_tree[/datum/heretic_knowledge/reroll_targets][HKT_ROUTE] = PATH_SIDE + heretic_research_tree[/datum/heretic_knowledge/reroll_targets][HKT_DEPTH] = 8 + + heretic_research_tree[/datum/heretic_knowledge/rifle][HKT_NEXT] += /datum/heretic_knowledge/rifle_ammo + heretic_research_tree[/datum/heretic_knowledge/rifle_ammo][HKT_ROUTE] = PATH_SIDE + heretic_research_tree[/datum/heretic_knowledge/rifle_ammo][HKT_DEPTH] = heretic_research_tree[/datum/heretic_knowledge/rifle][HKT_DEPTH] + + //and we're done + QDEL_LIST_ASSOC_VAL(paths) + return heretic_research_tree diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm index 040d4a43f63..e234224c22c 100644 --- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm @@ -1,44 +1,32 @@ -/** - * # The path of Ash. - * - * Goes as follows: - * - * Nightwatcher's Secret - * Grasp of Ash - * Ashen Passage - * > Sidepaths: - * Scorching Shark - * Ashen Eyes - * - * Mark of Ash - * Ritual of Knowledge - * Fire Blast - * Mask of Madness - * > Sidepaths: - * Space Phase - * Curse of Paralysis - * - * Fiery Blade - * Nightwatcher's Rebirth - * > Sidepaths: - * Ashen Ritual - * Eldritch Coin - * - * Ashlord's Rite - */ + +/datum/heretic_knowledge_tree_column/main/ash + neighbour_type_left = /datum/heretic_knowledge_tree_column/cosmic_to_ash + neighbour_type_right = /datum/heretic_knowledge_tree_column/ash_to_moon + + route = PATH_ASH + ui_bgr = "node_ash" + start = /datum/heretic_knowledge/limited_amount/starting/base_ash + grasp = /datum/heretic_knowledge/ashen_grasp + tier1 = /datum/heretic_knowledge/spell/ash_passage + mark = /datum/heretic_knowledge/mark/ash_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/ash + unique_ability = /datum/heretic_knowledge/spell/fire_blast + tier2 = /datum/heretic_knowledge/mad_mask + blade = /datum/heretic_knowledge/blade_upgrade/ash + tier3 = /datum/heretic_knowledge/spell/flame_birth + ascension = /datum/heretic_knowledge/ultimate/ash_final + /datum/heretic_knowledge/limited_amount/starting/base_ash name = "Nightwatcher's Secret" desc = "Opens up the Path of Ash to you. \ Allows you to transmute a match and a knife into an Ashen Blade. \ You can only create two at a time." gain_text = "The City Guard know their watch. If you ask them at night, they may tell you about the ashy lantern." - next_knowledge = list(/datum/heretic_knowledge/ashen_grasp) required_atoms = list( /obj/item/knife = 1, /obj/item/match = 1, ) result_atoms = list(/obj/item/melee/sickly_blade/ash) - route = PATH_ASH research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "ash_blade" @@ -47,10 +35,7 @@ desc = "Your Mansus Grasp will burn the eyes of the victim, damaging them and blurring their vision." gain_text = "The Nightwatcher was the first of them, his treason started it all. \ Their lantern, expired to ash - their watch, absent." - next_knowledge = list(/datum/heretic_knowledge/spell/ash_passage) cost = 1 - route = PATH_ASH - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_ash" @@ -77,15 +62,10 @@ name = "Ashen Passage" desc = "Grants you Ashen Passage, a spell that lets you phase out of reality and traverse a short distance, passing though any walls." gain_text = "He knew how to walk between the planes." - next_knowledge = list( - /datum/heretic_knowledge/mark/ash_mark, - /datum/heretic_knowledge/summon/fire_shark, - /datum/heretic_knowledge/medallion, - ) - spell_to_add = /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash + + action_to_add = /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash cost = 1 - route = PATH_ASH - depth = 4 + /datum/heretic_knowledge/mark/ash_mark name = "Mark of Ash" @@ -96,8 +76,6 @@ gain_text = "He was a very particular man, always watching in the dead of night. \ But in spite of his duty, he regularly tranced through the Manse with his blazing lantern held high. \ He shone brightly in the darkness, until the blaze begin to die." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/ash) - route = PATH_ASH mark_type = /datum/status_effect/eldritch/ash /datum/heretic_knowledge/mark/ash_mark/trigger_mark(mob/living/source, mob/living/target) @@ -112,8 +90,8 @@ grasp.build_all_button_icons() /datum/heretic_knowledge/knowledge_ritual/ash - next_knowledge = list(/datum/heretic_knowledge/spell/fire_blast) - route = PATH_ASH + + /datum/heretic_knowledge/spell/fire_blast name = "Volcano Blast" @@ -121,11 +99,8 @@ at a nearby enemy, setting them on fire and burning them. If they do not extinguish themselves, \ the beam will continue to another target." gain_text = "No fire was hot enough to rekindle them. No fire was bright enough to save them. No fire is eternal." - next_knowledge = list(/datum/heretic_knowledge/mad_mask) - spell_to_add = /datum/action/cooldown/spell/charged/beam/fire_blast + action_to_add = /datum/action/cooldown/spell/charged/beam/fire_blast cost = 1 - route = PATH_ASH - depth = 7 research_tree_icon_frame = 7 @@ -135,12 +110,6 @@ The mask instills fear into heathens who witness it, causing stamina damage, hallucinations, and insanity. \ It can also be forced onto a heathen, to make them unable to take it off..." gain_text = "The Nightwatcher was lost. That's what the Watch believed. Yet he walked the world, unnoticed by the masses." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/ash, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/spell/space_phase, - /datum/heretic_knowledge/curse/paralysis, - ) required_atoms = list( /obj/item/organ/liver = 1, /obj/item/melee/baton/security = 1, // Technically means a cattleprod is valid @@ -149,18 +118,16 @@ ) result_atoms = list(/obj/item/clothing/mask/madness_mask) cost = 1 - route = PATH_ASH research_tree_icon_path = 'icons/obj/clothing/masks.dmi' research_tree_icon_state = "mad_mask" - depth = 8 /datum/heretic_knowledge/blade_upgrade/ash name = "Fiery Blade" desc = "Your blade now lights enemies ablaze on attack." gain_text = "He returned, blade in hand, he swung and swung as the ash fell from the skies. \ His city, the people he swore to watch... and watch he did, as they all burnt to cinders." - next_knowledge = list(/datum/heretic_knowledge/spell/flame_birth) - route = PATH_ASH + + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_ash" @@ -178,15 +145,8 @@ If any victims afflicted are in critical condition, they will also instantly die." gain_text = "The fire was inescapable, and yet, life remained in his charred body. \ The Nightwatcher was a particular man, always watching." - next_knowledge = list( - /datum/heretic_knowledge/ultimate/ash_final, - /datum/heretic_knowledge/summon/ashy, - /datum/heretic_knowledge/eldritch_coin, - ) - spell_to_add = /datum/action/cooldown/spell/aoe/fiery_rebirth + action_to_add = /datum/action/cooldown/spell/aoe/fiery_rebirth cost = 1 - route = PATH_ASH - depth = 10 research_tree_icon_frame = 5 /datum/heretic_knowledge/ultimate/ash_final @@ -201,7 +161,7 @@ gain_text = "The Watch is dead, the Nightwatcher burned with it. Yet his fire burns evermore, \ for the Nightwatcher brought forth the rite to mankind! His gaze continues, as now I am one with the flames, \ WITNESS MY ASCENSION, THE ASHY LANTERN BLAZES ONCE MORE!" - route = PATH_ASH + ascension_achievement = /datum/award/achievement/misc/ash_ascension /// A static list of all traits we apply on ascension. var/static/list/traits_to_apply = list( diff --git a/code/modules/antagonists/heretic/knowledge/blade_lore.dm b/code/modules/antagonists/heretic/knowledge/blade_lore.dm index b708009d3fb..cd70bab1633 100644 --- a/code/modules/antagonists/heretic/knowledge/blade_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/blade_lore.dm @@ -1,48 +1,34 @@ -/** - * # The path of Blades. Stab stab. - * - * Goes as follows: - * - * The Cutting Edge - * Grasp of the Blade - * Dance of the Brand - * > Sidepaths: - * Shattered Risen - * Armorer's Ritual - * - * Mark of the Blade - * Ritual of Knowledge - * Realignment - * > Sidepaths: - * Lionhunter Rifle - * - * Stance of the Scarred Duelist - * > Sidepaths: - * Carving Knife - * Mawed Crucible - * - * Swift Blades - * Furious Steel - * > Sidepaths: - * Maid in the Mirror - * Rust Charge - * - * Maelstrom of Silver - */ + +/datum/heretic_knowledge_tree_column/main/blade + neighbour_type_left = /datum/heretic_knowledge_tree_column/void_to_blade + neighbour_type_right = /datum/heretic_knowledge_tree_column/blade_to_rust + + route = PATH_BLADE + ui_bgr = "node_blade" + + start = /datum/heretic_knowledge/limited_amount/starting/base_blade + grasp = /datum/heretic_knowledge/blade_grasp + tier1 = /datum/heretic_knowledge/blade_dance + mark = /datum/heretic_knowledge/mark/blade_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/blade + unique_ability = /datum/heretic_knowledge/spell/realignment + tier2 = /datum/heretic_knowledge/duel_stance + blade = /datum/heretic_knowledge/blade_upgrade/blade + tier3 = /datum/heretic_knowledge/spell/furious_steel + ascension = /datum/heretic_knowledge/ultimate/blade_final + /datum/heretic_knowledge/limited_amount/starting/base_blade name = "The Cutting Edge" desc = "Opens up the Path of Blades to you. \ Allows you to transmute a knife with one bar of silver or titanium to create a Sundered Blade. \ You can create up to four at a time." gain_text = "Our great ancestors forged swords and practiced sparring on the eve of great battles." - next_knowledge = list(/datum/heretic_knowledge/blade_grasp) required_atoms = list( /obj/item/knife = 1, list(/obj/item/stack/sheet/mineral/silver, /obj/item/stack/sheet/mineral/titanium) = 1, ) result_atoms = list(/obj/item/melee/sickly_blade/dark) limit = 4 // It's the blade path, it's a given - route = PATH_BLADE research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "dark_blade" @@ -51,10 +37,7 @@ desc = "Your Mansus Grasp will cause a short stun when used on someone lying down or facing away from you." gain_text = "The story of the footsoldier has been told since antiquity. It is one of blood and valor, \ and is championed by sword, steel and silver." - next_knowledge = list(/datum/heretic_knowledge/blade_dance) cost = 1 - route = PATH_BLADE - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_blade" @@ -85,14 +68,7 @@ towards your attacker. This effect can only trigger once every 20 seconds." gain_text = "The footsoldier was known to be a fearsome duelist. \ Their general quickly appointed them as their personal Champion." - next_knowledge = list( - /datum/heretic_knowledge/limited_amount/risen_corpse, - /datum/heretic_knowledge/mark/blade_mark, - /datum/heretic_knowledge/armor, - ) cost = 1 - route = PATH_BLADE - depth = 4 research_tree_icon_path = 'icons/mob/actions/actions_ecult.dmi' research_tree_icon_state = "shatter" /// Whether the counter-attack is ready or not. @@ -183,8 +159,6 @@ The knife will block any attack directed towards you, but is consumed on use." gain_text = "His general wished to end the war, but the Champion knew there could be no life without death. \ He would slay the coward himself, and anyone who tried to run." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/blade) - route = PATH_BLADE mark_type = /datum/status_effect/eldritch/blade /datum/heretic_knowledge/mark/blade_mark/create_mark(mob/living/source, mob/living/target) @@ -202,8 +176,8 @@ source.apply_status_effect(/datum/status_effect/protective_blades, 60 SECONDS, 1, 20, 0 SECONDS) /datum/heretic_knowledge/knowledge_ritual/blade - next_knowledge = list(/datum/heretic_knowledge/spell/realignment) - route = PATH_BLADE + + /datum/heretic_knowledge/spell/realignment name = "Realignment" @@ -211,11 +185,9 @@ During this process, you will rapidly regenerate stamina and quickly recover from stuns, however, you will be unable to attack. \ This spell can be cast in rapid succession, but doing so will increase the cooldown." gain_text = "In the flurry of death, he found peace within himself. Despite insurmountable odds, he forged on." - next_knowledge = list(/datum/heretic_knowledge/duel_stance) - spell_to_add = /datum/action/cooldown/spell/realignment + action_to_add = /datum/action/cooldown/spell/realignment cost = 1 - route = PATH_BLADE - depth = 7 + /// The amount of blood flow reduced per level of severity of gained bleeding wounds for Stance of the Torn Champion. #define BLOOD_FLOW_PER_SEVEIRTY -1 @@ -227,16 +199,7 @@ you gain increased resistance to gaining wounds and resistance to batons." gain_text = "In time, it was he who stood alone among the bodies of his former comrades, awash in blood, none of it his own. \ He was without rival, equal, or purpose." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/blade, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/rune_carver, - /datum/heretic_knowledge/crucible, - /datum/heretic_knowledge/rifle, - ) cost = 1 - route = PATH_BLADE - depth = 8 research_tree_icon_path = 'icons/effects/blood.dmi' research_tree_icon_state = "suitblood" research_tree_icon_dir = SOUTH @@ -298,8 +261,6 @@ You are able to infuse your mansus grasp directly into your blades, and your blades are more effective against structures." gain_text = "I found him cleaved in twain, halves locked in a duel without end; \ a flurry of blades, neither hitting their mark, for the Champion was indomitable." - next_knowledge = list(/datum/heretic_knowledge/spell/furious_steel) - route = PATH_BLADE research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_blade" /// How much force do we apply to the offhand? @@ -403,15 +364,8 @@ at a target, dealing damage and causing bleeding." gain_text = "Without thinking, I took the knife of a fallen soldier and threw with all my might. My aim was true! \ The Torn Champion smiled at their first taste of agony, and with a nod, their blades became my own." - next_knowledge = list( - /datum/heretic_knowledge/summon/maid_in_mirror, - /datum/heretic_knowledge/ultimate/blade_final, - /datum/heretic_knowledge/spell/rust_charge, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/projectile/furious_steel + action_to_add = /datum/action/cooldown/spell/pointed/projectile/furious_steel cost = 1 - route = PATH_BLADE - depth = 10 /datum/heretic_knowledge/ultimate/blade_final name = "Maelstrom of Silver" @@ -424,7 +378,7 @@ Your Sundered Blades deal bonus damage and heal you on attack for a portion of the damage dealt." gain_text = "The Torn Champion is freed! I will become the blade reunited, and with my greater ambition, \ I AM UNMATCHED! A STORM OF STEEL AND SILVER IS UPON US! WITNESS MY ASCENSION!" - route = PATH_BLADE + ascension_achievement = /datum/award/achievement/misc/blade_ascension /datum/heretic_knowledge/ultimate/blade_final/is_valid_sacrifice(mob/living/carbon/human/sacrifice) @@ -444,7 +398,7 @@ ) ADD_TRAIT(user, TRAIT_NEVER_WOUNDED, name) RegisterSignal(user, COMSIG_HERETIC_BLADE_ATTACK, PROC_REF(on_eldritch_blade)) - user.apply_status_effect(/datum/status_effect/protective_blades/recharging, null, 8, 30, 0.25 SECONDS, 1 MINUTES) + user.apply_status_effect(/datum/status_effect/protective_blades/recharging, null, 8, 30, 0.25 SECONDS, /obj/effect/floating_blade, 1 MINUTES) user.add_stun_absorption( source = name, message = span_warning("%EFFECT_OWNER throws off the stun!"), diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm index 98e457c8d8c..cb2ac746912 100644 --- a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm @@ -1,43 +1,33 @@ -/** - * # The path of Cosmos. - * - * Goes as follows: - * - * Eternal Gate - * Grasp of Cosmos - * Cosmic Runes - * > Sidepaths: - * Priest's Ritual - * Scorching Shark - * - * Mark of Cosmos - * Ritual of Knowledge - * Star Touch - * Star Blast - * > Sidepaths: - * Curse of Corrosion - * Space Phase - * - * Cosmic Blade - * Cosmic Expansion - * > Sidepaths: - * Eldritch Coin - * - * Creators's Gift - */ + +/datum/heretic_knowledge_tree_column/main/cosmic + neighbour_type_left = /datum/heretic_knowledge_tree_column/rust_to_cosmic + neighbour_type_right = /datum/heretic_knowledge_tree_column/cosmic_to_ash + + route = PATH_COSMIC + ui_bgr = "node_cosmos" + + start = /datum/heretic_knowledge/limited_amount/starting/base_cosmic + grasp = /datum/heretic_knowledge/cosmic_grasp + tier1 = /datum/heretic_knowledge/spell/cosmic_runes + mark = /datum/heretic_knowledge/mark/cosmic_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/cosmic + unique_ability = /datum/heretic_knowledge/spell/star_touch + tier2 = /datum/heretic_knowledge/spell/star_blast + blade = /datum/heretic_knowledge/blade_upgrade/cosmic + tier3 = /datum/heretic_knowledge/spell/cosmic_expansion + ascension = /datum/heretic_knowledge/ultimate/cosmic_final + /datum/heretic_knowledge/limited_amount/starting/base_cosmic name = "Eternal Gate" desc = "Opens up the Path of Cosmos to you. \ Allows you to transmute a sheet of plasma and a knife into an Cosmic Blade. \ You can only create two at a time." gain_text = "A nebula appeared in the sky, its infernal birth shone upon me. This was the start of a great transcendence." - next_knowledge = list(/datum/heretic_knowledge/cosmic_grasp) required_atoms = list( /obj/item/knife = 1, /obj/item/stack/sheet/mineral/plasma = 1, ) result_atoms = list(/obj/item/melee/sickly_blade/cosmic) - route = PATH_COSMIC research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "cosmic_blade" @@ -47,10 +37,7 @@ People with a star mark can not pass cosmic fields." gain_text = "Some stars dimmed, others' magnitude increased. \ With newfound strength I could channel the nebula's power into myself." - next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_runes) cost = 1 - route = PATH_COSMIC - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_cosmos" @@ -75,15 +62,9 @@ However, people with a star mark will get transported along with another person using the rune." gain_text = "The distant stars crept into my dreams, roaring and screaming without reason. \ I spoke, and heard my own words echoed back." - next_knowledge = list( - /datum/heretic_knowledge/summon/fire_shark, - /datum/heretic_knowledge/mark/cosmic_mark, - /datum/heretic_knowledge/essence, - ) - spell_to_add = /datum/action/cooldown/spell/cosmic_rune + action_to_add = /datum/action/cooldown/spell/cosmic_rune cost = 1 - route = PATH_COSMIC - depth = 4 + /datum/heretic_knowledge/mark/cosmic_mark name = "Mark of Cosmos" @@ -93,13 +74,9 @@ They will then be paralyzed for 2 seconds." gain_text = "The Beast now whispered to me occasionally, only small tidbits of their circumstances. \ I can help them, I have to help them." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/cosmic) - route = PATH_COSMIC mark_type = /datum/status_effect/eldritch/cosmic /datum/heretic_knowledge/knowledge_ritual/cosmic - next_knowledge = list(/datum/heretic_knowledge/spell/star_touch) - route = PATH_COSMIC /datum/heretic_knowledge/spell/star_touch name = "Star Touch" @@ -109,28 +86,16 @@ The beam lasts a minute, until the beam is obstructed or until a new target has been found." gain_text = "After waking in a cold sweat I felt a palm on my scalp, a sigil burned onto me. \ My veins now emitted a strange purple glow, the Beast knows I will surpass its expectations." - next_knowledge = list(/datum/heretic_knowledge/spell/star_blast) - spell_to_add = /datum/action/cooldown/spell/touch/star_touch + action_to_add = /datum/action/cooldown/spell/touch/star_touch cost = 1 - route = PATH_COSMIC - depth = 7 /datum/heretic_knowledge/spell/star_blast name = "Star Blast" desc = "Fires a projectile that moves very slowly, raising a short-lived wall of cosmic fields where it goes. \ Anyone hit by the projectile will receive burn damage, a knockdown, and give people in a three tile range a star mark." gain_text = "The Beast was behind me now at all times, with each sacrifice words of affirmation coursed through me." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/cosmic, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/curse/corrosion, - /datum/heretic_knowledge/summon/rusty, - /datum/heretic_knowledge/spell/space_phase, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/projectile/star_blast + action_to_add = /datum/action/cooldown/spell/pointed/projectile/star_blast cost = 1 - route = PATH_COSMIC - depth = 8 /datum/heretic_knowledge/blade_upgrade/cosmic name = "Cosmic Blade" @@ -141,8 +106,6 @@ a cosmic trail and increase your combo timer up to ten seconds." gain_text = "The Beast took my blades in their hand, I kneeled and felt a sharp pain. \ The blades now glistened with fragmented power. I fell to the ground and wept at the beast's feet." - next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_expansion) - route = PATH_COSMIC research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_cosmos" /// Storage for the second target. @@ -236,14 +199,8 @@ desc = "Grants you Cosmic Expansion, a spell that creates a 3x3 area of cosmic fields around you. \ Nearby beings will also receive a star mark." gain_text = "The ground now shook beneath me. The Beast inhabited me, and their voice was intoxicating." - next_knowledge = list( - /datum/heretic_knowledge/ultimate/cosmic_final, - /datum/heretic_knowledge/eldritch_coin, - ) - spell_to_add = /datum/action/cooldown/spell/conjure/cosmic_expansion + action_to_add = /datum/action/cooldown/spell/conjure/cosmic_expansion cost = 1 - route = PATH_COSMIC - depth = 10 /datum/heretic_knowledge/ultimate/cosmic_final name = "Creators's Gift" @@ -260,7 +217,7 @@ I clung on to them, they would protect me, and I would protect it. \ I closed my eyes with my head laid against their form. I was safe. \ WITNESS MY ASCENSION!" - route = PATH_COSMIC + ascension_achievement = /datum/award/achievement/misc/cosmic_ascension /// A static list of command we can use with our mob. var/static/list/star_gazer_commands = list( diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm index afbbe0f30dd..39fb7fcc84f 100644 --- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm @@ -3,47 +3,36 @@ /// The max amount of health a voiceless dead has. #define MUTE_MAX_HEALTH 50 -/** - * # The path of Flesh. - * - * Goes as follows: - * - * Principle of Hunger - * Grasp of Flesh - * Imperfect Ritual - * > Sidepaths: - * Void Cloak - * - * Mark of Flesh - * Ritual of Knowledge - * Flesh Surgery - * Raw Ritual - * > Sidepaths: - * Blood Siphon - * Opening Blast - * - * Bleeding Steel - * Lonely Ritual - * > Sidepaths: - * Cleave - * Aptera Vulnera - * - * Priest's Final Hymn - */ +/datum/heretic_knowledge_tree_column/main/flesh + neighbour_type_left = /datum/heretic_knowledge_tree_column/lock_to_flesh + neighbour_type_right = /datum/heretic_knowledge_tree_column/flesh_to_void + + route = PATH_FLESH + ui_bgr = "node_flesh" + + start = /datum/heretic_knowledge/limited_amount/starting/base_flesh + grasp = /datum/heretic_knowledge/limited_amount/flesh_grasp + tier1 = /datum/heretic_knowledge/limited_amount/flesh_ghoul + mark = /datum/heretic_knowledge/mark/flesh_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/flesh + unique_ability = /datum/heretic_knowledge/spell/flesh_surgery + tier2 = /datum/heretic_knowledge/summon/raw_prophet + blade = /datum/heretic_knowledge/blade_upgrade/flesh + tier3 = /datum/heretic_knowledge/summon/stalker + ascension = /datum/heretic_knowledge/ultimate/flesh_final + /datum/heretic_knowledge/limited_amount/starting/base_flesh name = "Principle of Hunger" desc = "Opens up the Path of Flesh to you. \ Allows you to transmute a knife and a pool of blood into a Bloody Blade. \ You can only create three at a time." gain_text = "Hundreds of us starved, but not me... I found strength in my greed." - next_knowledge = list(/datum/heretic_knowledge/limited_amount/flesh_grasp) required_atoms = list( /obj/item/knife = 1, /obj/effect/decal/cleanable/blood = 1, ) result_atoms = list(/obj/item/melee/sickly_blade/flesh) limit = 3 // Bumped up so they can arm up their ghouls too. - route = PATH_FLESH research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "flesh_blade" @@ -62,11 +51,11 @@ Ghouls have only 25 health and look like husks to the heathens' eyes, but can use Bloody Blades effectively. \ You can only create one at a time by this method." gain_text = "My new found desires drove me to greater and greater heights." - next_knowledge = list(/datum/heretic_knowledge/limited_amount/flesh_ghoul) + limit = 1 cost = 1 - route = PATH_FLESH - depth = 3 + + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_flesh" @@ -131,21 +120,16 @@ Voiceless Dead are mute ghouls and only have 50 health, but can use Bloody Blades effectively. \ You can only create two at a time." gain_text = "I found notes of a dark ritual, unfinished... yet still, I pushed forward." - next_knowledge = list( - /datum/heretic_knowledge/mark/flesh_mark, - /datum/heretic_knowledge/void_cloak, - ) required_atoms = list( /mob/living/carbon/human = 1, /obj/item/food/grown/poppy = 1, ) limit = 2 cost = 1 - route = PATH_FLESH research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "ghoul_voiceless" - depth = 4 + /datum/heretic_knowledge/limited_amount/flesh_ghoul/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) . = ..() @@ -217,13 +201,11 @@ desc = "Your Mansus Grasp now applies the Mark of Flesh. The mark is triggered from an attack with your Bloody Blade. \ When triggered, the victim begins to bleed significantly." gain_text = "That's when I saw them, the marked ones. They were out of reach. They screamed, and screamed." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/flesh) - route = PATH_FLESH + + mark_type = /datum/status_effect/eldritch/flesh /datum/heretic_knowledge/knowledge_ritual/flesh - next_knowledge = list(/datum/heretic_knowledge/spell/flesh_surgery) - route = PATH_FLESH /datum/heretic_knowledge/spell/flesh_surgery name = "Knitting of Flesh" @@ -232,11 +214,8 @@ This spell also allows you to heal your minions and summons, or restore failing organs to acceptable status." gain_text = "But they were not out of my reach for long. With every step, the screams grew, until at last \ I learned that they could be silenced." - next_knowledge = list(/datum/heretic_knowledge/summon/raw_prophet) - spell_to_add = /datum/action/cooldown/spell/touch/flesh_surgery + action_to_add = /datum/action/cooldown/spell/touch/flesh_surgery cost = 1 - route = PATH_FLESH - depth = 7 /datum/heretic_knowledge/summon/raw_prophet name = "Raw Ritual" @@ -245,12 +224,6 @@ the ability to link minds to communicate with ease, but are very fragile and weak in combat." gain_text = "I could not continue alone. I was able to summon The Uncanny Man to help me see more. \ The screams... once constant, now silenced by their wretched appearance. Nothing was out of reach." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/flesh, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/spell/blood_siphon, - /datum/heretic_knowledge/spell/opening_blast, - ) required_atoms = list( /obj/item/organ/eyes = 1, /obj/effect/decal/cleanable/blood = 1, @@ -258,17 +231,14 @@ ) mob_to_summon = /mob/living/basic/heretic_summon/raw_prophet cost = 1 - route = PATH_FLESH poll_ignore_define = POLL_IGNORE_RAW_PROPHET - depth = 8 + /datum/heretic_knowledge/blade_upgrade/flesh name = "Bleeding Steel" desc = "Your Bloody Blade now causes enemies to bleed heavily on attack." gain_text = "The Uncanny Man was not alone. They led me to the Marshal. \ I finally began to understand. And then, blood rained from the heavens." - next_knowledge = list(/datum/heretic_knowledge/summon/stalker) - route = PATH_FLESH research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_flesh" ///What type of wound do we apply on hit @@ -289,11 +259,7 @@ Stalkers can jaunt, release EMPs, shapeshift into animals or automatons, and are strong in combat." gain_text = "I was able to combine my greed and desires to summon an eldritch beast I had never seen before. \ An ever shapeshifting mass of flesh, it knew well my goals. The Marshal approved." - next_knowledge = list( - /datum/heretic_knowledge/ultimate/flesh_final, - /datum/heretic_knowledge/spell/apetra_vulnera, - /datum/heretic_knowledge/spell/cleave, - ) + required_atoms = list( /obj/item/organ/tail = 1, /obj/item/organ/stomach = 1, @@ -303,9 +269,9 @@ ) mob_to_summon = /mob/living/basic/heretic_summon/stalker cost = 1 - route = PATH_FLESH + poll_ignore_define = POLL_IGNORE_STALKER - depth = 10 + /datum/heretic_knowledge/ultimate/flesh_final name = "Priest's Final Hymn" @@ -321,7 +287,6 @@ Men of this world, hear me, for the time has come! The Marshal guides my army! \ Reality will bend to THE LORD OF THE NIGHT or be unraveled! WITNESS MY ASCENSION!" required_atoms = list(/mob/living/carbon/human = 4) - route = PATH_FLESH ascension_achievement = /datum/award/achievement/misc/flesh_ascension /datum/heretic_knowledge/ultimate/flesh_final/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) diff --git a/code/modules/antagonists/heretic/knowledge/general_side.dm b/code/modules/antagonists/heretic/knowledge/general_side.dm index 10ff108c5f8..e2f445cb8a6 100644 --- a/code/modules/antagonists/heretic/knowledge/general_side.dm +++ b/code/modules/antagonists/heretic/knowledge/general_side.dm @@ -11,8 +11,6 @@ /obj/item/clothing/under = 1, ) cost = 1 - route = PATH_SIDE - depth = 8 research_tree_icon_path = 'icons/mob/actions/actions_animal.dmi' research_tree_icon_state = "gaze" diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm index 6a6c18eb3e2..b7173736b7e 100644 --- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm @@ -1,29 +1,22 @@ -/** - * # The path of Lock. - * - * Goes as follows: - * - * A Steward's Secret - * Grasp of Lock - * Key Keeper’s Burden - * > Sidepaths: - * Mindgate - * Concierge's Rite - * Mark Of Lock - * Ritual of Knowledge - * Burglar's Finesse - * > Sidepaths: - * Opening Blast - * Unfathomable Curio - * Unsealed arts - * - * Opening Blade - * Caretaker’s Last Refuge - * > Sidepaths: - * Apetra Vulnera - * - * Unlock the Labyrinth - */ + +/datum/heretic_knowledge_tree_column/main/lock + neighbour_type_left = /datum/heretic_knowledge_tree_column/moon_to_lock + neighbour_type_right = /datum/heretic_knowledge_tree_column/lock_to_flesh + + route = PATH_LOCK + ui_bgr = "node_lock" + + start = /datum/heretic_knowledge/limited_amount/starting/base_knock + grasp = /datum/heretic_knowledge/lock_grasp + tier1 = /datum/heretic_knowledge/key_ring + mark = /datum/heretic_knowledge/mark/lock_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/lock + unique_ability = /datum/heretic_knowledge/limited_amount/concierge_rite + tier2 = /datum/heretic_knowledge/spell/burglar_finesse + blade = /datum/heretic_knowledge/blade_upgrade/flesh/lock + tier3 = /datum/heretic_knowledge/spell/caretaker_refuge + ascension = /datum/heretic_knowledge/ultimate/lock_final + /datum/heretic_knowledge/limited_amount/starting/base_knock name = "A Steward's Secret" desc = "Opens up the Path of Lock to you. \ @@ -31,14 +24,12 @@ You can only create two at a time and they function as fast crowbars. \ In addition, they can fit into utility belts." gain_text = "The Locked Labyrinth leads to freedom. But only the trapped Stewards know the correct path." - next_knowledge = list(/datum/heretic_knowledge/lock_grasp) required_atoms = list( /obj/item/knife = 1, /obj/item/crowbar = 1, ) result_atoms = list(/obj/item/melee/sickly_blade/lock) limit = 2 - route = PATH_LOCK research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "key_blade" @@ -48,10 +39,7 @@ DNA locks on mechs will be removed, and any pilot will be ejected. Works on consoles. \ Makes a distinctive knocking sound on use." gain_text = "Nothing may remain closed from my touch." - next_knowledge = list(/datum/heretic_knowledge/key_ring) cost = 1 - route = PATH_LOCK - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_lock" @@ -112,15 +100,10 @@ /obj/item/card/id = 1, ) result_atoms = list(/obj/item/card/id/advanced/heretic) - next_knowledge = list( - /datum/heretic_knowledge/mark/lock_mark, - /datum/heretic_knowledge/spell/mind_gate, - ) cost = 1 - route = PATH_LOCK research_tree_icon_path = 'icons/obj/card.dmi' research_tree_icon_state = "card_gold" - depth = 4 + /datum/heretic_knowledge/mark/lock_mark name = "Mark of Lock" @@ -128,13 +111,9 @@ Attack a marked person to bar them from all passages for the duration of the mark. \ This will make it so that they have no access whatsoever, even public access doors will reject them." gain_text = "The Gatekeeper was a corrupt Steward. She hindered her fellows for her own twisted amusement." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/lock) - route = PATH_LOCK mark_type = /datum/status_effect/eldritch/lock /datum/heretic_knowledge/knowledge_ritual/lock - next_knowledge = list(/datum/heretic_knowledge/limited_amount/concierge_rite) - route = PATH_LOCK /datum/heretic_knowledge/limited_amount/concierge_rite // item that creates 3 max at a time heretic only barriers, probably should limit to 1 only, holy people can also pass name = "Concierge's Rite" @@ -147,36 +126,23 @@ /obj/item/multitool = 1, ) result_atoms = list(/obj/item/heretic_labyrinth_handbook) - next_knowledge = list(/datum/heretic_knowledge/spell/burglar_finesse) cost = 1 - route = PATH_LOCK research_tree_icon_path = 'icons/obj/service/library.dmi' research_tree_icon_state = "heretichandbook" - depth = 7 /datum/heretic_knowledge/spell/burglar_finesse name = "Burglar's Finesse" desc = "Grants you Burglar's Finesse, a single-target spell \ that puts a random item from the victims backpack into your hand." gain_text = "Consorting with Burglar spirits is frowned upon, but a Steward will always want to learn about new doors." - next_knowledge = list( - /datum/heretic_knowledge/spell/opening_blast, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/blade_upgrade/flesh/lock, - /datum/heretic_knowledge/unfathomable_curio, - /datum/heretic_knowledge/painting, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/burglar_finesse + + action_to_add = /datum/action/cooldown/spell/pointed/burglar_finesse cost = 1 - route = PATH_LOCK - depth = 8 /datum/heretic_knowledge/blade_upgrade/flesh/lock //basically a chance-based weeping avulsion version of the former name = "Opening Blade" desc = "Your blade has a chance to cause a weeping avulsion on attack." gain_text = "The Pilgrim-Surgeon was not an Steward. Nonetheless, its blades and sutures proved a match for their keys." - next_knowledge = list(/datum/heretic_knowledge/spell/caretaker_refuge) - route = PATH_LOCK wound_type = /datum/wound/slash/flesh/critical research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_lock" @@ -192,14 +158,8 @@ While in refuge, you cannot use your hands or spells, and you are immune to slowdown. \ You are invincible but unable to harm anything. Cancelled by being hit with an anti-magic item." gain_text = "Jealously, the Guard and the Hound hunted me. But I unlocked my form, and was but a haze, untouchable." - next_knowledge = list( - /datum/heretic_knowledge/ultimate/lock_final, - /datum/heretic_knowledge/spell/apetra_vulnera, - ) - route = PATH_LOCK - spell_to_add = /datum/action/cooldown/spell/caretaker + action_to_add = /datum/action/cooldown/spell/caretaker cost = 1 - depth = 10 /datum/heretic_knowledge/ultimate/lock_final name = "Unlock the Labyrinth" @@ -215,7 +175,6 @@ My foes were the Locks and my blades were the Key! \ The Labyrinth will be Locked no more, and freedom will be ours! WITNESS US!" required_atoms = list(/mob/living/carbon/human = 3) - route = PATH_LOCK ascension_achievement = /datum/award/achievement/misc/lock_ascension /datum/heretic_knowledge/ultimate/lock_final/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm index a18ed25f8d5..ba00fd9c4a4 100644 --- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm @@ -1,59 +1,45 @@ -/** - * # The path of Moon. - * - * Goes as follows: - * - * Moonlight Troupe - * Grasp of Lunacy - * Smile of the moon - * > Sidepaths: - * Mind Gate - * Ashen Eyes - * - * Mark of Moon - * Ritual of Knowledge - * Lunar Parade - * Moonlight Amulet - * > Sidepaths: - * Curse of Paralasys - * Unfathomable Curio - * Unsealed Arts - * - * Moonlight blade - * Ringleaders Rise - * > Sidepaths: - * Ashen Ritual - * - * Last Act - */ + +/datum/heretic_knowledge_tree_column/main/moon + neighbour_type_left = /datum/heretic_knowledge_tree_column/ash_to_moon + neighbour_type_right = /datum/heretic_knowledge_tree_column/moon_to_lock + + route = PATH_MOON + ui_bgr = "node_moon" + + start = /datum/heretic_knowledge/limited_amount/starting/base_moon + grasp = /datum/heretic_knowledge/moon_grasp + tier1 = /datum/heretic_knowledge/spell/moon_smile + mark = /datum/heretic_knowledge/mark/moon_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/moon + unique_ability = /datum/heretic_knowledge/spell/moon_parade + tier2 = /datum/heretic_knowledge/moon_amulet + blade = /datum/heretic_knowledge/blade_upgrade/moon + tier3 = /datum/heretic_knowledge/spell/moon_ringleader + ascension = /datum/heretic_knowledge/ultimate/moon_final + /datum/heretic_knowledge/limited_amount/starting/base_moon name = "Moonlight Troupe" desc = "Opens up the Path of Moon to you. \ Allows you to transmute 2 sheets of iron and a knife into an Lunar Blade. \ You can only create two at a time." gain_text = "Under the light of the moon the laughter echoes." - next_knowledge = list(/datum/heretic_knowledge/moon_grasp) required_atoms = list( /obj/item/knife = 1, /obj/item/stack/sheet/iron = 2, ) result_atoms = list(/obj/item/melee/sickly_blade/moon) - route = PATH_MOON research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "moon_blade" -/datum/heretic_knowledge/base_moon/on_gain(mob/user, datum/antagonist/heretic/our_heretic) - add_traits(user ,TRAIT_EMPATH, REF(src)) +/datum/heretic_knowledge/limited_amount/starting/base_moon/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + ADD_TRAIT(user, TRAIT_EMPATH, REF(src)) /datum/heretic_knowledge/moon_grasp name = "Grasp of Lunacy" desc = "Your Mansus Grasp will cause your victims to hallucinate everyone as lunar mass, \ and hides your identity for a short duration." gain_text = "The troupe on the side of the moon showed me truth, and I took it." - next_knowledge = list(/datum/heretic_knowledge/spell/moon_smile) cost = 1 - route = PATH_MOON - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_moon" @@ -83,15 +69,9 @@ desc = "Grants you Smile of the moon, a ranged spell muting, blinding, deafening and knocking down the target for a\ duration based on their sanity." gain_text = "The moon smiles upon us all and those who see its true side can bring its joy." - next_knowledge = list( - /datum/heretic_knowledge/mark/moon_mark, - /datum/heretic_knowledge/medallion, - /datum/heretic_knowledge/spell/mind_gate, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/moon_smile + + action_to_add = /datum/action/cooldown/spell/pointed/moon_smile cost = 1 - route = PATH_MOON - depth = 4 /datum/heretic_knowledge/mark/moon_mark name = "Mark of Moon" @@ -100,25 +80,17 @@ gain_text = "The troupe on the moon would dance all day long \ and in that dance the moon would smile upon us \ but when the night came its smile would dull forced to gaze on the earth." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/moon) - route = PATH_MOON mark_type = /datum/status_effect/eldritch/moon /datum/heretic_knowledge/knowledge_ritual/moon - next_knowledge = list(/datum/heretic_knowledge/spell/moon_parade) - route = PATH_MOON /datum/heretic_knowledge/spell/moon_parade name = "Lunar Parade" desc = "Grants you Lunar Parade, a spell that - after a short charge - sends a carnival forward \ when hitting someone they are forced to join the parade and suffer hallucinations." gain_text = "The music like a reflection of the soul compelled them, like moths to a flame they followed" - next_knowledge = list(/datum/heretic_knowledge/moon_amulet) - spell_to_add = /datum/action/cooldown/spell/pointed/projectile/moon_parade + action_to_add = /datum/action/cooldown/spell/pointed/projectile/moon_parade cost = 1 - route = PATH_MOON - depth = 7 - /datum/heretic_knowledge/moon_amulet name = "Moonlight Amulet" @@ -126,13 +98,7 @@ If the item is used on someone with low sanity they go berserk attacking everyone, \ if their sanity isn't low enough it decreases their mood." gain_text = "At the head of the parade he stood, the moon condensed into one mass, a reflection of the soul." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/moon, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/unfathomable_curio, - /datum/heretic_knowledge/curse/paralysis, - /datum/heretic_knowledge/painting, - ) + required_atoms = list( /obj/item/organ/heart = 1, /obj/item/stack/sheet/glass = 2, @@ -140,8 +106,8 @@ ) result_atoms = list(/obj/item/clothing/neck/heretic_focus/moon_amulet) cost = 1 - route = PATH_MOON - depth = 8 + + research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "moon_amulette" research_tree_icon_frame = 9 @@ -150,8 +116,8 @@ name = "Moonlight Blade" desc = "Your blade now deals brain damage, causes random hallucinations and does sanity damage." gain_text = "His wit was sharp as a blade, cutting through the lie to bring us joy." - next_knowledge = list(/datum/heretic_knowledge/spell/moon_ringleader) - route = PATH_MOON + + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_moon" @@ -177,14 +143,11 @@ If their sanity is low enough this turns them insane, the spell then halves their sanity." gain_text = "I grabbed his hand and we rose, those who saw the truth rose with us. \ The ringleader pointed up and the dim light of truth illuminated us further." - next_knowledge = list( - /datum/heretic_knowledge/ultimate/moon_final, - /datum/heretic_knowledge/summon/ashy, - ) - spell_to_add = /datum/action/cooldown/spell/aoe/moon_ringleader + + action_to_add = /datum/action/cooldown/spell/aoe/moon_ringleader cost = 1 - route = PATH_MOON - depth = 10 + + research_tree_icon_frame = 5 /datum/heretic_knowledge/ultimate/moon_final @@ -197,7 +160,7 @@ gain_text = "We dived down towards the crowd, his soul splitting off in search of greater venture \ for where the Ringleader had started the parade, I shall continue it unto the suns demise \ WITNESS MY ASCENSION, THE MOON SMILES ONCE MORE AND FOREVER MORE IT SHALL!" - route = PATH_MOON + ascension_achievement = /datum/award/achievement/misc/moon_ascension /datum/heretic_knowledge/ultimate/moon_final/is_valid_sacrifice(mob/living/sacrifice) diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm index a44ce82e6fb..c122aff98d7 100644 --- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm @@ -1,47 +1,33 @@ -/** - * # The path of Rust. - * - * Goes as follows: - * - * Blacksmith's Tale - * Grasp of Rust - * Leeching Walk - * > Sidepaths: - * Priest's Ritual - * Armorer's Ritual - * - * Mark of Rust - * Ritual of Knowledge - * Rust Construction - * > Sidepaths: - * Lionhunter Rifle - * - * Aggressive Spread - * > Sidepaths: - * Curse of Corrosion - * Mawed Crucible - * - * Toxic Blade - * Entropic Plume - * > Sidepaths: - * Rusted Ritual - * Rust Charge - * - * Rustbringer's Oath - */ + +/datum/heretic_knowledge_tree_column/main/rust + neighbour_type_left = /datum/heretic_knowledge_tree_column/blade_to_rust + neighbour_type_right = /datum/heretic_knowledge_tree_column/rust_to_cosmic + + route = PATH_RUST + ui_bgr = "node_rust" + + start = /datum/heretic_knowledge/limited_amount/starting/base_rust + grasp = /datum/heretic_knowledge/rust_fist + tier1 = /datum/heretic_knowledge/rust_regen + mark = /datum/heretic_knowledge/mark/rust_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/rust + unique_ability = /datum/heretic_knowledge/spell/rust_construction + tier2 = /datum/heretic_knowledge/spell/area_conversion + blade = /datum/heretic_knowledge/blade_upgrade/rust + tier3 = /datum/heretic_knowledge/spell/entropic_plume + ascension = /datum/heretic_knowledge/ultimate/rust_final + /datum/heretic_knowledge/limited_amount/starting/base_rust name = "Blacksmith's Tale" desc = "Opens up the Path of Rust to you. \ Allows you to transmute a knife with any trash item into a Rusty Blade. \ You can only create two at a time." gain_text = "\"Let me tell you a story\", said the Blacksmith, as he gazed deep into his rusty blade." - next_knowledge = list(/datum/heretic_knowledge/rust_fist) required_atoms = list( /obj/item/knife = 1, /obj/item/trash = 1, ) result_atoms = list(/obj/item/melee/sickly_blade/rust) - route = PATH_RUST research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "rust_blade" @@ -51,10 +37,7 @@ Already rusted surfaces are destroyed. Surfaces and structures can only be rusted by using Right-Click. \ Allows you to rust basic iron walls and floors." gain_text = "On the ceiling of the Mansus, rust grows as moss does on a stone." - next_knowledge = list(/datum/heretic_knowledge/rust_regen) cost = 1 - route = PATH_RUST - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_rust" @@ -90,17 +73,10 @@ name = "Leeching Walk" desc = "Grants you passive healing and resistance to batons while standing over rust." gain_text = "The speed was unparalleled, the strength unnatural. The Blacksmith was smiling." - next_knowledge = list( - /datum/heretic_knowledge/mark/rust_mark, - /datum/heretic_knowledge/armor, - /datum/heretic_knowledge/essence, - /datum/heretic_knowledge/entropy_pulse, - ) cost = 1 - route = PATH_RUST research_tree_icon_path = 'icons/effects/eldritch.dmi' research_tree_icon_state = "cloud_swirl" - depth = 4 + /datum/heretic_knowledge/rust_regen/on_gain(mob/user, datum/antagonist/heretic/our_heretic) user.AddElement(/datum/element/leeching_walk) @@ -114,8 +90,6 @@ When triggered, your victim will suffer heavy disgust and confusion. \ Allows you to rust reinforced walls and floors as well as plasteel." gain_text = "The Blacksmith looks away. To a place lost long ago. \"Rusted Hills help those in dire need... at a cost.\"" - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/rust) - route = PATH_RUST mark_type = /datum/status_effect/eldritch/rust /datum/heretic_knowledge/mark/rust_mark/on_gain(mob/user, datum/antagonist/heretic/our_heretic) @@ -123,8 +97,6 @@ our_heretic.increase_rust_strength() /datum/heretic_knowledge/knowledge_ritual/rust - next_knowledge = list(/datum/heretic_knowledge/spell/rust_construction) - route = PATH_RUST /datum/heretic_knowledge/spell/rust_construction name = "Rust Construction" @@ -132,29 +104,16 @@ Anyone overtop the wall will be throw aside (or upwards) and sustain damage." gain_text = "Images of foreign and ominous structures began to dance in my mind. Covered head to toe in thick rust, \ they no longer looked man made. Or perhaps they never were in the first place." - next_knowledge = list(/datum/heretic_knowledge/spell/area_conversion) - spell_to_add = /datum/action/cooldown/spell/pointed/rust_construction + action_to_add = /datum/action/cooldown/spell/pointed/rust_construction cost = 1 - route = PATH_RUST - depth = 7 /datum/heretic_knowledge/spell/area_conversion name = "Aggressive Spread" desc = "Grants you Aggressive Spread, a spell that spreads rust to nearby surfaces. \ Already rusted surfaces are destroyed \ Also improves the rusting abilities of non rust-heretics." gain_text = "All wise men know well not to visit the Rusted Hills... Yet the Blacksmith's tale was inspiring." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/rust, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/curse/corrosion, - /datum/heretic_knowledge/summon/rusty, - /datum/heretic_knowledge/crucible, - /datum/heretic_knowledge/rifle, - ) - spell_to_add = /datum/action/cooldown/spell/aoe/rust_conversion + action_to_add = /datum/action/cooldown/spell/aoe/rust_conversion cost = 1 - route = PATH_RUST - depth = 8 research_tree_icon_frame = 5 /datum/heretic_knowledge/spell/area_conversion/on_gain(mob/user, datum/antagonist/heretic/our_heretic) @@ -166,8 +125,6 @@ desc = "Your Rusty Blade now disgusts enemies on attack \ Allows you to rust Titanium and Plastitanium.." gain_text = "The Blacksmith hands you their blade. \"The Blade will guide you through the flesh, should you let it.\" \ The heavy rust weights it down. You stare deeply into it. The Rusted Hills call for you, now." - next_knowledge = list(/datum/heretic_knowledge/spell/entropic_plume) - route = PATH_RUST research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_rust" @@ -189,14 +146,11 @@ at friend or foe wildly. Also rusts and destroys and surfaces it hits and improves the rusting abilities of non-rust heretics." gain_text = "The corrosion was unstoppable. The rust was unpleasable. \ The Blacksmith was gone, and you hold their blade. Champions of hope, the Rustbringer is nigh!" - next_knowledge = list( - /datum/heretic_knowledge/ultimate/rust_final, - /datum/heretic_knowledge/spell/rust_charge, - ) - spell_to_add = /datum/action/cooldown/spell/cone/staggered/entropic_plume + + action_to_add = /datum/action/cooldown/spell/cone/staggered/entropic_plume cost = 1 - route = PATH_RUST - depth = 10 + + /datum/heretic_knowledge/spell/entropic_plume/on_gain(mob/user) . = ..() @@ -212,7 +166,7 @@ and becoming immune to many effects and dangers \ You will be able to rust almost anything upon ascending." gain_text = "Champion of rust. Corruptor of steel. Fear the dark, for the RUSTBRINGER has come! \ The Blacksmith forges ahead! Rusted Hills, CALL MY NAME! WITNESS MY ASCENSION!" - route = PATH_RUST + ascension_achievement = /datum/award/achievement/misc/rust_ascension /// If TRUE, then immunities are currently active. var/immunities_active = FALSE diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm index 3a5e84e75bc..1031b9bf3c7 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm @@ -15,7 +15,7 @@ required_atoms = list(/mob/living/carbon/human = 1) cost = 0 priority = MAX_KNOWLEDGE_PRIORITY // Should be at the top - route = PATH_START + is_starting_knowledge = TRUE research_tree_icon_path = 'icons/effects/eldritch.dmi' research_tree_icon_state = "eye_close" research_tree_icon_frame = 1 diff --git a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm index 90a51dcccb0..b4470f9c7fb 100644 --- a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm +++ b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm @@ -1,13 +1,20 @@ +/datum/heretic_knowledge_tree_column/ash_to_moon + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/ash + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/moon + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/medallion + tier2 = /datum/heretic_knowledge/curse/paralysis + tier3 = /datum/heretic_knowledge/summon/ashy + // Sidepaths for knowledge between Ash and Flesh. /datum/heretic_knowledge/medallion name = "Ashen Eyes" desc = "Allows you to transmute a pair of eyes, a candle, and a glass shard into an Eldritch Medallion. \ The Eldritch Medallion grants you thermal vision while worn, and also functions as a focus." gain_text = "Piercing eyes guided them through the mundane. Neither darkness nor terror could stop them." - next_knowledge = list( - /datum/heretic_knowledge/spell/ash_passage, - /datum/heretic_knowledge/spell/moon_smile, - ) + required_atoms = list( /obj/item/organ/eyes = 1, /obj/item/shard = 1, @@ -15,10 +22,8 @@ ) result_atoms = list(/obj/item/clothing/neck/eldritch_amulet) cost = 1 - route = PATH_SIDE research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "eye_medalion" - depth = 4 /datum/heretic_knowledge/curse/paralysis name = "Curse of Paralysis" @@ -26,10 +31,7 @@ While cursed, the victim will be unable to walk. You can additionally supply an item that a victim has touched \ or is covered in the victim's blood to make the curse last longer." gain_text = "The flesh of humanity is weak. Make them bleed. Show them their fragility." - next_knowledge = list( - /datum/heretic_knowledge/mad_mask, - /datum/heretic_knowledge/moon_amulet, - ) + required_atoms = list( /obj/item/bodypart/leg/left = 1, /obj/item/bodypart/leg/right = 1, @@ -39,10 +41,10 @@ duration_modifier = 2 curse_color = "#f19a9a" cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "curse_paralysis" - depth = 8 + /datum/heretic_knowledge/curse/paralysis/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE) if(chosen_mob.usable_legs <= 0) // What're you gonna do, curse someone who already can't walk? @@ -68,10 +70,7 @@ Ash Spirits have a short range jaunt and the ability to cause bleeding in foes at range. \ They also have the ability to create a ring of fire around themselves for a length of time." gain_text = "I combined my principle of hunger with my desire for destruction. The Marshal knew my name, and the Nightwatcher gazed on." - next_knowledge = list( - /datum/heretic_knowledge/spell/flame_birth, - /datum/heretic_knowledge/spell/moon_ringleader, - ) + required_atoms = list( /obj/effect/decal/cleanable/ash = 1, /obj/item/bodypart/head = 1, @@ -79,6 +78,6 @@ ) mob_to_summon = /mob/living/basic/heretic_summon/ash_spirit cost = 1 - route = PATH_SIDE + poll_ignore_define = POLL_IGNORE_ASH_SPIRIT - depth = 10 + diff --git a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm index 8a1fe6b5a87..a09c9cd8797 100644 --- a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm +++ b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm @@ -1,3 +1,13 @@ +/datum/heretic_knowledge_tree_column/blade_to_rust + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/blade + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/rust + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/armor + tier2 = list(/datum/heretic_knowledge/crucible, /datum/heretic_knowledge/rifle) + tier3 = /datum/heretic_knowledge/spell/rust_charge + // Sidepaths for knowledge between Rust and Blade. /datum/heretic_knowledge/armor name = "Armorer's Ritual" @@ -5,21 +15,18 @@ Eldritch Armor provides great protection while also acting as a focus when hooded." gain_text = "The Rusted Hills welcomed the Blacksmith in their generosity. And the Blacksmith \ returned their generosity in kind." - next_knowledge = list( - /datum/heretic_knowledge/rust_regen, - /datum/heretic_knowledge/blade_dance, - ) + required_atoms = list( /obj/structure/table = 1, /obj/item/clothing/mask/gas = 1, ) result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch) cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/clothing/suits/armor.dmi' research_tree_icon_state = "eldritch_armor" research_tree_icon_frame = 12 - depth = 4 + /datum/heretic_knowledge/crucible name = "Mawed Crucible" @@ -27,20 +34,17 @@ The Mawed Crucible can brew powerful potions for combat and utility, but must be fed bodyparts and organs between uses." gain_text = "This is pure agony. I wasn't able to summon the figure of the Aristocrat, \ but with the Priest's attention I stumbled upon a different recipe..." - next_knowledge = list( - /datum/heretic_knowledge/duel_stance, - /datum/heretic_knowledge/spell/area_conversion, - ) + required_atoms = list( /obj/structure/reagent_dispensers/watertank = 1, /obj/structure/table = 1, ) result_atoms = list(/obj/structure/destructible/eldritch_crucible) cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "crucible" - depth = 8 + /datum/heretic_knowledge/rifle name = "Lionhunter's Rifle" @@ -52,11 +56,7 @@ causing the shot to mark your victim with your grasp and teleport you directly to them." gain_text = "I met an old man in an antique shop who wielded a very unusual weapon. \ I could not purchase it at the time, but they showed me how they made it ages ago." - next_knowledge = list( - /datum/heretic_knowledge/duel_stance, - /datum/heretic_knowledge/spell/area_conversion, - /datum/heretic_knowledge/rifle_ammo, - ) + required_atoms = list( /obj/item/stack/sheet/mineral/wood = 1, /obj/item/stack/sheet/animalhide = 1, @@ -64,8 +64,8 @@ ) result_atoms = list(/obj/item/gun/ballistic/rifle/lionhunter) cost = 1 - route = PATH_SIDE - depth = 8 + + research_tree_icon_path = 'icons/obj/weapons/guns/ballistic.dmi' research_tree_icon_state = "goldrevolver" @@ -81,10 +81,10 @@ ) result_atoms = list(/obj/item/ammo_box/strilka310/lionhunter) cost = 0 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/weapons/guns/ammo.dmi' research_tree_icon_state = "310_strip" - depth = 8 + /// A list of calibers that the ritual will deny. Only ballistic calibers are allowed. var/static/list/caliber_blacklist = list( CALIBER_LASER, @@ -111,11 +111,8 @@ name = "Rust Charge" desc = "A charge that must be started on a rusted tile and will destroy any rusted objects you come into contact with, will deal high damage to others and rust around you during the charge." gain_text = "The hills sparkled now, as I neared them my mind began to wander. I quickly regained my resolve and pushed forward, this last leg would be the most treacherous." - next_knowledge = list( - /datum/heretic_knowledge/spell/furious_steel, - /datum/heretic_knowledge/spell/entropic_plume, - ) - spell_to_add = /datum/action/cooldown/mob_cooldown/charge/rust + + action_to_add = /datum/action/cooldown/mob_cooldown/charge/rust cost = 1 - route = PATH_SIDE - depth = 10 + + diff --git a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm index 7b7bdddb19a..d15bb242989 100644 --- a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm +++ b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm @@ -1,3 +1,14 @@ +/datum/heretic_knowledge_tree_column/cosmic_to_ash + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/cosmic + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/ash + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/summon/fire_shark + tier2 = /datum/heretic_knowledge/spell/space_phase + tier3 = /datum/heretic_knowledge/eldritch_coin + + // Sidepaths for knowledge between Cosmos and Ash. /datum/heretic_knowledge/summon/fire_shark @@ -6,10 +17,7 @@ Fire Sharks are fast and strong in groups, but die quickly. They are also highly resistant against fire attacks. \ Fire Sharks inject phlogiston into its victims and spawn plasma once they die." gain_text = "The cradle of the nebula was cold, but not dead. Light and heat flits even through the deepest darkness, and is hunted by its own predators." - next_knowledge = list( - /datum/heretic_knowledge/spell/cosmic_runes, - /datum/heretic_knowledge/spell/ash_passage, - ) + required_atoms = list( /obj/effect/decal/cleanable/ash = 1, /obj/item/organ/liver = 1, @@ -17,9 +25,9 @@ ) mob_to_summon = /mob/living/basic/heretic_summon/fire_shark cost = 1 - route = PATH_SIDE + poll_ignore_define = POLL_IGNORE_FIRE_SHARK - depth = 4 + research_tree_icon_dir = EAST /datum/heretic_knowledge/spell/space_phase @@ -27,14 +35,11 @@ desc = "Grants you Space Phase, a spell that allows you to move freely through space. \ You can only phase in and out when you are on a space or misc turf." gain_text = "You feel like your body can move through space as if you where dust." - next_knowledge = list( - /datum/heretic_knowledge/spell/star_blast, - /datum/heretic_knowledge/mad_mask, - ) - spell_to_add = /datum/action/cooldown/spell/jaunt/space_crawl + + action_to_add = /datum/action/cooldown/spell/jaunt/space_crawl cost = 1 - route = PATH_SIDE - depth = 8 + + research_tree_icon_frame = 6 /datum/heretic_knowledge/eldritch_coin @@ -44,17 +49,14 @@ when landing on tails. If you insert the coin into an airlock, it will be consumed \ to fry its electronics, opening the airlock permanently unless bolted. " gain_text = "The Mansus is a place of all sorts of sins. But greed held a special role." - next_knowledge = list( - /datum/heretic_knowledge/spell/cosmic_expansion, - /datum/heretic_knowledge/spell/flame_birth, - ) + required_atoms = list( /obj/item/stack/sheet/mineral/diamond = 1, /obj/item/stack/sheet/mineral/plasma = 1, ) result_atoms = list(/obj/item/coin/eldritch) cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/economy.dmi' research_tree_icon_state = "coin_heretic" - depth = 10 + diff --git a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm index d54646fe103..bd7f1cf621c 100644 --- a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm +++ b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm @@ -1,3 +1,13 @@ +/datum/heretic_knowledge_tree_column/flesh_to_void + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/flesh + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/void + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/void_cloak + tier2 = /datum/heretic_knowledge/spell/blood_siphon + tier3 = list(/datum/heretic_knowledge/spell/void_prison, /datum/heretic_knowledge/spell/cleave) + // Sidepaths for knowledge between Flesh and Void. /datum/heretic_knowledge/void_cloak @@ -7,10 +17,7 @@ and while the hood is up, the cloak is completely invisible. It also provide decent armor and \ has pockets which can hold one of your blades, various ritual components (such as organs), and small heretical trinkets." gain_text = "The Owl is the keeper of things that are not quite in practice, but in theory are. Many things are." - next_knowledge = list( - /datum/heretic_knowledge/limited_amount/flesh_ghoul, - /datum/heretic_knowledge/cold_snap, - ) + required_atoms = list( /obj/item/shard = 1, /obj/item/clothing/suit = 1, @@ -18,24 +25,18 @@ ) result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/void) cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/clothing/suits/armor.dmi' research_tree_icon_state = "void_cloak" - depth = 4 /datum/heretic_knowledge/spell/blood_siphon name = "Blood Siphon" desc = "Grants you Blood Siphon, a spell that drains a victim of blood and health, transferring it to you. \ Also has a chance to transfer wounds from you to the victim." gain_text = "\"No matter the man, we bleed all the same.\" That's what the Marshal told me." - next_knowledge = list( - /datum/heretic_knowledge/spell/void_phase, - /datum/heretic_knowledge/summon/raw_prophet, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/blood_siphon + + action_to_add = /datum/action/cooldown/spell/pointed/blood_siphon cost = 1 - route = PATH_SIDE - depth = 8 /datum/heretic_knowledge/spell/void_prison name = "Void Prison" @@ -45,14 +46,9 @@ I try to yell, grab hold of this fool and tell them to run. \ But the only welts made are on my own beating fist. \ My smiling face turns to regard me, reflecting back in glassy eyes the empty path I have been lead down." - next_knowledge = list( - /datum/heretic_knowledge/spell/void_phase, - /datum/heretic_knowledge/summon/raw_prophet, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/void_prison + + action_to_add = /datum/action/cooldown/spell/pointed/void_prison cost = 1 - route = PATH_SIDE - depth = 8 /datum/heretic_knowledge/spell/cleave name = "Blood Cleave" @@ -60,11 +56,8 @@ that causes heavy bleeding and blood loss to anyone afflicted." gain_text = "At first I didn't understand these instruments of war, but the Priest \ told me to use them regardless. Soon, he said, I would know them well." - next_knowledge = list( - /datum/heretic_knowledge/summon/stalker, - /datum/heretic_knowledge/spell/void_pull, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/cleave + + action_to_add = /datum/action/cooldown/spell/pointed/cleave cost = 1 - route = PATH_SIDE - depth = 10 + + diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm b/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm index 706b83abac7..0f7c9d9fc70 100644 --- a/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm +++ b/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm @@ -1,3 +1,19 @@ +/datum/heretic_knowledge_tree_column/lock_to_flesh + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/lock + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/flesh + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/dummy_lock_to_flesh + tier2 = /datum/heretic_knowledge/spell/opening_blast + tier3 = /datum/heretic_knowledge/spell/apetra_vulnera + +/datum/heretic_knowledge/dummy_lock_to_flesh + name = "Flesh and Lock ways" + desc = "Research this to gain access to the other path" + gain_text = "There are ways from feasting to wounding, the power of birth is close to the power of opening." + cost = 1 + // Sidepaths for knowledge between Knock and Flesh. /datum/heretic_knowledge/spell/opening_blast name = "Wave Of Desperation" @@ -5,14 +21,9 @@ It removes your restraints, repels and knocks down adjacent people, and applies the Mansus Grasp to everything nearby. \ However, you will fall unconscious a short time after casting this spell." gain_text = "My shackles undone in dark fury, their feeble bindings crumble before my power." - next_knowledge = list( - /datum/heretic_knowledge/summon/raw_prophet, - /datum/heretic_knowledge/spell/burglar_finesse, - ) - spell_to_add = /datum/action/cooldown/spell/aoe/wave_of_desperation + + action_to_add = /datum/action/cooldown/spell/aoe/wave_of_desperation cost = 1 - route = PATH_SIDE - depth = 8 /datum/heretic_knowledge/spell/apetra_vulnera name = "Apetra Vulnera" @@ -20,11 +31,8 @@ which causes heavy bleeding on all bodyparts of the victim that have more than 15 brute damage. \ Wounds a random limb if no limb is sufficiently damaged." gain_text = "Flesh opens, and blood spills. My master seeks sacrifice, and I shall appease." - next_knowledge = list( - /datum/heretic_knowledge/summon/stalker, - /datum/heretic_knowledge/spell/caretaker_refuge, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/apetra_vulnera + + action_to_add = /datum/action/cooldown/spell/pointed/apetra_vulnera cost = 1 - route = PATH_SIDE - depth = 10 + + diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm index cb719d163d5..5d3795b0ce9 100644 --- a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm +++ b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm @@ -1,19 +1,32 @@ +/datum/heretic_knowledge_tree_column/moon_to_lock + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/moon + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/lock + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/spell/mind_gate + tier2 = list(/datum/heretic_knowledge/unfathomable_curio, /datum/heretic_knowledge/painting) + tier3 = /datum/heretic_knowledge/dummy_moon_to_lock + // Sidepaths for knowledge between Knock and Moon. +/datum/heretic_knowledge/dummy_moon_to_lock + name = "Lock and Moon ways" + desc = "Research this to gain access to the other path" + gain_text = "The powers of Madness are like a wound in one's soul, and every wound can be opened and closed." + cost = 1 + + + /datum/heretic_knowledge/spell/mind_gate name = "Mind Gate" desc = "Grants you Mind Gate, a spell which inflicts hallucinations, \ confusion, oxygen loss and brain damage to its target over 10 seconds.\ The caster takes 20 brain damage per use." gain_text = "My mind swings open like a gate, and its insight will let me perceive the truth." - next_knowledge = list( - /datum/heretic_knowledge/key_ring, - /datum/heretic_knowledge/spell/moon_smile, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/mind_gate + + action_to_add = /datum/action/cooldown/spell/pointed/mind_gate cost = 1 - route = PATH_SIDE - depth = 4 /datum/heretic_knowledge/unfathomable_curio name = "Unfathomable Curio" @@ -22,10 +35,7 @@ veil you, allowing you to take 5 hits without suffering damage, this veil will recharge very slowly \ outside of combat." gain_text = "The mansus holds many a curio, some are not meant for the mortal eye." - next_knowledge = list( - /datum/heretic_knowledge/spell/burglar_finesse, - /datum/heretic_knowledge/moon_amulet, - ) + required_atoms = list( /obj/item/organ/lungs = 1, /obj/item/stack/rods = 3, @@ -33,10 +43,10 @@ ) result_atoms = list(/obj/item/storage/belt/unfathomable_curio) cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/clothing/belts.dmi' research_tree_icon_state = "unfathomable_curio" - depth = 8 + /datum/heretic_knowledge/painting name = "Unsealed Arts" @@ -49,17 +59,14 @@ Master of the Rusted Mountain: Requires a piece of Trash. Curses non-heretics to rust the floor they walk on." gain_text = "A wind of inspiration blows through me. Beyond the veil and past the gate great works exist, yet to be painted. \ They yearn for mortal eyes, so I shall give them an audience." - next_knowledge = list( - /datum/heretic_knowledge/spell/burglar_finesse, - /datum/heretic_knowledge/moon_amulet, - ) + required_atoms = list(/obj/item/canvas = 1) result_atoms = list(/obj/item/canvas) cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/obj/signs.dmi' research_tree_icon_state = "eldritch_painting_weeping" - depth = 8 + /datum/heretic_knowledge/painting/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) if(locate(/obj/item/organ/eyes) in atoms) diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm index d4e5d465125..953590124f6 100644 --- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm +++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm @@ -1,3 +1,14 @@ +/datum/heretic_knowledge_tree_column/rust_to_cosmic + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/rust + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/cosmic + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/essence + tier2 = list(/datum/heretic_knowledge/curse/corrosion, /datum/heretic_knowledge/entropy_pulse) + tier3 = /datum/heretic_knowledge/summon/rusty + + // Sidepaths for knowledge between Rust and Cosmos. /datum/heretic_knowledge/essence @@ -6,18 +17,15 @@ Eldritch water can be consumed for potent healing, or given to heathens for deadly poisoning." gain_text = "This is an old recipe. The Owl whispered it to me. \ Created by the Priest - the Liquid that both was and is not." - next_knowledge = list( - /datum/heretic_knowledge/rust_regen, - /datum/heretic_knowledge/spell/cosmic_runes, - ) + required_atoms = list( /obj/structure/reagent_dispensers/watertank = 1, /obj/item/shard = 1, ) result_atoms = list(/obj/item/reagent_containers/cup/beaker/eldritch) cost = 1 - route = PATH_SIDE - depth = 4 + + research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "eldritch_flask" @@ -30,11 +38,11 @@ /obj/item/trash = 1, ) cost = 0 - route = PATH_SIDE + research_tree_icon_path = 'icons/mob/actions/actions_ecult.dmi' research_tree_icon_state = "corrode" research_tree_icon_frame = 10 - depth = 4 + var/rusting_range = 8 /datum/heretic_knowledge/entropy_pulse/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) @@ -53,10 +61,7 @@ While cursed, the victim will repeatedly vomit while their organs will take constant damage. You can additionally supply an item \ that a victim has touched or is covered in the victim's blood to make the curse last longer." gain_text = "The body of humanity is temporary. Their weaknesses cannot be stopped, like iron falling to rust. Show them all." - next_knowledge = list( - /datum/heretic_knowledge/spell/area_conversion, - /datum/heretic_knowledge/spell/star_blast, - ) + required_atoms = list( /obj/item/wirecutters = 1, /obj/effect/decal/cleanable/vomit = 1, @@ -66,10 +71,10 @@ duration_modifier = 4 curse_color = "#c1ffc9" cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "curse_corrosion" - depth = 8 + /datum/heretic_knowledge/curse/corrosion/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE) to_chat(chosen_mob, span_danger("You feel very ill...")) @@ -89,10 +94,7 @@ desc = "Allows you to transmute a pool of vomit, some cable coil, and 10 sheets of iron into a Rust Walker. \ Rust Walkers excel at spreading rust and are moderately strong in combat." gain_text = "I combined my knowledge of creation with my desire for corruption. The Marshal knew my name, and the Rusted Hills echoed out." - next_knowledge = list( - /datum/heretic_knowledge/spell/area_conversion, - /datum/heretic_knowledge/spell/star_blast, - ) + required_atoms = list( /obj/effect/decal/cleanable/vomit = 1, /obj/item/stack/sheet/iron = 10, @@ -100,7 +102,7 @@ ) mob_to_summon = /mob/living/basic/heretic_summon/rust_walker cost = 1 - route = PATH_SIDE + poll_ignore_define = POLL_IGNORE_RUST_SPIRIT - depth = 8 + diff --git a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm index 17eb87affba..df3ad9a34c7 100644 --- a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm +++ b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm @@ -1,5 +1,17 @@ // Sidepaths for knowledge between Void and Blade. +/datum/heretic_knowledge_tree_column/void_to_blade + neighbour_type_left = /datum/heretic_knowledge_tree_column/main/void + neighbour_type_right = /datum/heretic_knowledge_tree_column/main/blade + + route = PATH_SIDE + + tier1 = /datum/heretic_knowledge/limited_amount/risen_corpse + tier2 = /datum/heretic_knowledge/rune_carver + tier3 = /datum/heretic_knowledge/summon/maid_in_mirror + + + /// The max health given to Shattered Risen #define RISEN_MAX_HEALTH 125 @@ -12,20 +24,17 @@ gain_text = "I witnessed a cold, rending force drag this corpse back to near-life. \ When it moves, it crunches like broken glass. Its hands are no longer recognizable as human - \ each clenched fist contains a brutal nest of sharp bone-shards instead." - next_knowledge = list( - /datum/heretic_knowledge/cold_snap, - /datum/heretic_knowledge/blade_dance, - ) + required_atoms = list( /obj/item/clothing/suit = 1, /obj/item/clothing/gloves/latex = 1, ) limit = 1 cost = 1 - route = PATH_SIDE + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "ghoul_shattered" - depth = 4 + /datum/heretic_knowledge/limited_amount/risen_corpse/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) . = ..() @@ -107,22 +116,6 @@ demolition_mod = 1.5 sharpness = SHARP_EDGED -/obj/item/mutant_hand/shattered_risen/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT) - -/obj/item/mutant_hand/shattered_risen/visual_equipped(mob/user, slot) - . = ..() - - // Even hand indexes are right hands, - // Odd hand indexes are left hand - // ...But also, we swap it intentionally here, - // so right icon is shown on the left (Because hands) - if(user.get_held_index_of_item(src) % 2 == 1) - icon_state = "[base_icon_state]_right" - else - icon_state = "[base_icon_state]_left" - /datum/heretic_knowledge/rune_carver name = "Carving Knife" desc = "Allows you to transmute a knife, a shard of glass, and a piece of paper to create a Carving Knife. \ @@ -130,10 +123,7 @@ Also makes for a handy throwing weapon." gain_text = "Etched, carved... eternal. There is power hidden in everything. I can unveil it! \ I can carve the monolith to reveal the chains!" - next_knowledge = list( - /datum/heretic_knowledge/spell/void_phase, - /datum/heretic_knowledge/duel_stance, - ) + required_atoms = list( /obj/item/knife = 1, /obj/item/shard = 1, @@ -141,8 +131,8 @@ ) result_atoms = list(/obj/item/melee/rune_carver) cost = 1 - route = PATH_SIDE - depth = 8 + + research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "rune_carver" @@ -154,10 +144,7 @@ However, they are weak to mortal gaze and take damage by being examined." gain_text = "Within each reflection, lies a gateway into an unimaginable world of colors never seen and \ people never met. The ascent is glass, and the walls are knives. Each step is blood, if you do not have a guide." - next_knowledge = list( - /datum/heretic_knowledge/spell/void_pull, - /datum/heretic_knowledge/spell/furious_steel, - ) + required_atoms = list( /obj/item/stack/sheet/mineral/titanium = 5, /obj/item/clothing/suit/armor = 1, @@ -165,7 +152,7 @@ /obj/item/organ/lungs = 1, ) cost = 1 - route = PATH_SIDE + mob_to_summon = /mob/living/basic/heretic_summon/maid_in_the_mirror poll_ignore_define = POLL_IGNORE_MAID_IN_MIRROR - depth = 10 + diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm index a4560058db8..b20009febcf 100644 --- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm @@ -1,6 +1,6 @@ // Heretic starting knowledge. -/// Global list of all heretic knowledge that have route = PATH_START. List of PATHS. +/// Global list of all heretic knowledge that have is_starting_knowledge = TRUE. List of PATHS. GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) /** @@ -10,7 +10,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) /proc/initialize_starting_knowledge() . = list() for(var/datum/heretic_knowledge/knowledge as anything in subtypesof(/datum/heretic_knowledge)) - if(initial(knowledge.route) == PATH_START) + if(initial(knowledge.is_starting_knowledge) == TRUE) . += knowledge /* @@ -21,13 +21,9 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) desc = "Starts your journey into the Mansus. \ Grants you the Mansus Grasp, a powerful and upgradable \ disabling spell that can be cast regardless of having a focus." - spell_to_add = /datum/action/cooldown/spell/touch/mansus_grasp + action_to_add = /datum/action/cooldown/spell/touch/mansus_grasp cost = 0 - route = PATH_START - -/datum/heretic_knowledge/spell/basic/New() - . = ..() - next_knowledge = subtypesof(/datum/heretic_knowledge/limited_amount/starting) + is_starting_knowledge = TRUE /** * The Living Heart heretic knowledge. @@ -47,7 +43,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) ) cost = 0 priority = MAX_KNOWLEDGE_PRIORITY - 1 // Knowing how to remake your heart is important - route = PATH_START + is_starting_knowledge = TRUE research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "living_heart" research_tree_icon_frame = 1 @@ -207,7 +203,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) result_atoms = list(/obj/item/clothing/neck/heretic_focus) cost = 0 priority = MAX_KNOWLEDGE_PRIORITY - 2 // Not as important as making a heart or sacrificing, but important enough. - route = PATH_START + is_starting_knowledge = TRUE research_tree_icon_path = 'icons/obj/clothing/neck.dmi' research_tree_icon_state = "eldritch_necklace" @@ -215,9 +211,9 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) name = "Cloak of Shadow" desc = "Grants you the spell Cloak of Shadow. This spell will completely conceal your identity in a purple smoke \ for three minutes, assisting you in keeping secrecy. Requires a focus to cast." - spell_to_add = /datum/action/cooldown/spell/shadow_cloak + action_to_add = /datum/action/cooldown/spell/shadow_cloak cost = 0 - route = PATH_START + is_starting_knowledge = TRUE /** * Codex Cicatrixi is available at the start: @@ -241,7 +237,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) banned_atom_types = list(/obj/item/pen) result_atoms = list(/obj/item/codex_cicatrix) cost = 1 - route = PATH_START + is_starting_knowledge = TRUE priority = MAX_KNOWLEDGE_PRIORITY - 3 // Least priority out of the starting knowledges, as it's an optional boon. var/static/list/non_mob_bindings = typecacheof(list(/obj/item/stack/sheet/leather, /obj/item/stack/sheet/animalhide)) research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' @@ -306,7 +302,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) name = "Feast of Owls" desc = "Allows you to undergo a ritual that gives you 5 knowledge points but locks you out of ascension. This can only be done once and cannot be reverted." gain_text = "Under the soft glow of unreason there is a beast that stalks the night. I shall bring it forth and let it enter my presence. It will feast upon my amibitions and leave knowledge in its wake." - route = PATH_START + is_starting_knowledge = TRUE required_atoms = list() research_tree_icon_path = 'icons/mob/actions/actions_animal.dmi' research_tree_icon_state = "god_transmit" diff --git a/code/modules/antagonists/heretic/knowledge/void_lore.dm b/code/modules/antagonists/heretic/knowledge/void_lore.dm index b7cf7b31b79..7ea49332ee7 100644 --- a/code/modules/antagonists/heretic/knowledge/void_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/void_lore.dm @@ -1,32 +1,22 @@ -/** - * # The path of VOID. - * - * Goes as follows: - * - * Glimmer of Winter - * Grasp of Void - * Aristocrat's Way - * > Sidepaths: - * Void Cloak - * Shattered Ritual - * - * Mark of Void - * Ritual of Knowledge - * Void Conduit - * Void Phase - * > Sidepaths: - * Void Stasis - * Carving Knife - * Blood Siphon - * - * Seeking blade - * Void Pull - * > Sidepaths: - * Cleave - * Maid in the Mirror - * - * Waltz at the End of Time - */ + +/datum/heretic_knowledge_tree_column/main/void + neighbour_type_left = /datum/heretic_knowledge_tree_column/flesh_to_void + neighbour_type_right = /datum/heretic_knowledge_tree_column/void_to_blade + + route = PATH_VOID + ui_bgr = "node_void" + + start = /datum/heretic_knowledge/limited_amount/starting/base_void + grasp = /datum/heretic_knowledge/void_grasp + tier1 = /datum/heretic_knowledge/cold_snap + mark = /datum/heretic_knowledge/mark/void_mark + ritual_of_knowledge = /datum/heretic_knowledge/knowledge_ritual/void + unique_ability = /datum/heretic_knowledge/spell/void_conduit + tier2 = /datum/heretic_knowledge/spell/void_phase + blade = /datum/heretic_knowledge/blade_upgrade/void + tier3 = /datum/heretic_knowledge/spell/void_pull + ascension = /datum/heretic_knowledge/ultimate/void_final + /datum/heretic_knowledge/limited_amount/starting/base_void name = "Glimmer of Winter" desc = "Opens up the Path of Void to you. \ @@ -34,10 +24,8 @@ You can only create two at a time." gain_text = "I feel a shimmer in the air, the air around me gets colder. \ I start to realize the emptiness of existence. Something's watching me." - next_knowledge = list(/datum/heretic_knowledge/void_grasp) required_atoms = list(/obj/item/knife = 1) result_atoms = list(/obj/item/melee/sickly_blade/void) - route = PATH_VOID research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi' research_tree_icon_state = "void_blade" @@ -58,10 +46,7 @@ desc = "Your Mansus Grasp will temporarily mute and chill the victim." gain_text = "I saw the cold watcher who observes me. The chill mounts within me. \ They are quiet. This isn't the end of the mystery." - next_knowledge = list(/datum/heretic_knowledge/cold_snap) cost = 1 - route = PATH_VOID - depth = 3 research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "grasp_void" @@ -87,16 +72,10 @@ You can still take damage due to a lack of pressure." gain_text = "I found a thread of cold breath. It lead me to a strange shrine, all made of crystals. \ Translucent and white, a depiction of a nobleman stood before me." - next_knowledge = list( - /datum/heretic_knowledge/mark/void_mark, - /datum/heretic_knowledge/void_cloak, - /datum/heretic_knowledge/limited_amount/risen_corpse, - ) cost = 1 - route = PATH_VOID research_tree_icon_path = 'icons/effects/effects.dmi' research_tree_icon_state = "the_freezer" - depth = 4 + /// Traits we apply to become immune to the environment var/static/list/gain_traits = list(TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE) @@ -127,13 +106,9 @@ When triggered, further silences the victim and swiftly lowers the temperature of their body and the air around them." gain_text = "A gust of wind? A shimmer in the air? The presence is overwhelming, \ my senses began to betray me. My mind is my own enemy." - next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/void) - route = PATH_VOID mark_type = /datum/status_effect/eldritch/void /datum/heretic_knowledge/knowledge_ritual/void - next_knowledge = list(/datum/heretic_knowledge/spell/void_conduit) - route = PATH_VOID /datum/heretic_knowledge/spell/void_conduit name = "Void Conduit" @@ -141,11 +116,8 @@ gain_text = "The hum in the still, cold air turns to a cacophonous rattle. \ Over the noise, there is no distinction to the clattering of window panes and the yawning knowledge that ricochets through my skull. \ The doors won't close. I can't keep the cold out now." - next_knowledge = list(/datum/heretic_knowledge/spell/void_phase) - spell_to_add = /datum/action/cooldown/spell/conjure/void_conduit + action_to_add = /datum/action/cooldown/spell/conjure/void_conduit cost = 1 - route = PATH_VOID - depth = 7 /datum/heretic_knowledge/spell/void_phase name = "Void Phase" @@ -153,25 +125,16 @@ Additionally causes damage to heathens around your original and target destination." gain_text = "The entity calls themself the Aristocrat. They effortlessly walk through air like \ nothing - leaving a harsh, cold breeze in their wake. They disappear, and I am left in the blizzard." - next_knowledge = list( - /datum/heretic_knowledge/blade_upgrade/void, - /datum/heretic_knowledge/reroll_targets, - /datum/heretic_knowledge/spell/blood_siphon, - /datum/heretic_knowledge/spell/void_prison, - /datum/heretic_knowledge/rune_carver, - ) - spell_to_add = /datum/action/cooldown/spell/pointed/void_phase + action_to_add = /datum/action/cooldown/spell/pointed/void_phase cost = 1 - route = PATH_VOID - depth = 8 research_tree_icon_frame = 7 /datum/heretic_knowledge/blade_upgrade/void name = "Seeking Blade" desc = "Your blade now freezes enemies. Additionally, you can now attack distant marked targets with your Void Blade, teleporting directly next to them." gain_text = "Fleeting memories, fleeting feet. I mark my way with frozen blood upon the snow. Covered and forgotten." - next_knowledge = list(/datum/heretic_knowledge/spell/void_pull) - route = PATH_VOID + + research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_void" @@ -198,15 +161,11 @@ desc = "Grants you Void Pull, a spell that pulls all nearby heathens towards you, stunning them briefly." gain_text = "All is fleeting, but what else stays? I'm close to ending what was started. \ The Aristocrat reveals themselves to me again. They tell me I am late. Their pull is immense, I cannot turn back." - next_knowledge = list( - /datum/heretic_knowledge/ultimate/void_final, - /datum/heretic_knowledge/spell/cleave, - /datum/heretic_knowledge/summon/maid_in_mirror, - ) - spell_to_add = /datum/action/cooldown/spell/aoe/void_pull + + action_to_add = /datum/action/cooldown/spell/aoe/void_pull cost = 1 - route = PATH_VOID - depth = 10 + + research_tree_icon_frame = 6 /datum/heretic_knowledge/ultimate/void_final @@ -219,7 +178,7 @@ gain_text = "The world falls into darkness. I stand in an empty plane, small flakes of ice fall from the sky. \ The Aristocrat stands before me, beckoning. We will play a waltz to the whispers of dying reality, \ as the world is destroyed before our eyes. The void will return all to nothing, WITNESS MY ASCENSION!" - route = PATH_VOID + ascension_achievement = /datum/award/achievement/misc/void_ascension ///soundloop for the void theme var/datum/looping_sound/void_loop/sound_loop diff --git a/code/modules/antagonists/heretic/magic/furious_steel.dm b/code/modules/antagonists/heretic/magic/furious_steel.dm index d61ce5d1a39..dceb9023bdb 100644 --- a/code/modules/antagonists/heretic/magic/furious_steel.dm +++ b/code/modules/antagonists/heretic/magic/furious_steel.dm @@ -22,6 +22,8 @@ projectile_type = /obj/projectile/floating_blade projectile_amount = 3 + ///Effect of the projectile that surrounds us while the spell is active + var/projectile_effect = /obj/effect/floating_blade /// A ref to the status effect surrounding our heretic on activation. var/datum/status_effect/protective_blades/blade_effect @@ -67,7 +69,7 @@ QDEL_NULL(blade_effect) var/mob/living/living_user = on_who - blade_effect = living_user.apply_status_effect(/datum/status_effect/protective_blades, null, projectile_amount, 25, 0.66 SECONDS, projectile_type) + blade_effect = living_user.apply_status_effect(/datum/status_effect/protective_blades, null, projectile_amount, 25, 0.66 SECONDS, projectile_effect) RegisterSignal(blade_effect, COMSIG_QDELETING, PROC_REF(on_status_effect_deleted)) /datum/action/cooldown/spell/pointed/projectile/furious_steel/on_deactivation(mob/on_who, refund_cooldown = TRUE) @@ -167,3 +169,4 @@ deactive_msg = "You conceal the cursed blades." projectile_amount = 2 projectile_type = /obj/projectile/floating_blade/haunted + projectile_effect = /obj/effect/floating_blade/haunted diff --git a/code/modules/antagonists/heretic/magic/shadow_cloak.dm b/code/modules/antagonists/heretic/magic/shadow_cloak.dm index ca0ca1fa15b..8b068fbefa1 100644 --- a/code/modules/antagonists/heretic/magic/shadow_cloak.dm +++ b/code/modules/antagonists/heretic/magic/shadow_cloak.dm @@ -126,7 +126,7 @@ /datum/status_effect/shadow_cloak id = "shadow_cloak" alert_type = null - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /// How much damage we've been hit with var/damage_sustained = 0 /// How much damage we can be hit with before it starts rolling reveal chances diff --git a/code/modules/antagonists/heretic/magic/space_crawl.dm b/code/modules/antagonists/heretic/magic/space_crawl.dm index cce9f46085b..74b02c59c10 100644 --- a/code/modules/antagonists/heretic/magic/space_crawl.dm +++ b/code/modules/antagonists/heretic/magic/space_crawl.dm @@ -7,7 +7,7 @@ */ /datum/action/cooldown/spell/jaunt/space_crawl name = "Space Phase" - desc = "Allows you to phase in and out of existance while in space or misc tiles." + desc = "Allows you to phase in and out of existence while in space or misc tiles." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm index d9cd5a05eab..e8e824cc718 100644 --- a/code/modules/antagonists/heretic/magic/star_touch.dm +++ b/code/modules/antagonists/heretic/magic/star_touch.dm @@ -201,35 +201,6 @@ if(current_target) on_beam_hit(current_target) -/// Checks if the beam is going through an invalid turf -/datum/status_effect/cosmic_beam/proc/los_check(atom/movable/user, mob/target) - var/turf/user_turf = user.loc - if(!istype(user_turf)) - return FALSE - var/obj/dummy = new(user_turf) - dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE //Grille/Glass so it can be used through common windows - var/turf/previous_step = user_turf - var/first_step = TRUE - for(var/turf/next_step as anything in (get_line(user_turf, target) - user_turf)) - if(first_step) - for(var/obj/blocker in user_turf) - if(!blocker.density || !(blocker.flags_1 & ON_BORDER_1)) - continue - if(blocker.CanPass(dummy, get_dir(user_turf, next_step))) - continue - return FALSE // Could not leave the first turf. - first_step = FALSE - if(next_step.density) - qdel(dummy) - return FALSE - for(var/atom/movable/movable as anything in next_step) - if(!movable.CanPass(dummy, get_dir(next_step, previous_step))) - qdel(dummy) - return FALSE - previous_step = next_step - qdel(dummy) - return TRUE - /// What to add when the beam connects to a target /datum/status_effect/cosmic_beam/proc/on_beam_hit(mob/living/target) if(!istype(target, /mob/living/basic/heretic_summon/star_gazer)) diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm index d82d145b3ef..362e9750b49 100644 --- a/code/modules/antagonists/heretic/status_effects/buffs.dm +++ b/code/modules/antagonists/heretic/status_effects/buffs.dm @@ -138,7 +138,7 @@ id = "Silver Knives" alert_type = null status_type = STATUS_EFFECT_MULTIPLE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /// The number of blades we summon up to. var/max_num_blades = 4 /// The radius of the blade's orbit. @@ -148,13 +148,13 @@ /// If TRUE, we self-delete our status effect after all the blades are deleted. var/delete_on_blades_gone = TRUE /// What blade type to create - var/blade_type = /obj/effect/floating_blade + var/obj/effect/floating_blade/blade_type /// A list of blade effects orbiting / protecting our owner var/list/obj/effect/floating_blade/blades = list() /datum/status_effect/protective_blades/on_creation( mob/living/new_owner, - new_duration = -1, + new_duration = STATUS_EFFECT_PERMANENT, max_num_blades = 4, blade_orbit_radius = 20, time_between_initial_blades = 0.25 SECONDS, @@ -190,8 +190,7 @@ if(QDELETED(src) || QDELETED(owner)) return - var/obj/effect/floating_blade/blade - blade = new blade_type(get_turf(owner)) + var/obj/effect/floating_blade/blade = new blade_type(get_turf(owner)) blades += blade blade.orbit(owner, blade_orbit_radius) RegisterSignal(blade, COMSIG_QDELETING, PROC_REF(remove_blade)) @@ -257,12 +256,12 @@ /datum/status_effect/protective_blades/recharging/on_creation( mob/living/new_owner, - new_duration = -1, + new_duration = STATUS_EFFECT_PERMANENT, max_num_blades = 4, blade_orbit_radius = 20, time_between_initial_blades = 0.25 SECONDS, + blade_type = /obj/projectile/floating_blade, blade_recharge_time = 1 MINUTES, - blade_type = /obj/effect/floating_blade, ) src.blade_recharge_time = blade_recharge_time @@ -279,7 +278,7 @@ /datum/status_effect/caretaker_refuge id = "Caretaker’s Last Refuge" status_type = STATUS_EFFECT_REFRESH - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null var/static/list/caretaking_traits = list(TRAIT_GODMODE, TRAIT_HANDS_BLOCKED, TRAIT_IGNORESLOWDOWN, TRAIT_SECLUDED_LOCATION) diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm index 8b1751bccde..7bb2ebbd0e7 100644 --- a/code/modules/antagonists/heretic/status_effects/debuffs.dm +++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm @@ -161,7 +161,7 @@ alert_type = /atom/movable/screen/alert/status_effect/heretic_lastresort duration = 12 SECONDS status_type = STATUS_EFFECT_REPLACE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /atom/movable/screen/alert/status_effect/heretic_lastresort name = "Last Resort" @@ -183,7 +183,7 @@ /datum/status_effect/moon_converted id = "moon converted" alert_type = /atom/movable/screen/alert/status_effect/moon_converted - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_REPLACE ///used to track damage var/damage_sustained = 0 diff --git a/code/modules/antagonists/heretic/status_effects/ghoul.dm b/code/modules/antagonists/heretic/status_effects/ghoul.dm index b212f1a024a..34778d380fb 100644 --- a/code/modules/antagonists/heretic/status_effects/ghoul.dm +++ b/code/modules/antagonists/heretic/status_effects/ghoul.dm @@ -1,7 +1,7 @@ /datum/status_effect/ghoul id = "ghoul" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/ghoul /// The new max health value set for the ghoul, if supplied var/new_max_health diff --git a/code/modules/antagonists/heretic/status_effects/void_chill.dm b/code/modules/antagonists/heretic/status_effects/void_chill.dm index ed4bf1f3cb5..c286fcd2982 100644 --- a/code/modules/antagonists/heretic/status_effects/void_chill.dm +++ b/code/modules/antagonists/heretic/status_effects/void_chill.dm @@ -84,7 +84,7 @@ /datum/status_effect/void_chill/lasting id = "lasting_void_chill" - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/movespeed_modifier/void_chill variable = TRUE diff --git a/code/modules/antagonists/heretic/structures/lock_final.dm b/code/modules/antagonists/heretic/structures/lock_final.dm index 295ecbb3a2b..cd187641f73 100644 --- a/code/modules/antagonists/heretic/structures/lock_final.dm +++ b/code/modules/antagonists/heretic/structures/lock_final.dm @@ -1,7 +1,7 @@ /obj/structure/lock_tear name = "???" desc = "It stares back. There's no reason to remain. Run." - max_integrity = INFINITE + max_integrity = INFINITY resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF icon = 'icons/obj/anomaly.dmi' icon_state = "bhole3" diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm index ddfe675d412..e4b4528d616 100644 --- a/code/modules/antagonists/nightmare/nightmare_organs.dm +++ b/code/modules/antagonists/nightmare/nightmare_organs.dm @@ -94,13 +94,13 @@ user.temporarilyRemoveItemFromInventory(src, TRUE) Insert(user) -/obj/item/organ/heart/nightmare/on_mob_insert(mob/living/carbon/heart_owner, special) +/obj/item/organ/heart/nightmare/on_mob_insert(mob/living/carbon/heart_owner, special, movement_flags) . = ..() if(special != HEART_SPECIAL_SHADOWIFY) blade = new/obj/item/light_eater heart_owner.put_in_hands(blade) -/obj/item/organ/heart/nightmare/on_mob_remove(mob/living/carbon/heart_owner, special) +/obj/item/organ/heart/nightmare/on_mob_remove(mob/living/carbon/heart_owner, special, movement_flags) . = ..() respawn_progress = 0 if(blade && special != HEART_SPECIAL_SHADOWIFY) diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm index 1aa318fb682..f45da0fdce8 100644 --- a/code/modules/antagonists/spy/spy_bounty.dm +++ b/code/modules/antagonists/spy/spy_bounty.dm @@ -639,7 +639,7 @@ theft_time = 10 SECONDS black_market_prob = 0 /// What typepath of bot we want to steal. - var/mob/living/simple_animal/bot/bot_type + var/mob/living/bot_type /// Weakref to the bot we want to steal. VAR_FINAL/datum/weakref/target_bot_ref @@ -655,7 +655,7 @@ /datum/spy_bounty/some_bot/init_bounty(datum/spy_bounty_handler/handler) for(var/datum/spy_bounty/some_bot/existing_bounty in handler.get_all_bounties()) - var/mob/living/simple_animal/bot/existing_bot_type = existing_bounty.bot_type + var/mob/living/existing_bot_type = existing_bounty.bot_type // ensures we don't get two similar bounties. // may occasionally cast a wider net than we'd desire, but it's not that bad. if(ispath(bot_type, initial(existing_bot_type.parent_type))) diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm index 83899300614..e25473de6e3 100644 --- a/code/modules/antagonists/traitor/uplink_handler.dm +++ b/code/modules/antagonists/traitor/uplink_handler.dm @@ -69,10 +69,6 @@ SEND_SIGNAL(src, COMSIG_UPLINK_HANDLER_ON_UPDATE) return -/// Checks if traitor has enough reputation to purchase an item -/datum/uplink_handler/proc/not_enough_reputation(datum/uplink_item/to_purchase) - return has_progression && progression_points < to_purchase.progression_minimum - /// Checks for uplink flags as well as items restricted to roles and species /datum/uplink_handler/proc/check_if_restricted(datum/uplink_item/to_purchase) if(!to_purchase.can_be_bought(src)) @@ -104,7 +100,7 @@ var/current_stock = item_stock[to_purchase.stock_key] var/stock = current_stock != null ? current_stock : INFINITY - if(telecrystals < to_purchase.cost || stock <= 0 || not_enough_reputation(to_purchase)) + if(telecrystals < to_purchase.real_cost(src) || stock <= 0) return FALSE return TRUE @@ -116,7 +112,7 @@ if(to_purchase.limited_stock != -1 && !(to_purchase.stock_key in item_stock)) item_stock[to_purchase.stock_key] = to_purchase.limited_stock - telecrystals -= to_purchase.cost + telecrystals -= to_purchase.real_cost(src) to_purchase.purchase(user, src, source) if(to_purchase.stock_key in item_stock) diff --git a/code/modules/antagonists/voidwalker/voidwalker_organs.dm b/code/modules/antagonists/voidwalker/voidwalker_organs.dm index 23f1c6fb2fe..760caf3c8d8 100644 --- a/code/modules/antagonists/voidwalker/voidwalker_organs.dm +++ b/code/modules/antagonists/voidwalker/voidwalker_organs.dm @@ -26,7 +26,7 @@ /// Speed modifier given when in gravity var/datum/movespeed_modifier/speed_modifier = /datum/movespeed_modifier/grounded_voidwalker /// The void eater weapon - var/obj/item/glass_breaker = /obj/item/void_eater + var/obj/item/glass_breaker /// Our brain transmit telepathy spell var/datum/action/transmit = /datum/action/cooldown/spell/list_target/telepathy/voidwalker @@ -52,7 +52,7 @@ glass_breaker = new/obj/item/void_eater organ_owner.put_in_hands(glass_breaker) -/obj/item/organ/brain/voidwalker/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/brain/voidwalker/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_ENTER_AREA) @@ -72,8 +72,7 @@ transmit.Remove(organ_owner) transmit = initial(transmit) - if(glass_breaker) - qdel(glass_breaker) + QDEL_NULL(glass_breaker) /obj/item/organ/brain/voidwalker/proc/on_atom_entering(mob/living/carbon/organ_owner, atom/entering) SIGNAL_HANDLER diff --git a/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm b/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm index 7934e757077..6b7f733265c 100644 --- a/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm +++ b/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm @@ -5,7 +5,7 @@ /// Regenerate in space /datum/status_effect/space_regeneration id = "space_regeneration" - duration = INFINITE + duration = STATUS_EFFECT_PERMANENT alert_type = null // How much do we heal per tick? var/healing = 1.5 @@ -20,7 +20,7 @@ /datum/status_effect/planet_allergy id = "planet_allergy" - duration = INFINITE + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/veryhighgravity /datum/status_effect/planet_allergy/tick() diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 751873c8362..52cd80e7155 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -158,9 +158,8 @@ return ..() /obj/item/soulstone/proc/hot_potato(mob/living/user) - to_chat(user, span_userdanger("Holy magics residing in \the [src] burn your hand!")) - var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm") - affecting.receive_damage( 0, 10 ) // 10 burn damage + to_chat(user, span_userdanger("Holy magics residing in [src] burn your hand!")) + user.apply_damage(10, BURN, user.get_active_hand()) user.emote("scream") user.update_damage_overlays() user.dropItemToGround(src) diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm index 18e2dae715c..28432b1ada8 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm @@ -31,10 +31,10 @@ /// Whether the spell requires wizard garb or not var/requires_wizard_garb = FALSE /// Used so you can't have specific spells together - var/list/no_coexistance_typecache + var/list/no_coexistence_typecache /datum/spellbook_entry/New() - no_coexistance_typecache = typecacheof(no_coexistance_typecache) + no_coexistence_typecache = typecacheof(no_coexistence_typecache) if(ispath(spell_type)) if(isnull(limit)) @@ -68,13 +68,13 @@ if(!isnull(limit) && times >= limit) return FALSE for(var/spell in user.actions) - if(is_type_in_typecache(spell, no_coexistance_typecache)) + if(is_type_in_typecache(spell, no_coexistence_typecache)) return FALSE var/datum/antagonist/wizard/wizard_datum = user.mind.has_antag_datum(/datum/antagonist/wizard) if(!wizard_datum) return TRUE for(var/perks in wizard_datum.perks) - if(is_type_in_typecache(perks, no_coexistance_typecache)) + if(is_type_in_typecache(perks, no_coexistence_typecache)) return FALSE if(is_type_in_list(src, wizard_datum.perks)) to_chat(user, span_warning("This perk already learned!")) diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm index e7c204a39e2..585385e9d2d 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm @@ -55,7 +55,7 @@ it will become easier for others to find your item of power." spell_type = /datum/action/cooldown/spell/lichdom category = SPELLBOOK_CATEGORY_DEFENSIVE - no_coexistance_typecache = list(/datum/action/cooldown/spell/splattercasting, /datum/spellbook_entry/perks/wormborn) + no_coexistence_typecache = list(/datum/action/cooldown/spell/splattercasting, /datum/spellbook_entry/perks/wormborn) /datum/spellbook_entry/chuunibyou name = "Chuuni Invocations" diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm index 6b8272ed5b7..d65e14578ec 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm @@ -77,7 +77,7 @@ spell_type = /datum/action/cooldown/spell/conjure_item/infinite_guns/gun category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 3 - no_coexistance_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage) + no_coexistence_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage) /datum/spellbook_entry/arcane_barrage name = "Arcane Barrage" @@ -85,7 +85,7 @@ spell_type = /datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 3 - no_coexistance_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/gun) + no_coexistence_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/gun) /datum/spellbook_entry/barnyard name = "Barnyard Curse" @@ -99,7 +99,7 @@ draining from you over time. You can replenish it from your victims, specifically their necks." spell_type = /datum/action/cooldown/spell/splattercasting category = SPELLBOOK_CATEGORY_OFFENSIVE - no_coexistance_typecache = list(/datum/action/cooldown/spell/lichdom) + no_coexistence_typecache = list(/datum/action/cooldown/spell/lichdom) /datum/spellbook_entry/sanguine_strike name = "Exsanguinating Strike" diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm index 07c152d113d..6c4947639f6 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm @@ -60,7 +60,7 @@ desc = "Your soul is infested with mana worms. When you die, you will be reborn as a large worm. \ When the worm dies, it has no such luck. Parasitic infection prevents you from binding your soul to objects." hud_icon = "wormborn" - no_coexistance_typecache = list(/datum/action/cooldown/spell/lichdom) + no_coexistence_typecache = list(/datum/action/cooldown/spell/lichdom) /datum/spellbook_entry/perks/wormborn/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy) . = ..() diff --git a/code/modules/antagonists/wizard/equipment/teleport_rod.dm b/code/modules/antagonists/wizard/equipment/teleport_rod.dm index e0a5ce31145..47187dcc4f7 100644 --- a/code/modules/antagonists/wizard/equipment/teleport_rod.dm +++ b/code/modules/antagonists/wizard/equipment/teleport_rod.dm @@ -174,7 +174,7 @@ particle_effect.alpha = 200 var/original_duration = initial(duration) - if(original_duration == -1) + if(original_duration == STATUS_EFFECT_PERMANENT) return animate(particle_effect, alpha = 50, time = original_duration) @@ -185,7 +185,7 @@ /datum/status_effect/teleport_flux/perma id = "perma_teleport_flux" status_type = STATUS_EFFECT_REPLACE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/teleport_flux/perma remove_on_fullheal = FALSE diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 5f50618e77d..5da6816eb85 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -100,12 +100,12 @@ /obj/item/assembly_holder/update_overlays() . = ..() for(var/i in 1 to LAZYLEN(assemblies)) - if(i % 2 == 1) + if(IS_LEFT_INDEX(i)) var/obj/item/assembly/assembly = assemblies[i] . += "[assembly.icon_state]_left" for(var/left_overlay in assembly.attached_overlays) . += "[left_overlay]_l" - if(i % 2 == 0) + if(IS_RIGHT_INDEX(i)) var/obj/item/assembly/assembly = assemblies[i] var/mutable_appearance/right = mutable_appearance(icon, "[assembly.icon_state]_left") right.transform = matrix(-1, 0, 0, 0, 1, 0) diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 69436f7985d..4afa1bba731 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -157,7 +157,7 @@ return FALSE if((HAS_TRAIT(user, TRAIT_DUMB) || HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50)) var/which_hand = BODY_ZONE_PRECISE_L_HAND - if(!(user.active_hand_index % 2)) + if(IS_RIGHT_INDEX(user.active_hand_index)) which_hand = BODY_ZONE_PRECISE_R_HAND triggered(user, which_hand) user.visible_message(span_warning("[user] accidentally sets off [src], breaking their fingers."), \ @@ -206,7 +206,7 @@ if(finder) finder.visible_message(span_warning("[finder] accidentally sets off [src], breaking their fingers."), \ span_warning("You accidentally trigger [src]!")) - triggered(finder, (finder.active_hand_index % 2 == 0) ? BODY_ZONE_PRECISE_R_HAND : BODY_ZONE_PRECISE_L_HAND) + triggered(finder, (IS_RIGHT_INDEX(finder.active_hand_index)) ? BODY_ZONE_PRECISE_R_HAND : BODY_ZONE_PRECISE_L_HAND) return TRUE //end the search! else visible_message(span_warning("[src] snaps shut!")) diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index 47a2af4651c..c2ffe56fc57 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -15,6 +15,28 @@ /turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0) return +/turf/open/proc/set_active_hotspot(obj/effect/hotspot/new_lad) + if(active_hotspot == new_lad) + return + var/hotspot_around = NONE + if(active_hotspot) + if(new_lad) + hotspot_around = active_hotspot.smoothing_junction + if(!QDELETED(active_hotspot)) + QDEL_NULL(active_hotspot) + else + for(var/direction in GLOB.cardinals) + var/turf/open/open = get_step(src, direction) + if(!isopenturf(open) || !open.active_hotspot) + continue + var/existing_directions = open.active_hotspot.smoothing_junction + open.active_hotspot.set_smoothed_icon_state(existing_directions | REVERSE_DIR(direction)) + hotspot_around |= direction + + active_hotspot = new_lad + if(active_hotspot) + active_hotspot.set_smoothed_icon_state(hotspot_around) + /** * Handles the creation of hotspots and initial activation of turfs. * Setting the conditions for the reaction to actually happen for gasmixtures @@ -55,10 +77,13 @@ if(((exposed_temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) && (plas > 0.5 || trit > 0.5 || h2 > 0.5)) || \ ((exposed_temperature < FREON_MAXIMUM_BURN_TEMPERATURE) && (freon > 0.5))) - active_hotspot = new /obj/effect/hotspot(src, exposed_volume*25, exposed_temperature) + set_active_hotspot(new /obj/effect/hotspot(src, exposed_volume * 25, exposed_temperature)) + if(COOLDOWN_FINISHED(src, fire_puff_cooldown)) + playsound(src, 'sound/effects/fire_puff.ogg', 50) + COOLDOWN_START(src, fire_puff_cooldown, 5 SECONDS) active_hotspot.just_spawned = (current_cycle < SSair.times_fired) - //remove just_spawned protection if no longer processing this cell + //remove just_spawned protection if no longer processing this cell SSair.add_to_active(src) /** @@ -78,6 +103,9 @@ light_power = 1 light_color = LIGHT_COLOR_FIRE + /// base sprite used for our icon states when smoothing + /// BAAAASICALY the same as icon_state but is helpful to avoid duplicated work + var/fire_stage = "" /** * Volume is the representation of how big and healthy a fire is. * Hotspot volume will be divided by turf volume to get the ratio for temperature setting on non bypassing mode. @@ -93,7 +121,8 @@ var/visual_update_tick = 0 ///Are we burning freon? var/cold_fire = FALSE - + ///the group of hotspots we are a part of + var/datum/hot_group/our_hot_group /obj/effect/hotspot/Initialize(mapload, starting_volume, starting_temperature) . = ..() @@ -110,6 +139,24 @@ COMSIG_ATOM_ABSTRACT_ENTERED = PROC_REF(on_entered), ) AddElement(/datum/element/connect_loc, loc_connections) + var/turf/open/our_turf = loc + //on creation we check adjacent turfs for hot spot to start grouping, if surrounding do not have hot spots we create our own + for(var/turf/open/to_check as anything in our_turf.atmos_adjacent_turfs) + if(to_check.active_hotspot) + var/obj/effect/hotspot/enemy_spot = to_check.active_hotspot + if(!our_hot_group) + enemy_spot.our_hot_group.add_to_group(src) + else if(our_hot_group != enemy_spot.our_hot_group && enemy_spot.our_hot_group) //if we belongs to a hot group from prior loop and we encounter another hot spot with a group then we merge + our_hot_group.merge_hot_groups(enemy_spot.our_hot_group) + if(!our_hot_group)//if after loop through all the adjacents turfs and we havent belong to a group yet, make our own + our_hot_group = new + our_hot_group.add_to_group(src) + +/obj/effect/hotspot/set_smoothed_icon_state(new_junction) + + smoothing_junction = new_junction + + update_color() /** * Perform interactions between the hotspot and the gasmixture. @@ -130,7 +177,7 @@ if(!istype(location) || !(location.air)) return - location.active_hotspot = src + location.set_active_hotspot(src) bypassing = !just_spawned && (volume > CELL_VOLUME*0.95) @@ -199,7 +246,7 @@ sparkle_overlay.alpha = sparkle_amt * 255 add_overlay(sparkle_overlay) if(temperature > 400000 && temperature < 1500000) //Lightning because very anime. - var/mutable_appearance/lightning_overlay = mutable_appearance(icon, "overcharged") + var/mutable_appearance/lightning_overlay = mutable_appearance('icons/effects/fire.dmi', "overcharged") lightning_overlay.blend_mode = BLEND_ADD add_overlay(lightning_overlay) if(temperature > 4500000) //This is where noblium happens. Some fusion-y effects. @@ -264,7 +311,7 @@ perform_exposure() if(bypassing) - icon_state = "3" + set_fire_stage("heavy") if(!cold_fire) location.burn_tile() @@ -280,20 +327,30 @@ else if(volume > CELL_VOLUME*0.4) - icon_state = "2" + set_fire_stage("medium") else - icon_state = "1" + set_fire_stage("light") if((visual_update_tick++ % 7) == 0) update_color() return TRUE +/obj/effect/hotspot/proc/set_fire_stage(stage) + if(fire_stage == stage) + return + fire_stage = stage + icon_state = stage + dir = pick(GLOB.cardinals) + update_color() + /obj/effect/hotspot/Destroy() SSair.hotspots -= src var/turf/open/T = loc if(istype(T) && T.active_hotspot == src) - T.active_hotspot = null + our_hot_group.remove_from_group(src) + our_hot_group = null + T.set_active_hotspot(null) return ..() /obj/effect/hotspot/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) @@ -305,4 +362,95 @@ /obj/effect/hotspot/singularity_pull(atom/singularity, current_size) return +/datum/looping_sound/fire + mid_sounds = list('sound/effects/fireclip1.ogg' = 1, 'sound/effects/fireclip2.ogg' = 1, 'sound/effects/fireclip3.ogg' = 1, 'sound/effects/fireclip4.ogg' = 1, + 'sound/effects/fireclip5.ogg' = 1, 'sound/effects/fireclip6.ogg' = 1, 'sound/effects/fireclip7.ogg' = 1) + volume = 50 + mid_length = 2 SECONDS + falloff_distance = 1 + +#define MIN_SIZE_SOUND 2 +///handle the grouping of hotspot and then determining an average center to play sound in +/datum/hot_group + var/list/obj/effect/hotspot/spot_list = list() + ///the sound center turf which the looping sound will play + var/turf/open/current_sound_loc + var/datum/looping_sound/fire/sound + var/tiles_limit = 80 // arbitrary limit so we dont have one giant group + ///these lists and average var are to find the average center of a group + var/list/x_coord = list() + var/list/y_coord = list() + var/list/z_coord = list() + var/average_x + var/average_y + var/average_Z + ///the range for the sound to drop off based on the size of the group + var/drop_off_dist + COOLDOWN_DECLARE(update_sound_center) + + +/datum/hot_group/Destroy() + . = ..() + current_sound_loc = null + spot_list = null + qdel(sound) + +/datum/hot_group/proc/remove_from_group(obj/effect/hotspot/target) + spot_list -= target + var/turf/open/target_turf = target.loc + x_coord -= target_turf.x + y_coord -= target_turf.y + if(!length(spot_list)) + qdel(src) + return + +/datum/hot_group/proc/add_to_group(obj/effect/hotspot/target) + spot_list += target + target.our_hot_group = src + var/turf/open/target_turf = target.loc + x_coord += target_turf.x + y_coord += target_turf.y + z_coord += target_turf.z + if(COOLDOWN_FINISHED(src, update_sound_center) && length(spot_list) > MIN_SIZE_SOUND)//arbitrary size to start playing the sound + update_sound() + COOLDOWN_START(src, update_sound_center, 5 SECONDS) + +/datum/hot_group/proc/merge_hot_groups(datum/hot_group/enemy_group) + if(length(spot_list) >= tiles_limit || length(enemy_group.spot_list) >= tiles_limit) + return + var/datum/hot_group/saving_group + var/datum/hot_group/sacrificial_group + if(length(spot_list) > length(enemy_group.spot_list) || (length(spot_list) == length(enemy_group.spot_list) && prob(50)))//we're bigger take all of their territory! + saving_group = src + sacrificial_group = enemy_group + else + saving_group = enemy_group + sacrificial_group = src + for(var/obj/effect/hotspot/reference as anything in sacrificial_group.spot_list) + reference.our_hot_group = saving_group + saving_group.spot_list += sacrificial_group.spot_list + saving_group.x_coord += sacrificial_group.x_coord + saving_group.y_coord += sacrificial_group.y_coord + qdel(sacrificial_group) + if(COOLDOWN_FINISHED(src, update_sound_center) && length(spot_list) > MIN_SIZE_SOUND)//arbitrary size to start playing the sound + update_sound() + COOLDOWN_START(src, update_sound_center, 5 SECONDS) + +/datum/hot_group/proc/update_sound() + //we can draw a cross around the average middle of any globs of group, curves or hollow groups may cause issues with this + average_x = round((max(x_coord) + min(x_coord))/2) + average_y = round((max(y_coord) + min(y_coord))/2) + average_Z = round((min(z_coord) + max(z_coord))/2) + drop_off_dist = max((max(y_coord) - min(y_coord)), (max(x_coord) - min(x_coord)), 1)// pick the largest value between the width and length of the group to determine sound drop off + var/turf/open/sound_turf = locate(average_x, average_y, average_Z) + if(sound) + sound.falloff_distance = drop_off_dist + if(sound_turf != current_sound_loc) + sound.parent = sound_turf + return + sound = new(sound_turf, TRUE) + sound.falloff_distance = drop_off_dist + current_sound_loc = sound_turf + +#undef MIN_SIZE_SOUND #undef INSUFFICIENT diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm index bf6cbc8807a..c375735839a 100644 --- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm +++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm @@ -37,7 +37,7 @@ ///Are we active? var/excited = FALSE ///Our gas mix - var/datum/gas_mixture/turf/air + var/datum/gas_mixture/air ///If there is an active hotspot on us store a reference to it here var/obj/effect/hotspot/active_hotspot @@ -50,6 +50,8 @@ ///gas IDs of current active gas overlays var/list/atmos_overlay_types var/significant_share_ticker = 0 + ///the cooldown on playing a fire starting sound each time a tile is ignited + COOLDOWN_DECLARE(fire_puff_cooldown) #ifdef TRACK_MAX_SHARE var/max_share = 0 #endif @@ -693,5 +695,6 @@ Then we space some of our heat, and think about if we should stop conducting. temperature += heat / heat_capacity //The higher your own heat cap the less heat you get from this arrangement sharer.temperature -= heat / sharer.heat_capacity + #undef LAST_SHARE_CHECK #undef PLANET_SHARE_CHECK diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm index 7feee7f9cae..5263ae26c6d 100644 --- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm +++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm @@ -96,7 +96,7 @@ return connected_alarm.select_mode(parent.get_creator(), options_map[mode.value]) - connected_alarm.investigate_log("was turned to [connected_alarm.selected_mode.name] by [parent.get_creator()]") + connected_alarm.investigate_log("was turned to [connected_alarm.selected_mode.name] by [parent.get_creator()]", INVESTIGATE_ATMOS) /obj/item/circuit_component/air_alarm display_name = "Air Alarm Core Control" diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index 9d46a73e16c..99e014d564b 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -661,7 +661,7 @@ continue var/turf/node_turf = get_turf(node) - if(isplatingturf(node_turf) || iscatwalkturf(node_turf)) + if(node_turf.underfloor_accessibility > UNDERFLOOR_HIDDEN) continue var/connected_dir = get_dir(src, node) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 4fcfe158259..ff09ef15ea5 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -690,7 +690,7 @@ shielding_powered = !shielding_powered SSair.start_processing_machine(src) message_admins("[ADMIN_LOOKUPFLW(user)] turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.") - user.investigate_log("turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.") + user.investigate_log("turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.", INVESTIGATE_ATMOS) update_appearance() /// Ejects tank from canister, if any @@ -712,7 +712,7 @@ suppress_reactions = !suppress_reactions SSair.start_processing_machine(src) message_admins("[ADMIN_LOOKUPFLW(user)] turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.") - user.investigate_log("turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.") + user.investigate_log("turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.", INVESTIGATE_ATMOS) /obj/machinery/portable_atmospherics/canister/proc/recolor(datum/greyscale_modify_menu/menu) set_greyscale(menu.split_colors, menu.config.type) diff --git a/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm b/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm index 434f243d6a3..60f3ac82bb8 100644 --- a/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm @@ -153,7 +153,7 @@ internal_tank.suppress_reactions = !internal_tank.suppress_reactions SSair.start_processing_machine(internal_tank) message_admins("[ADMIN_LOOKUPFLW(usr)] turned [internal_tank.suppress_reactions ? "on" : "off"] the [internal_tank] reaction suppression.") - usr.investigate_log("turned [internal_tank.suppress_reactions ? "on" : "off"] the [internal_tank] reaction suppression.") + usr.investigate_log("turned [internal_tank.suppress_reactions ? "on" : "off"] the [internal_tank] reaction suppression.", INVESTIGATE_ATMOS) . = TRUE update_appearance() diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index 36d5bf4eb7c..99b75afa0f6 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -163,7 +163,7 @@ suppress_reactions = !suppress_reactions SSair.start_processing_machine(src) message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") - usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.", INVESTIGATE_ATMOS) . = TRUE update_appearance() diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index 29759e52e42..b1b094c9db0 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -185,7 +185,7 @@ suppress_reactions = !suppress_reactions SSair.start_processing_machine(src) message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") - usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.", INVESTIGATE_ATMOS) . = TRUE update_appearance() diff --git a/code/modules/autowiki/pages/fishing.dm b/code/modules/autowiki/pages/fishing.dm index eab26bd6c6c..ce2872235bb 100644 --- a/code/modules/autowiki/pages/fishing.dm +++ b/code/modules/autowiki/pages/fishing.dm @@ -44,7 +44,7 @@ if(fish::breeding_timeout != def_breeding) extra_info += "It takes [DisplayTimeText(fish::breeding_timeout)] to reproduce instead of [def_breeding_text]" if(length(extra_info)) - description += "
[extra_info.Join(extra_info,"
")]" + description += "
[extra_info.Join("
")]" var/list/output_list = list( "name" = full_capitalize(escape_value(fish::name)), diff --git a/code/modules/bitrunning/alerts.dm b/code/modules/bitrunning/alerts.dm index 54738b594c2..478ae3c04cb 100644 --- a/code/modules/bitrunning/alerts.dm +++ b/code/modules/bitrunning/alerts.dm @@ -7,6 +7,7 @@ name = "Domain Completed" desc = "The domain is completed. Activate to exit." timeout = 20 SECONDS + clickable_glow = TRUE /atom/movable/screen/alert/bitrunning/qserver_domain_complete/Click(location, control, params) . = ..() diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm index 7925461652a..1ed1647a672 100644 --- a/code/modules/bitrunning/components/avatar_connection.dm +++ b/code/modules/bitrunning/components/avatar_connection.dm @@ -64,7 +64,9 @@ var/alias = our_client?.prefs?.read_preference(/datum/preference/name/hacker_alias) || pick(GLOB.hacker_aliases) if(alias && avatar.real_name != alias) - avatar.fully_replace_character_name(avatar.real_name, alias) + avatar.fully_replace_character_name(newname = alias) + + update_avatar_id() for(var/skill_type in old_mind.known_skills) avatar.mind.set_experience(skill_type, old_mind.get_skill_exp(skill_type), silent = TRUE) @@ -111,6 +113,20 @@ )) +/// Updates our avatar's ID to match our avatar's name. +/datum/component/avatar_connection/proc/update_avatar_id() + var/mob/living/avatar = parent + var/obj/item/card/id/our_id = locate() in avatar.get_all_contents() + if(isnull(our_id)) + return + + our_id.registered_name = avatar.real_name + our_id.update_label() + our_id.update_icon() + if(our_id.registered_account) + our_id.registered_account.account_holder = avatar.real_name + + /// Disconnects the avatar and returns the mind to the old_body. /datum/component/avatar_connection/proc/full_avatar_disconnect(cause_damage = FALSE, datum/source) #ifndef UNIT_TESTS diff --git a/code/modules/bitrunning/objects/disks.dm b/code/modules/bitrunning/objects/disks.dm index 17b768c54d0..dd81a0f463e 100644 --- a/code/modules/bitrunning/objects/disks.dm +++ b/code/modules/bitrunning/objects/disks.dm @@ -185,3 +185,9 @@ /obj/item/crusher_trophy/vortex_talisman, /obj/item/crusher_trophy/ice_demon_cube, ) + +/obj/item/bitrunning_disk/item/mini_uzi + name = "bitrunning gear: mini-uzi" + selectable_items = list( + /obj/item/gun/ballistic/automatic/mini_uzi, + ) diff --git a/code/modules/bitrunning/objects/vendor.dm b/code/modules/bitrunning/objects/vendor.dm index d44630bc3be..f373a8617b4 100644 --- a/code/modules/bitrunning/objects/vendor.dm +++ b/code/modules/bitrunning/objects/vendor.dm @@ -33,7 +33,7 @@ /obj/machinery/computer/order_console/bitrunning/order_groceries(mob/living/purchaser, obj/item/card/id/card, list/groceries) var/list/things_to_order = list() for(var/datum/orderable_item/item as anything in groceries) - things_to_order[item.item_path] = groceries[item] + things_to_order[item.purchase_path] = groceries[item] var/datum/supply_pack/bitrunning/pack = new( purchaser = purchaser, \ diff --git a/code/modules/bitrunning/orders/bepis.dm b/code/modules/bitrunning/orders/bepis.dm index 4b7253bdaf2..f10eaaf3245 100644 --- a/code/modules/bitrunning/orders/bepis.dm +++ b/code/modules/bitrunning/orders/bepis.dm @@ -2,18 +2,18 @@ category_index = CATEGORY_BEPIS /datum/orderable_item/bepis/circuit_stack - item_path = /obj/item/stack/circuit_stack/full + purchase_path = /obj/item/stack/circuit_stack/full cost_per_order = 150 /datum/orderable_item/bepis/survival_pen - item_path = /obj/item/pen/survival + purchase_path = /obj/item/pen/survival cost_per_order = 150 /datum/orderable_item/bepis/party_sleeper - item_path = /obj/item/circuitboard/machine/sleeper/party + purchase_path = /obj/item/circuitboard/machine/sleeper/party cost_per_order = 750 desc = "A decommissioned sleeper circuitboard, repurposed for recreational purposes." /datum/orderable_item/bepis/sprayoncan - item_path = /obj/item/toy/sprayoncan + purchase_path = /obj/item/toy/sprayoncan cost_per_order = 750 diff --git a/code/modules/bitrunning/orders/flair.dm b/code/modules/bitrunning/orders/flair.dm index ef36348eb6a..0b1b79b03d0 100644 --- a/code/modules/bitrunning/orders/flair.dm +++ b/code/modules/bitrunning/orders/flair.dm @@ -2,39 +2,39 @@ category_index = CATEGORY_BITRUNNING_FLAIR /datum/orderable_item/bitrunning_flair/cornchips - item_path = /obj/item/food/cornchips + purchase_path = /obj/item/food/cornchips cost_per_order = 100 /datum/orderable_item/bitrunning_flair/mountain_wind - item_path = /obj/item/reagent_containers/cup/soda_cans/space_mountain_wind + purchase_path = /obj/item/reagent_containers/cup/soda_cans/space_mountain_wind cost_per_order = 100 /datum/orderable_item/bitrunning_flair/pwr_game - item_path = /obj/item/reagent_containers/cup/soda_cans/pwr_game + purchase_path = /obj/item/reagent_containers/cup/soda_cans/pwr_game cost_per_order = 200 /datum/orderable_item/bitrunning_flair/grey_bull - item_path = /obj/item/reagent_containers/cup/soda_cans/grey_bull + purchase_path = /obj/item/reagent_containers/cup/soda_cans/grey_bull cost_per_order = 200 /datum/orderable_item/bitrunning_flair/medkit - item_path = /obj/item/storage/medkit/brute + purchase_path = /obj/item/storage/medkit/brute desc = "Don't beat yourself up, it's just a game!" cost_per_order = 500 /datum/orderable_item/bitrunning_flair/medkit_fire - item_path = /obj/item/storage/medkit/fire + purchase_path = /obj/item/storage/medkit/fire desc = "Great after heated gaming sessions." cost_per_order = 500 /datum/orderable_item/bitrunning_flair/oval_sunglasses - item_path = /obj/item/clothing/glasses/sunglasses/oval + purchase_path = /obj/item/clothing/glasses/sunglasses/oval cost_per_order = 1000 /datum/orderable_item/bitrunning_flair/trenchcoat - item_path = /obj/item/clothing/suit/jacket/trenchcoat + purchase_path = /obj/item/clothing/suit/jacket/trenchcoat cost_per_order = 1000 /datum/orderable_item/bitrunning_flair/jackboots - item_path = /obj/item/clothing/shoes/jackboots + purchase_path = /obj/item/clothing/shoes/jackboots cost_per_order = 1000 diff --git a/code/modules/bitrunning/orders/tech.dm b/code/modules/bitrunning/orders/tech.dm index 9dd1db17c79..cb509fb6252 100644 --- a/code/modules/bitrunning/orders/tech.dm +++ b/code/modules/bitrunning/orders/tech.dm @@ -3,54 +3,54 @@ /datum/orderable_item/bitrunning_tech/item_tier1 cost_per_order = 750 - item_path = /obj/item/bitrunning_disk/item/tier1 + purchase_path = /obj/item/bitrunning_disk/item/tier1 desc = "This disk contains a program that lets you equip a medical beamgun, a C4 explosive, or a box of infinite pizza." /datum/orderable_item/bitrunning_tech/item_tier2 cost_per_order = 1250 - item_path = /obj/item/bitrunning_disk/item/tier2 + purchase_path = /obj/item/bitrunning_disk/item/tier2 desc = "This disk contains a program that lets you equip a luxury medipen, a pistol, or an armour vest." /datum/orderable_item/bitrunning_tech/item_tier3 cost_per_order = 2000 - item_path = /obj/item/bitrunning_disk/item/tier3 + purchase_path = /obj/item/bitrunning_disk/item/tier3 desc = "This disk contains a program that lets you equip an advanced energy gun, a dual bladed energy sword, or a minibomb." /datum/orderable_item/bitrunning_tech/ability_tier1 cost_per_order = 750 - item_path = /obj/item/bitrunning_disk/ability/tier1 + purchase_path = /obj/item/bitrunning_disk/ability/tier1 desc = "This disk contains a program that lets you cast Summon Cheese or Lesser Heal." /datum/orderable_item/bitrunning_tech/ability_tier2 cost_per_order = 1500 - item_path = /obj/item/bitrunning_disk/ability/tier2 + purchase_path = /obj/item/bitrunning_disk/ability/tier2 desc = "This disk contains a program that lets you cast Fireball, Lightning Bolt, or Forcewall." /datum/orderable_item/bitrunning_tech/ability_tier3 cost_per_order = 2500 - item_path = /obj/item/bitrunning_disk/ability/tier3 + purchase_path = /obj/item/bitrunning_disk/ability/tier3 desc = "This disk contains a program that lets you shapeshift into a lesser ashdrake, or a polar bear." /datum/orderable_item/bitrunning_tech/flip_skillchip - item_path = /obj/item/skillchip/matrix_taunt + purchase_path = /obj/item/skillchip/matrix_taunt cost_per_order = 1500 /datum/orderable_item/bitrunning_tech/pka_mod - item_path = /obj/item/bitrunning_disk/item/pka_mods + purchase_path = /obj/item/bitrunning_disk/item/pka_mods cost_per_order = 750 desc = "This disk contains a program that lets you equip modkits for the proto-kinetic accelerator. Proto-kinetic accelerator not included." /datum/orderable_item/bitrunning_tech/pka_mod/premium - item_path = /obj/item/bitrunning_disk/item/pka_mods/premium + purchase_path = /obj/item/bitrunning_disk/item/pka_mods/premium cost_per_order = 1600 desc = "This disk contains a program that lets you equip stronger modkits for the proto-kinetic accelerator. Proto-kinetic accelerator not included." /datum/orderable_item/bitrunning_tech/pkc_mod - item_path = /obj/item/bitrunning_disk/item/pkc_mods + purchase_path = /obj/item/bitrunning_disk/item/pkc_mods cost_per_order = 750 desc = "This disk contains a program that lets you equip trophies for the proto-kinetic crusher. Proto-kinetic crusher no included." /datum/orderable_item/bitrunning_tech/pkc_mod/premium - item_path = /obj/item/bitrunning_disk/item/pkc_mods/premium + purchase_path = /obj/item/bitrunning_disk/item/pkc_mods/premium cost_per_order = 1600 desc = "This disk contains a program that lets you equip stronger trophies for the proto-kinetic crusher. Proto-kinetic crusher not included." diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index 672a79ba72c..5e29c1729b4 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -45,7 +45,7 @@ /// Maximum rate at which a glitch can spawn var/threat_prob_max = 15 /// The turfs we can place a hololadder on. - var/turf/exit_turfs = list() + var/list/turf/exit_turfs = list() /// Determines if we broadcast to entertainment monitors or not var/broadcasting = FALSE /// Cooldown between being able to toggle broadcasting diff --git a/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm b/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm index 84eb53e026a..39699072057 100644 --- a/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm +++ b/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm @@ -13,10 +13,10 @@ /obj/item/toy/beach_ball = 2, /obj/item/clothing/shoes/sandal = 1, /obj/item/clothing/glasses/sunglasses = 1, - /obj/item/gun/ballistic/automatic/mini_uzi = 1, + /obj/item/gun/energy/laser/chameleon/ballistic_only = 1, + /obj/item/bitrunning_disk/item/mini_uzi = 1, ) - /datum/lazy_template/virtual_domain/island_brawl/setup_domain(list/created_atoms) for(var/obj/effect/mob_spawn/ghost_role/human/virtual_domain/islander/spawner in created_atoms) custom_spawns += spawner diff --git a/code/modules/capture_the_flag/ctf_game.dm b/code/modules/capture_the_flag/ctf_game.dm index 2f292218e79..968e6e2953e 100644 --- a/code/modules/capture_the_flag/ctf_game.dm +++ b/code/modules/capture_the_flag/ctf_game.dm @@ -403,7 +403,7 @@ alpha = 255 /obj/structure/trap/ctf/examine(mob/user) - return + return list() /obj/structure/trap/ctf/trap_effect(mob/living/living) if(!is_ctf_target(living)) diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index 23e578c2ed2..a93fdc97d8e 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -174,7 +174,7 @@ name = "Crayons" description = "Dr. Jones's kid ate all of our crayons again. Please send us yours." reward = CARGO_CRATE_VALUE * 4 - required_count = 24 + required_count = 8 wanted_types = list(/obj/item/toy/crayon = TRUE) /datum/bounty/item/assistant/pens diff --git a/code/modules/cargo/bounties/botany.dm b/code/modules/cargo/bounties/botany.dm index 905122584bf..042bb60636f 100644 --- a/code/modules/cargo/bounties/botany.dm +++ b/code/modules/cargo/bounties/botany.dm @@ -1,8 +1,8 @@ /datum/bounty/item/botany reward = CARGO_CRATE_VALUE * 10 - var/datum/bounty/item/botany/multiplier = 0 //adds bonus reward money; increased for higher tier or rare mutations - var/datum/bounty/item/botany/bonus_desc //for adding extra flavor text to bounty descriptions - var/datum/bounty/item/botany/foodtype = "meal" //same here + var/multiplier = 0 //adds bonus reward money; increased for higher tier or rare mutations + var/bonus_desc //for adding extra flavor text to bounty descriptions + var/foodtype = "meal" //same here /datum/bounty/item/botany/New() ..() diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index 1ba739d3343..bb1c4ea8a8b 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -303,7 +303,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a return if (!isnum(boomInput[i])) //If the user doesn't input a number, set that specific explosion value to zero tgui_alert(usr, "That wasn't a number! Value set to default (zero) instead.") - boomInput = 0 + boomInput[i] = 0 explosionChoice = 1 temp_pod.explosionSize = boomInput . = TRUE diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm index f7ce106d580..0b6451e5698 100644 --- a/code/modules/cargo/goodies.dm +++ b/code/modules/cargo/goodies.dm @@ -259,7 +259,7 @@ name = "Can of Fish Food Single-Pack" desc = "For keeping your little friends fed and alive." cost = PAYCHECK_CREW - contains = list(/obj/item/fish_feed) + contains = list(/obj/item/reagent_containers/cup/fish_feed) /datum/supply_pack/goody/naturalbait name = "Freshness Jars full of Natural Bait Single-Pack" diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm index b0ea8948580..bb8d3da04d8 100644 --- a/code/modules/cargo/markets/market_items/misc.dm +++ b/code/modules/cargo/markets/market_items/misc.dm @@ -125,6 +125,17 @@ stock_max = 1 availability_prob = 15 +/datum/market_item/misc/fishing_capsule + name = "Fishing Spot Capsule" + //IUU stands for Illegal Unreported and Unregulated fishing. Ironic. + desc = "A repurposed mining capsule connected to a selection of exclusive fishing spots. Approved by the Intergalactic IUU Fishing Association." + price_min = CARGO_CRATE_VALUE * 1.125 + price_max = CARGO_CRATE_VALUE * 2.125 + item = /obj/item/survivalcapsule/fishing + stock_min = 1 + stock_max = 4 + availability_prob = 80 + /datum/market_item/misc/fish name = "Fish" desc = "Fish! Fresh fish! Fish you can cut, grind and even keep in aquarium if you want to! Get some before the next fight at my village breaks out!" @@ -134,12 +145,3 @@ stock_min = 3 stock_max = 8 availability_prob = 90 - -/datum/market_item/misc/giant_wrench_parts - name = "Big Slappy parts" - desc = "Cheap illegal Big Slappy parts. The fastest and statistically most dangerous wrench." - item = /obj/item/weaponcrafting/giant_wrench - price_min = CARGO_CRATE_VALUE * 2 - price_max = CARGO_CRATE_VALUE * 5 - stock_max = 1 - availability_prob = 25 diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm index 12241450ba5..f9fd6245c8d 100644 --- a/code/modules/cargo/markets/market_items/weapons.dm +++ b/code/modules/cargo/markets/market_items/weapons.dm @@ -64,6 +64,16 @@ stock_max = 3 availability_prob = 45 +/datum/market_item/weapon/carpenter_hammer + name = "Carpenter hammer" + desc = "When you really want to look like a psycho..." + item = /obj/item/carpenter_hammer + + price_min = CARGO_CRATE_VALUE * 1 + price_max = CARGO_CRATE_VALUE * 1.25 + stock_max = 2 + availability_prob = 65 + /datum/market_item/weapon/emp_grenade name = "EMP Grenade" desc = "Use this grenade for SHOCKING results!" @@ -92,3 +102,12 @@ price_max = CARGO_CRATE_VALUE * 50 stock_max = 1 availability_prob = 15 + +/datum/market_item/weapon/giant_wrench_parts + name = "Big Slappy parts" + desc = "Cheap illegal Big Slappy parts. The fastest and statistically most dangerous wrench." + item = /obj/item/weaponcrafting/giant_wrench + price_min = CARGO_CRATE_VALUE * 2 + price_max = CARGO_CRATE_VALUE * 5 + stock_max = 1 + availability_prob = 25 diff --git a/code/modules/cargo/packs/emergency.dm b/code/modules/cargo/packs/emergency.dm index 2d19c276ec1..3cbe3b56aff 100644 --- a/code/modules/cargo/packs/emergency.dm +++ b/code/modules/cargo/packs/emergency.dm @@ -24,7 +24,7 @@ cost = CARGO_CRATE_VALUE * 4 contains = list( /mob/living/basic/bot/medbot = 2, - /mob/living/simple_animal/bot/floorbot = 2, + /mob/living/basic/bot/repairbot = 2, /obj/item/tank/internals/emergency_oxygen = 5, /obj/item/clothing/mask/breath = 5, ) diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm index aa9eef75875..894bd2ca9cc 100644 --- a/code/modules/cargo/packs/medical.dm +++ b/code/modules/cargo/packs/medical.dm @@ -199,6 +199,14 @@ contains = list(/obj/item/lazarus_injector = 2) crate_name = "Lazarus injectors crate" +/datum/supply_pack/medical/paperwork_implants + name = "Paperwork Implant Set" + desc = "A crate containing two implants, which can be surgically implanted to effectivize crewmembers at paperwork. Warranty void if exposed to electromagnetic pulses." + cost = CARGO_CRATE_VALUE * 3 + contains = list(/obj/item/organ/cyberimp/arm/paperwork = 2) + crate_name = "Paperwork implant crate" + discountable = SUPPLY_PACK_RARE_DISCOUNTABLE + /datum/supply_pack/medical/lost_crew name = "Recovered NT Employee corpse" desc = "We don't have the accomodations to bring them back, could we send them to you? Please revive and employ them. \ diff --git a/code/modules/cargo/packs/service.dm b/code/modules/cargo/packs/service.dm index dda48dc9e57..89f80cd7a5b 100644 --- a/code/modules/cargo/packs/service.dm +++ b/code/modules/cargo/packs/service.dm @@ -237,12 +237,16 @@ /datum/supply_pack/service/randomized/ready_donk name = "Ready-Donk Variety Crate" - desc = "Featuring a line up of Donk Co.'s most popular pastry! Contains \ + desc = "Featuring a line up of Donk Co.'s fan-favourite microwave meal! Contains \ a random assortment of Ready Donk products." cost = CARGO_CRATE_VALUE * 3 contains = list(/obj/item/food/ready_donk, /obj/item/food/ready_donk/mac_n_cheese, /obj/item/food/ready_donk/donkhiladas, + /obj/item/food/ready_donk/nachos_grandes, + /obj/item/food/ready_donk/donkrange_chicken, + /obj/item/food/ready_donk/salisbury_steak, + /obj/item/food/ready_donk/country_chicken, ) crate_name = "\improper Ready-Donk crate" crate_type = /obj/structure/closet/crate/freezer/donk @@ -296,7 +300,7 @@ cost = CARGO_CRATE_VALUE * 5 contains = list(/obj/item/book/manual/fish_catalog, /obj/item/storage/fish_case/random/freshwater = 3, - /obj/item/fish_feed, + /obj/item/reagent_containers/cup/fish_feed, /obj/item/storage/box/aquarium_props, /obj/item/aquarium_kit, ) diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index da3356e8250..154aa2ce83a 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -444,7 +444,7 @@ if(ismob(to_insert)) if(!reverse_option_list["Mobs"]) return FALSE - if(!isliving(to_insert)) //let's not put ghosts or camera mobs inside + if(!isliving(to_insert)) //let's not put ghosts or eye mobs inside return FALSE var/mob/living/mob_to_insert = to_insert if(mob_to_insert.anchored || mob_to_insert.incorporeal_move) diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm index 4ca500a9e71..08e79c305a4 100644 --- a/code/modules/client/client_colour.dm +++ b/code/modules/client/client_colour.dm @@ -220,6 +220,9 @@ /datum/client_colour/malfunction colour = list(/*R*/ 0,0,0,0, /*G*/ 0,175,0,0, /*B*/ 0,0,0,0, /*A*/ 0,0,0,1, /*C*/0,-130,0,0) // Matrix colors +/datum/client_colour/perceptomatrix + colour = list(/*R*/ 1,0,0,0, /*G*/ 0,1,0,0, /*B*/ 0,0,1,0, /*A*/ 0,0,0,1, /*C*/0,-0.02,-0.02,0) // veeery slightly pink + /datum/client_colour/monochrome colour = COLOR_MATRIX_GRAYSCALE priority = PRIORITY_HIGH //we can't see colors anyway! diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index de17fea971a..ea1bee354e4 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -275,3 +275,7 @@ /// Discord information of the client if they have linked their account and we fetched it var/list/discord + + /// Does this client's mob need to rebuild its plane masters after login? + /// This is currently only used so a client can switch between 515 and 516 without breaking their rendering. + var/rebuild_plane_masters = FALSE diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index f56ad3562a3..9fff4e49763 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -329,10 +329,20 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(GLOB.player_details[ckey]) reconnecting = TRUE player_details = GLOB.player_details[ckey] - player_details.byond_version = full_version + var/old_version = player_details.byond_version + player_details.byond_version = byond_version + player_details.byond_build = byond_build + +#if MIN_COMPILER_VERSION > 516 + #warn Fully change default relay_loc to "1,1", rather than changing it based on client version +#endif + if(old_version != byond_version) + rebuild_plane_masters = TRUE + else player_details = new(ckey) - player_details.byond_version = full_version + player_details.byond_version = byond_version + player_details.byond_build = byond_build GLOB.player_details[ckey] = player_details @@ -735,22 +745,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( qdel(query_datediff) qdel(query_get_client_age) if(!new_player) - var/datum/db_query/query_log_player = SSdbcore.NewQuery( + SSdbcore.FireAndForget( "UPDATE [format_table_name("player")] SET lastseen = Now(), lastseen_round_id = :round_id, ip = INET_ATON(:ip), computerid = :computerid, lastadminrank = :admin_rank, accountjoindate = :account_join_date WHERE ckey = :ckey", list("round_id" = GLOB.round_id, "ip" = address, "computerid" = computer_id, "admin_rank" = admin_rank, "account_join_date" = account_join_date || null, "ckey" = ckey) ) - if(!query_log_player.Execute()) - qdel(query_log_player) - return - qdel(query_log_player) if(!account_join_date) account_join_date = "Error" - var/datum/db_query/query_log_connection = SSdbcore.NewQuery({" + SSdbcore.FireAndForget({" INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`server_ip`,`server_port`,`round_id`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),INET_ATON(:internet_address),:port,:round_id,:ckey,INET_ATON(:ip),:computerid) "}, list("internet_address" = world.internet_address || "0", "port" = world.port, "round_id" = GLOB.round_id, "ckey" = ckey, "ip" = address, "computerid" = computer_id)) - query_log_connection.Execute() - qdel(query_log_connection) SSserver_maint.UpdateHubStatus() diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm index 3a880dcdbb5..d0982a0c312 100644 --- a/code/modules/client/player_details.dm +++ b/code/modules/client/player_details.dm @@ -1,6 +1,6 @@ ///assoc list of ckey -> /datum/player_details -GLOBAL_LIST_EMPTY(player_details) +GLOBAL_LIST_EMPTY_TYPED(player_details, /datum/player_details) /// Tracks information about a client between log in and log outs /datum/player_details @@ -18,8 +18,10 @@ GLOBAL_LIST_EMPTY(player_details) /// Lazylist of preference slots this client has joined the round under /// Numbers are stored as strings var/list/joined_as_slots - /// Version of byond this client is using - var/byond_version = "Unknown" + /// Major version of BYOND this client is using. + var/byond_version + /// Build number of BYOND this client is using. + var/byond_build /// Tracks achievements they have earned var/datum/achievement_data/achievements /// World.time this player last died @@ -35,6 +37,12 @@ GLOBAL_LIST_EMPTY(player_details) previous_names += html_encode("[previous_name] ([played_names[previous_name]])") return previous_names.Join("; ") +/// Returns the full version string (i.e 515.1642) of the BYOND version and build. +/datum/player_details/proc/full_byond_version() + if(!byond_version) + return "Unknown" + return "[byond_version].[byond_build || "xxx"]" + /// Adds the new names to the player's played_names list on their /datum/player_details for use of admins. /// `ckey` should be their ckey, and `data` should be an associative list with the keys being the names they played under and the values being the unique mob ID tied to that name. /proc/log_played_names(ckey, data) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 51a3bdbda16..8a39b457e49 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -537,7 +537,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) return default_randomization /datum/preferences/proc/refresh_membership() - if(!istype(parent)) + set waitfor = FALSE + + if(!istype(parent, /client)) return var/patron = parent.check_patreon() diff --git a/code/modules/client/preferences/darkened_flash.dm b/code/modules/client/preferences/accessibility.dm similarity index 51% rename from code/modules/client/preferences/darkened_flash.dm rename to code/modules/client/preferences/accessibility.dm index ef89467bf35..26f0a790985 100644 --- a/code/modules/client/preferences/darkened_flash.dm +++ b/code/modules/client/preferences/accessibility.dm @@ -4,3 +4,10 @@ default_value = FALSE savefile_key = "darkened_flash" savefile_identifier = PREFERENCE_PLAYER + +/// When toggled, will darken the screen on screen shake +/datum/preference/toggle/screen_shake_darken + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + default_value = FALSE + savefile_key = "screen_shake_darken" + savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm index e456d366b96..e2802b3d29d 100644 --- a/code/modules/client/preferences/species_features/basic.dm +++ b/code/modules/client/preferences/species_features/basic.dm @@ -38,14 +38,12 @@ if (!initial(eyes_organ.eye_color_left)) eyes_organ.eye_color_left = value - eyes_organ.old_eye_color_left = value if(hetero) // Don't override the snowflakes please return if (!initial(eyes_organ.eye_color_right)) eyes_organ.eye_color_right = value - eyes_organ.old_eye_color_right = value eyes_organ.refresh() /datum/preference/color/eye_color/create_default_value() diff --git a/code/modules/clothing/chameleon/chameleon_action_subtypes.dm b/code/modules/clothing/chameleon/chameleon_action_subtypes.dm index bd15bb908f2..1c433f0c0d9 100644 --- a/code/modules/clothing/chameleon/chameleon_action_subtypes.dm +++ b/code/modules/clothing/chameleon/chameleon_action_subtypes.dm @@ -301,3 +301,7 @@ . = ..() for(var/other_type in other_cham_types) add_chameleon_items(other_type) + +/datum/action/item_action/chameleon/change/gun/ballistic + chameleon_type = /obj/item/gun/ballistic + chameleon_name = "Gun" diff --git a/code/modules/clothing/chameleon/chameleon_gun.dm b/code/modules/clothing/chameleon/chameleon_gun.dm index dafd0ba5f62..4a282a3e75a 100644 --- a/code/modules/clothing/chameleon/chameleon_gun.dm +++ b/code/modules/clothing/chameleon/chameleon_gun.dm @@ -11,14 +11,15 @@ /// The badmin mode. Makes your projectiles act like the real deal. var/real_hits = FALSE + /// how it looks by default. + var/default_look = /obj/item/gun/energy/laser /obj/item/gun/energy/laser/chameleon/Initialize(mapload) . = ..() - recharge_newshot() AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) // Init order shenanigans dictate we have to do this last so we can't just use `active_type` var/datum/action/item_action/chameleon/change/gun/gun_action = locate() in actions - gun_action?.update_look(/obj/item/gun/energy/laser) + gun_action?.update_look(default_look) /** * Description: Resets the currently loaded chameleon variables, essentially resetting it to brand new. @@ -54,36 +55,34 @@ inhand_x_dimension = gun_to_set.inhand_x_dimension inhand_y_dimension = gun_to_set.inhand_y_dimension + // We dupe this casing and then delete it at the end, to grab the projectile. + var/obj/item/ammo_casing/casing_to_dupe + if(istype(gun_to_set, /obj/item/gun/ballistic)) var/obj/item/gun/ballistic/ball_gun = gun_to_set - var/obj/item/ammo_box/ball_ammo = new ball_gun.spawn_magazine_type(gun_to_set) - qdel(ball_gun) - - if(!istype(ball_ammo) || !ball_ammo.ammo_type) - qdel(ball_ammo) - return FALSE - - var/obj/item/ammo_casing/ball_cartridge = new ball_ammo.ammo_type(gun_to_set) - set_chameleon_ammo(ball_cartridge) + // We also need to copy the starting magazine for ballistics. + casing_to_dupe = initial(ball_gun.spawn_magazine_type.ammo_type) + casing_to_dupe = new casing_to_dupe(src) else if(istype(gun_to_set, /obj/item/gun/magic)) var/obj/item/gun/magic/magic_gun = gun_to_set - var/obj/item/ammo_casing/magic_cartridge = new magic_gun.ammo_type(gun_to_set) - set_chameleon_ammo(magic_cartridge) + casing_to_dupe = new magic_gun.ammo_type(src) else if(istype(gun_to_set, /obj/item/gun/energy)) var/obj/item/gun/energy/energy_gun = gun_to_set + // Even if the energy gun has multiple ammo types, we copy the first. Energy guns always (should) have a list in ammo_type. if(islist(energy_gun.ammo_type) && energy_gun.ammo_type.len) - var/obj/item/ammo_casing/energy_cartridge = energy_gun.ammo_type[1] - set_chameleon_ammo(energy_cartridge) + var/obj/item/first_casing = energy_gun.ammo_type[1] + casing_to_dupe = new first_casing.type(src) else if(istype(gun_to_set, /obj/item/gun/syringe)) - var/obj/item/ammo_casing/syringe_cartridge = new /obj/item/ammo_casing/syringegun(src) - set_chameleon_ammo(syringe_cartridge) + casing_to_dupe = new /obj/item/ammo_casing/syringegun(src) else - var/obj/item/ammo_casing/default_cartridge = new /obj/item/ammo_casing(src) - set_chameleon_ammo(default_cartridge) + casing_to_dupe = new /obj/item/ammo_casing(src) + + set_chameleon_ammo(casing_to_dupe) + qdel(casing_to_dupe) /** * Description: Sets the ammo type our gun should have. @@ -155,3 +154,11 @@ var/obj/item/gun/new_gun = new guntype(src) set_chameleon_gun(new_gun) qdel(new_gun) + +/obj/item/gun/energy/laser/chameleon/ballistic_only + actions_types = list(/datum/action/item_action/chameleon/change/gun/ballistic) + default_look = /obj/item/gun/ballistic/automatic/mini_uzi + +/obj/item/gun/energy/laser/chameleon/ballistic_only/Initialize(mapload) + . = ..() + set_chameleon_disguise(default_look) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 0c0e73940d2..9f68581e3ab 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -431,9 +431,9 @@ parts_covered += "head" if(body_parts_covered & CHEST) parts_covered += "torso" - if(body_parts_covered & ARMS|HANDS) + if(body_parts_covered & (ARMS|HANDS)) parts_covered += "arms" - if(body_parts_covered & LEGS|FEET) + if(body_parts_covered & (LEGS|FEET)) parts_covered += "legs" if(length(parts_covered)) readout += "[output_string] will protect the wearer's [english_list(parts_covered)] from [span_tooltip("The extremely low pressure is the biggest danger posed by the vacuum of space.", "low pressure")]." diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 418f8358f4d..7d990e1ace5 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -12,7 +12,6 @@ siemens_coefficient = 0.5 body_parts_covered = HANDS slot_flags = ITEM_SLOT_GLOVES - equip_sound = 'sound/items/equip/glove_equip.ogg' drop_sound = 'sound/items/handling/glove_drop.ogg' pickup_sound = 'sound/items/handling/glove_pick_up.ogg' attack_verb_continuous = list("challenges") diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index ab6e03ae493..3c8edb4de73 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -6,6 +6,7 @@ equip_delay_other = 60 species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion clothing_traits = list(TRAIT_CHUNKYFINGERS) + equip_sound = 'sound/items/equip/glove_equip.ogg' /// Determines the version of boxing (or any martial art for that matter) that the boxing gloves gives var/style_to_give = /datum/martial_art/boxing diff --git a/code/modules/clothing/gloves/insulated.dm b/code/modules/clothing/gloves/insulated.dm index c7acc7f87e2..10791ad09f7 100644 --- a/code/modules/clothing/gloves/insulated.dm +++ b/code/modules/clothing/gloves/insulated.dm @@ -13,6 +13,7 @@ custom_price = PAYCHECK_CREW * 10 custom_premium_price = PAYCHECK_COMMAND * 6 cut_type = /obj/item/clothing/gloves/cut + equip_sound = 'sound/items/equip/glove_equip.ogg' /obj/item/clothing/gloves/color/yellow/Initialize(mapload) . = ..() diff --git a/code/modules/clothing/gloves/plasmaman.dm b/code/modules/clothing/gloves/plasmaman.dm index d429ab10fd8..36fd467282b 100644 --- a/code/modules/clothing/gloves/plasmaman.dm +++ b/code/modules/clothing/gloves/plasmaman.dm @@ -9,6 +9,7 @@ max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE armor_type = /datum/armor/color_plasmaman + equip_sound = 'sound/items/equip/glove_equip.ogg' /datum/armor/color_plasmaman bio = 100 diff --git a/code/modules/clothing/gloves/special.dm b/code/modules/clothing/gloves/special.dm index 7a005fe7fc3..09df92bb8c4 100644 --- a/code/modules/clothing/gloves/special.dm +++ b/code/modules/clothing/gloves/special.dm @@ -115,6 +115,7 @@ armor_type = /datum/armor/latex_gloves clothing_traits = list(TRAIT_QUICK_CARRY) resistance_flags = NONE + equip_sound = 'sound/items/equip/glove_equip.ogg' /datum/armor/latex_gloves bio = 100 diff --git a/code/modules/clothing/gloves/tacklers.dm b/code/modules/clothing/gloves/tacklers.dm index ce3db5ab654..9564c20bfde 100644 --- a/code/modules/clothing/gloves/tacklers.dm +++ b/code/modules/clothing/gloves/tacklers.dm @@ -8,6 +8,7 @@ resistance_flags = NONE custom_premium_price = PAYCHECK_COMMAND * 3.5 clothing_traits = list(TRAIT_FINGERPRINT_PASSTHROUGH,TRAIT_FAST_CUFFING) + equip_sound = 'sound/items/equip/glove_equip.ogg' /// For storing our tackler datum so we can remove it after var/datum/component/tackler /// See: [/datum/component/tackler/var/stamina_cost] diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 56a8661a412..3b74106d436 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -143,7 +143,7 @@ /obj/item/clothing/head/utility/hardhat/welding/adjust_visor(mob/living/user) . = ..() if(.) - playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 50, TRUE) + playsound(src, up ? SFX_VISOR_UP : SFX_VISOR_DOWN, 50, TRUE) /obj/item/clothing/head/utility/hardhat/welding/worn_overlays(mutable_appearance/standing, isinhands) . = ..() diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 186468c83ac..c5b897ceaf1 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -39,6 +39,7 @@ drop_sound = 'sound/items/handling/helmet/helmet_drop1.ogg' visor_toggle_up_sound = SFX_VISOR_UP visor_toggle_down_sound = SFX_VISOR_DOWN + hair_mask = HAIR_MASK_HIDE_ABOVE_45_DEG_LOW /obj/item/clothing/head/helmet/sec/Initialize(mapload) . = ..() @@ -68,6 +69,17 @@ return ..() +/obj/item/clothing/head/helmet/sec/attack_self(mob/user) + . = ..() + if(.) + return + balloon_alert(user, "[flags_inv & HIDEHAIR ? "loosening" : "tightening"] straps...") + if(!do_after(user, 3 SECONDS, src)) + return + flags_inv ^= HIDEHAIR + balloon_alert(user, "[flags_inv & HIDEHAIR ? "tightened" : "loosened"] straps") + return TRUE + /obj/item/clothing/head/helmet/sec/click_alt(mob/user) flipped_visor = !flipped_visor balloon_alert(user, "visor flipped") @@ -713,7 +725,6 @@ dog_fashion = /datum/dog_fashion/head/holymelon armor_type = /datum/armor/helmet_watermelon max_integrity = 15 - var/decayed = FALSE /obj/item/clothing/head/helmet/durability/holymelon/fire_resist resistance_flags = FIRE_PROOF @@ -721,13 +732,10 @@ /obj/item/clothing/head/helmet/durability/holymelon/Initialize(mapload) . = ..() - if(decayed) - decay() - return AddComponent( /datum/component/anti_magic, \ - antimagic_flags = MAGIC_RESISTANCE_HOLY, \ + antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, \ inventory_flags = ITEM_SLOT_OCLOTHING, \ charges = 1, \ drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \ diff --git a/code/modules/clothing/head/mind_monkey_helmet.dm b/code/modules/clothing/head/mind_monkey_helmet.dm index e9ff9978239..602f64eefb2 100644 --- a/code/modules/clothing/head/mind_monkey_helmet.dm +++ b/code/modules/clothing/head/mind_monkey_helmet.dm @@ -12,8 +12,8 @@ var/light_colors = 1 ///which icon state color this is (red, blue, yellow) /// This chance is increased by 7 every time the helmet fails to get a host, to dissuade spam. starts negative to add 1 safe reuse var/rage_chance = -7 - /// Holds the steam effect at dangerous rage chance levels. - var/obj/effect/abstract/particle_holder/particle_effect + /// Currently used particle type + var/particle_path /obj/item/clothing/head/helmet/monkey_sentience/Initialize(mapload) . = ..() @@ -60,7 +60,8 @@ UnregisterSignal(magnification, COMSIG_SPECIES_LOSS) magnification = null visible_message(span_notice("[src] falls silent and drops on the floor. Maybe you should try again later?")) - var/particle_path + if (particle_path) + remove_shared_particles(particle_path) switch(rage_chance) if(-7 to 0) user.visible_message(span_notice("[src] falls silent and drops on the floor. Try again later?")) @@ -83,11 +84,10 @@ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE) particle_path = /particles/smoke/steam rage_chance += 7 - - QDEL_NULL(particle_effect) if(particle_path) - particle_effect = new(src, particle_path) - QDEL_IN(particle_effect, 2 MINUTES) + add_shared_particles(particle_path) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, remove_shared_particles), particle_path), 2 MINUTES) + addtimer(VARSET_CALLBACK(src, particle_path, null), 2 MINUTES) if((rage_chance > 0) && prob(rage_chance)) // too much spam means agnry gorilla running at you malfunction(user) diff --git a/code/modules/clothing/head/perceptomatrix.dm b/code/modules/clothing/head/perceptomatrix.dm new file mode 100644 index 00000000000..4250a1e3996 --- /dev/null +++ b/code/modules/clothing/head/perceptomatrix.dm @@ -0,0 +1,248 @@ + +#define PERCEPTOMATRIX_INACTIVE_FLAGS SNUG_FIT|STACKABLE_HELMET_EXEMPT|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT +#define PERCEPTOMATRIX_ACTIVE_FLAGS PERCEPTOMATRIX_INACTIVE_FLAGS|CASTING_CLOTHES // we love casting spells + +/// Helmet which can turn you into a BEAST!! once an anomaly core is inserted +/obj/item/clothing/head/helmet/perceptomatrix + name = "perceptomatrix helm" + desc = "This piece of headgear harnesses the energies of a hallucinative anomaly to create a safe audiovisual replica of -all- external stimuli directly into the cerebral cortex, \ + granting the user effective immunity to both psychic threats, and anything that would affect their perception - be it ear, eye, or even brain damage. \ + It can also violently discharge said energy, inducing hallucinations in others." + icon_state = "perceptomatrix_helmet_inactive" + worn_icon_state = "perceptomatrix_helmet_inactive" + base_icon_state = "perceptomatrix_helmet" + force = 10 + dog_fashion = null + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 8 SECONDS + clothing_flags = PERCEPTOMATRIX_ACTIVE_FLAGS + clothing_traits = list( + /* eye/ear protection */ + TRAIT_NOFLASH, + TRAIT_TRUE_NIGHT_VISION, + TRAIT_SIGHT_BYPASS, + TRAIT_EXPANDED_FOV, + TRAIT_GOOD_HEARING, + /* mental protection */ + TRAIT_PERCEPTUAL_TRAUMA_BYPASS, + TRAIT_RDS_SUPPRESSED, + TRAIT_MADNESS_IMMUNE, + TRAIT_HALLUCINATION_IMMUNE, + /* psychic protection */ + TRAIT_NO_MINDSWAP, + TRAIT_UNCONVERTABLE, + ) + flags_cover = HEADCOVERSEYES|EARS_COVERED + flags_inv = HIDEHAIR|HIDEFACE + flash_protect = FLASH_PROTECTION_WELDER_SENSITIVE + resistance_flags = FIRE_PROOF | ACID_PROOF + equip_sound = 'sound/items/handling/helmet/helmet_equip1.ogg' + pickup_sound = 'sound/items/handling/helmet/helmet_pickup1.ogg' + drop_sound = 'sound/items/handling/helmet/helmet_drop1.ogg' + armor_type = /datum/armor/head_helmet_matrix + actions_types = list(/datum/action/cooldown/spell/pointed/percept_hallucination) + + /// If we have a core or not + var/core_installed = FALSE + /// Active components to add onto the mob, deleted and created on core installation/removal + var/list/active_components = list() + +// weaker overall but better against energy +/datum/armor/head_helmet_matrix + melee = 15 + bullet = 15 + laser = 45 + energy = 60 + bomb = 15 + fire = 50 + acid = 50 + wound = 10 + +/obj/item/clothing/head/helmet/perceptomatrix/Initialize(mapload) + . = ..() + + update_appearance(UPDATE_ICON_STATE) + update_anomaly_state() + +/obj/item/clothing/head/helmet/perceptomatrix/equipped(mob/living/user, slot) + . = ..() + if(slot & ITEM_SLOT_HEAD) + RegisterSignal(user, COMSIG_MOB_BEFORE_SPELL_CAST, PROC_REF(pre_cast_core_check)) + +/obj/item/clothing/head/helmet/perceptomatrix/dropped(mob/living/user, silent) + UnregisterSignal(user, COMSIG_MOB_BEFORE_SPELL_CAST) + ..() + +// Prevent casting the spell w/o the core. +/obj/item/clothing/head/helmet/perceptomatrix/proc/pre_cast_core_check(mob/caster, datum/action/cooldown/spell/spell) + SIGNAL_HANDLER + if((!core_installed) && spell.school == SCHOOL_PSYCHIC) + to_chat(caster, span_warning("You can't zap minds through [src]'s shielding without a core installed!")) + return SPELL_CANCEL_CAST + +/obj/item/clothing/head/helmet/perceptomatrix/proc/update_anomaly_state() + + // If the core isn't installed, or it's temporarily deactivated, disable special functions. + if(!core_installed) + clothing_flags = PERCEPTOMATRIX_INACTIVE_FLAGS + detach_clothing_traits(clothing_traits) + QDEL_LIST(active_components) + RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, forced = TRUE) + return + + clothing_flags = PERCEPTOMATRIX_ACTIVE_FLAGS + attach_clothing_traits(initial(clothing_traits)) + + // When someone makes TRAIT_DEAF an element, or status effect, or whatever, give this item a way to bypass said deafness. + // just blocking future instances of deafness isn't what the item is meant to do but there's no proper way to do it otherwise at the moment. + active_components += AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_HEAD), reduce_amount = 2) // should be same as highest value + active_components += AddComponent( + /datum/component/anti_magic, \ + antimagic_flags = MAGIC_RESISTANCE_MIND, \ + inventory_flags = ITEM_SLOT_HEAD, \ + ) + AddElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, forced = TRUE) + + update_icon_state() + +/obj/item/clothing/head/helmet/perceptomatrix/Destroy(force) + QDEL_LIST(active_components) + return ..() + +/obj/item/clothing/head/helmet/perceptomatrix/examine(mob/user) + . = ..() + if (!core_installed) + . += span_warning("It requires a hallucination anomaly core in order to function.") + +/obj/item/clothing/head/helmet/perceptomatrix/item_action_slot_check(slot, mob/user, datum/action/action) + return slot & ITEM_SLOT_HEAD + +/obj/item/clothing/head/helmet/perceptomatrix/update_icon_state() + icon_state = base_icon_state + (core_installed ? "" : "_inactive") + worn_icon_state = base_icon_state + (core_installed ? "" : "_inactive") + return ..() + +/obj/item/clothing/head/helmet/perceptomatrix/item_interaction(mob/user, obj/item/weapon, params) + if (!istype(weapon, /obj/item/assembly/signaler/anomaly/hallucination)) + return NONE + balloon_alert(user, "inserting...") + if (!do_after(user, delay = 3 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING + qdel(weapon) + core_installed = TRUE + update_anomaly_state() + update_appearance(UPDATE_ICON_STATE) + playsound(src, 'sound/machines/crate/crate_open.ogg', 50, FALSE) + return ITEM_INTERACT_SUCCESS + +/obj/item/clothing/head/helmet/perceptomatrix/functioning + core_installed = TRUE + +/datum/action/cooldown/spell/pointed/percept_hallucination + name = "Hallucinate" + desc = "Redirect perceptual energies towards a target, staggering them." + button_icon_state = "blind" + ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi' + + sound = 'sound/items/weapons/emitter2.ogg' + school = SCHOOL_PSYCHIC + cooldown_time = 15 SECONDS + + invocation_type = INVOCATION_NONE + spell_requirements = NONE + antimagic_flags = MAGIC_RESISTANCE_MIND + + active_msg = "You prepare to zap a target with hallucinations..." + + /// The amount of blurriness to apply + var/eye_blur_duration = 7 SECONDS + /// The amount of stagger to apply + var/stagger_duration = 3 SECONDS + /// The amount of hallucination to apply + var/hallucination_duration = 25 SECONDS + /// Spark system + var/datum/effect_system/spark_spread/quantum/spark_sys + +/datum/action/cooldown/spell/pointed/percept_hallucination/New(Target) + . = ..() + + spark_sys = new /datum/effect_system/spark_spread/quantum + +/datum/action/cooldown/spell/pointed/percept_hallucination/Destroy() + QDEL_NULL(spark_sys) + return ..() + +/datum/action/cooldown/spell/pointed/percept_hallucination/is_valid_target(atom/cast_on) + . = ..() + if(!.) + return FALSE + if(ishuman(cast_on)) + return TRUE + if(istype(cast_on, /obj/item/food/pancakes)) + return TRUE + + return FALSE + +/datum/action/cooldown/spell/pointed/percept_hallucination/proc/blows_up_pancakes_with_mind(obj/item/food/pancakes/pancakes) + + owner.visible_message( + span_userdanger("[owner] blows up [pancakes] with [owner.p_their()] mind!"), + span_userdanger("You blow up [pancakes] with your mind!") + ) + + for(var/mob/chef in get_hearers_in_view(7, pancakes)) + if(!chef.mind) + continue + // if cooked by chef, or if EITHER 5% chance OR its april fools. a || (b || c) + if(HAS_TRAIT_FROM(pancakes, TRAIT_FOOD_CHEF_MADE, REF(chef.mind)) || (prob(5) || check_holidays(APRIL_FOOLS))) + chef.say("Ma fuckin' pancakes!") + + playsound(pancakes, 'sound/effects/fuse.ogg', 80) + animate(pancakes, time = 1, pixel_z = 12, easing = ELASTIC_EASING) + animate(time = 1, pixel_z = 0, easing = BOUNCE_EASING) + for(var/i in 1 to 15) + animate(color = (i % 2) ? "#ffffff": "#ff6739", time = 1, easing = QUAD_EASING, flags = ANIMATION_CONTINUE) + + addtimer(CALLBACK(src, PROC_REF(pancake_explosion), pancakes), 1.5 SECONDS) + +/datum/action/cooldown/spell/pointed/percept_hallucination/proc/pancake_explosion(obj/pancakes) + explosion(pancakes, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 1, flame_range = 2) + qdel(pancakes) + StartCooldown() + +/datum/action/cooldown/spell/pointed/percept_hallucination/proc/cast_fx(atom/cast_on) + owner.Beam(cast_on, icon_state = "greyscale_lightning", beam_color = COLOR_FADED_PINK, time = 0.5 SECONDS) + + spark_sys.set_up(2, 1, get_turf(owner)) + spark_sys.start() + spark_sys.set_up(4, 1, get_turf(cast_on)) + spark_sys.start() + +/datum/action/cooldown/spell/pointed/percept_hallucination/cast(mob/living/carbon/human/cast_on) + . = ..() + + cast_fx(cast_on) + + if(istype(cast_on, /obj/item/food/pancakes)) + blows_up_pancakes_with_mind(cast_on) + return + + if(cast_on.can_block_magic(antimagic_flags)) + to_chat(cast_on, span_notice("You feel psychic energies reflecting off you.")) + to_chat(owner, span_warning("[cast_on] deflects the energy!")) + return + + to_chat(cast_on, span_warning("Your brain feels like it's on fire!")) + cast_on.emote("scream") + cast_on.set_eye_blur_if_lower(eye_blur_duration) + cast_on.adjust_staggered(stagger_duration) + cast_on.apply_status_effect(/datum/status_effect/hallucination, hallucination_duration, \ + hallucination_duration * 0.2, hallucination_duration) // lower/upper hallucination freq. bound + + return + +#undef PERCEPTOMATRIX_INACTIVE_FLAGS +#undef PERCEPTOMATRIX_ACTIVE_FLAGS diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 16a84f3eb04..0355fff952a 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -94,9 +94,6 @@ GLOBAL_LIST_INIT(clown_mask_options, list( var/valid_wearer = ismob(loc) var/mob/wearer = loc if(istype(tool, /obj/item/cigarette)) - if(flags_cover & MASKCOVERSMOUTH) - balloon_alert(user, "mask's mouth is covered!") - return ..() if(max_filters <= 0 || cig) balloon_alert(user, "can't hold that!") @@ -240,7 +237,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list( /obj/item/clothing/mask/gas/welding/adjust_visor(mob/living/user) . = ..() if(.) - playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 50, TRUE) + playsound(src, up ? SFX_VISOR_UP : SFX_VISOR_DOWN, 50, TRUE) if(!fishing_modifier) return if(up) diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index ee896d4e0fa..1180ab5148f 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -139,7 +139,7 @@ to_chat(user, span_notice("Your helmet's torch can't pass through your welding visor!")) set_light_on(FALSE) helmet_on = FALSE - playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing + playsound(src, up ? SFX_VISOR_UP : SFX_VISOR_DOWN, 50, TRUE) update_appearance() /obj/item/clothing/head/helmet/space/plasmaman/update_icon_state() diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index bbff5f887ed..05a0451be9e 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -821,7 +821,6 @@ equip_delay_other = 40 clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED) max_integrity = 15 - var/decayed = FALSE /obj/item/clothing/suit/armor/durability/holymelon/fire_resist resistance_flags = FIRE_PROOF @@ -829,13 +828,10 @@ /obj/item/clothing/suit/armor/durability/holymelon/Initialize(mapload) . = ..() - if(decayed) - decay() - return AddComponent( /datum/component/anti_magic, \ - antimagic_flags = MAGIC_RESISTANCE_HOLY, \ + antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, \ inventory_flags = ITEM_SLOT_OCLOTHING, \ charges = 1, \ drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \ diff --git a/code/modules/deathmatch/deathmatch_modifier.dm b/code/modules/deathmatch/deathmatch_modifier.dm index 9faafa91a48..9671f19c92a 100644 --- a/code/modules/deathmatch/deathmatch_modifier.dm +++ b/code/modules/deathmatch/deathmatch_modifier.dm @@ -519,7 +519,7 @@ modifiers_pool -= modpath ///Pick global modifiers at random. - for(var/iteration in rand(3, 5)) + for(var/iteration in 1 to rand(3, 5)) var/datum/deathmatch_modifier/modifier = GLOB.deathmatch_game.modifiers[pick_n_take(modifiers_pool)] modifier.on_select(lobby) modifier.on_start_game(lobby) diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm index 29a64c2b98a..59e2f0feb86 100644 --- a/code/modules/detectivework/evidence.dm +++ b/code/modules/detectivework/evidence.dm @@ -8,6 +8,9 @@ inhand_icon_state = "" w_class = WEIGHT_CLASS_TINY item_flags = NOBLUDGEON + drop_sound = 'sound/items/evidence_bag/evidence_bag_drop.ogg' + pickup_sound = 'sound/items/evidence_bag/evidence_bag_pickup.ogg' + sound_vary = TRUE /obj/item/evidencebag/Initialize(mapload) . = ..() @@ -15,20 +18,12 @@ max_slots = 1, max_specific_storage = WEIGHT_CLASS_NORMAL, ) + atom_storage.allow_quick_gather = TRUE + atom_storage.collection_mode = COLLECT_ONE RegisterSignal(atom_storage, COMSIG_STORAGE_STORED_ITEM, PROC_REF(on_insert)) RegisterSignal(atom_storage, COMSIG_STORAGE_REMOVED_ITEM, PROC_REF(on_remove)) - -/obj/item/evidencebag/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - if(interacting_with == loc || !isitem(interacting_with) || HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) - return NONE - if(atom_storage.attempt_insert(interacting_with, user)) - return ITEM_INTERACT_SUCCESS - return NONE - -/obj/item/evidencebag/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(atom_storage.attempt_insert(tool, user)) - return ITEM_INTERACT_SUCCESS - return NONE + atom_storage.rustle_sound = 'sound/items/evidence_bag/evidence_bag_zip.ogg' + atom_storage.remove_rustle_sound = 'sound/items/evidence_bag/evidence_bag_unzip.ogg' /obj/item/evidencebag/update_desc(updates) . = ..() @@ -60,12 +55,15 @@ /obj/item/evidencebag/proc/on_insert(datum/storage/storage, obj/item/to_insert, mob/user, force) SIGNAL_HANDLER + update_weight_class(to_insert.w_class) /obj/item/evidencebag/proc/on_remove(datum/storage/storage, obj/item/to_remove, atom/remove_to_loc, silent) SIGNAL_HANDLER + if(!atom_storage.get_total_weight()) return + update_weight_class(WEIGHT_CLASS_TINY) /obj/item/evidencebag/attack_self(mob/user) @@ -74,6 +72,7 @@ return user.visible_message(span_notice("[user] empties [src]."), span_notice("You empty [src]."),\ span_hear("You hear someone rustle around in a plastic bag, and remove something.")) + playsound(src,'sound/items/evidence_bag/evidence_bag_unzip.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE) atom_storage.remove_all() /obj/item/storage/box/evidence diff --git a/code/modules/escape_menu/escape_menu.dm b/code/modules/escape_menu/escape_menu.dm index 57fe4b88813..38d456594de 100644 --- a/code/modules/escape_menu/escape_menu.dm +++ b/code/modules/escape_menu/escape_menu.dm @@ -47,6 +47,10 @@ GLOBAL_LIST_EMPTY(escape_menus) RegisterSignal(client, COMSIG_QDELETING, PROC_REF(on_client_qdel)) RegisterSignal(client, COMSIG_CLIENT_MOB_LOGIN, PROC_REF(on_client_mob_login)) + SEND_SOUND(client, 'sound/misc/escape_menu/esc_open.ogg') + var/sound/esc_middle = sound('sound/misc/escape_menu/esc_middle.ogg', repeat = FALSE, channel = CHANNEL_ESCAPEMENU, volume = 80) + SEND_SOUND(client, esc_middle) + if (!isnull(ckey)) GLOB.escape_menus[ckey] = src @@ -57,6 +61,10 @@ GLOBAL_LIST_EMPTY(escape_menus) GLOB.escape_menus -= ckey plane_master_controller.remove_filter("escape_menu_blur") + var/sound/esc_clear = sound(null, repeat = FALSE, channel = CHANNEL_ESCAPEMENU) //yes, I'm doing it like this with a null, no its absolutely intentional, cuts off the sound right as needed. + SEND_SOUND(client, esc_clear) + SEND_SOUND(client, 'sound/misc/escape_menu/esc_close.ogg') + return ..() /datum/escape_menu/proc/on_client_qdel() diff --git a/code/modules/events/ghost_role/blob.dm b/code/modules/events/ghost_role/blob.dm index 1f483e6a683..a4eed6781a7 100644 --- a/code/modules/events/ghost_role/blob.dm +++ b/code/modules/events/ghost_role/blob.dm @@ -36,7 +36,7 @@ if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS var/mob/dead/observer/new_blob = chosen_one - var/mob/camera/blob/BC = new_blob.become_overmind() + var/mob/eye/blob/BC = new_blob.become_overmind() spawned_mobs += BC message_admins("[ADMIN_LOOKUPFLW(BC)] has been made into a blob overmind by an event.") BC.log_message("was spawned as a blob overmind by an event.", LOG_GAME) diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index b7e8db30c52..d542c5026be 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -65,7 +65,7 @@ GLOBAL_LIST_EMPTY(all_wormholes) // So we can pick wormholes to teleport to . = ..() GLOB.all_wormholes -= src -/obj/effect/portal/wormhole/teleport(atom/movable/M) +/obj/effect/portal/wormhole/teleport(atom/movable/M, force = FALSE) if(iseffect(M)) //sparks don't teleport return if(M.anchored) diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm index ea37c9dc759..18a54aebd2e 100644 --- a/code/modules/fishing/aquarium/aquarium.dm +++ b/code/modules/fishing/aquarium/aquarium.dm @@ -1,14 +1,3 @@ -#define AQUARIUM_LAYER_STEP 0.01 -/// Aquarium content layer offsets -#define AQUARIUM_MIN_OFFSET 0.02 -#define AQUARIUM_MAX_OFFSET 1 -/// The layer of the glass overlay -#define AQUARIUM_GLASS_LAYER 0.01 -/// The layer of the aquarium pane borders -#define AQUARIUM_BORDERS_LAYER AQUARIUM_MAX_OFFSET + AQUARIUM_LAYER_STEP -/// Layer for stuff rendered below the glass overlay -#define AQUARIUM_BELOW_GLASS_LAYER 0.01 - /obj/structure/aquarium name = "aquarium" desc = "A vivarium in which aquatic fauna and flora are usually kept and displayed." @@ -17,371 +6,75 @@ icon = 'icons/obj/aquarium/tanks.dmi' icon_state = "aquarium_map" + base_icon_state = "aquarium" integrity_failure = 0.3 - /// The icon state is used for mapping so mappers know what they're placing. This prefixes the real icon used in game. - /// For an example, "aquarium" gives the base sprite of "aquarium_base", the glass is "aquarium_glass_water", and so on. - var/icon_prefix = "aquarium" - - var/fluid_type = AQUARIUM_FLUID_FRESHWATER - var/fluid_temp = DEFAULT_AQUARIUM_TEMP - var/min_fluid_temp = MIN_AQUARIUM_TEMP - var/max_fluid_temp = MAX_AQUARIUM_TEMP - - ///While the feed storage is not empty, this is the interval which the fish are fed. - var/feeding_interval = 3 MINUTES - ///The last time fishes were fed by the acquarium itsef. - var/last_feeding - - /// Can fish reproduce in this quarium. - var/reproduction_and_growth = TRUE - //This is the area where fish can swim var/aquarium_zone_min_px = 2 var/aquarium_zone_max_px = 31 var/aquarium_zone_min_py = 10 var/aquarium_zone_max_py = 28 - var/list/fluid_types = list(AQUARIUM_FLUID_SALTWATER, AQUARIUM_FLUID_FRESHWATER, AQUARIUM_FLUID_SULPHWATEVER, AQUARIUM_FLUID_AIR) - - var/panel_open = FALSE + /// Default beauty of the aquarium, without anything inside it + var/default_beauty = 150 - ///Current layers in use by aquarium contents - var/list/used_layers = list() - - /// /obj/item/fish in the aquarium, sorted by type - does not include things with aquarium visuals that are not fish - var/list/tracked_fish_by_type - - /// Var used to keep track of the current beauty of the aquarium, which can be throughfully changed by aquarium content. - var/current_beauty = 150 + ///Tracks the fluid type of our aquarium component. Used for the icon suffix of some overlays and splashing water when broken. + var/fluid_type = AQUARIUM_FLUID_FRESHWATER /obj/structure/aquarium/Initialize(mapload) . = ..() - update_appearance() - RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(track_if_fish)) - AddElement(/datum/element/relay_attackers) - RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) - create_reagents(6, SEALED_CONTAINER) - RegisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed)) + AddComponent(/datum/component/aquarium, aquarium_zone_min_px, aquarium_zone_max_px, aquarium_zone_min_py, aquarium_zone_max_py, default_beauty) AddComponent(/datum/component/plumbing/aquarium, start = anchored) - if(current_beauty) - AddElement(/datum/element/beauty, current_beauty) - ADD_KEEP_TOGETHER(src, INNATE_TRAIT) - -/obj/structure/aquarium/proc/track_if_fish(atom/source, atom/initialized) - SIGNAL_HANDLER - if(isfish(initialized)) - LAZYADDASSOCLIST(tracked_fish_by_type, initialized.type, initialized) - -/obj/structure/aquarium/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - if(isfish(arrived)) - LAZYADDASSOCLIST(tracked_fish_by_type, arrived.type, arrived) - -/obj/structure/aquarium/Exited(atom/movable/gone, direction) - . = ..() - LAZYREMOVEASSOC(tracked_fish_by_type, gone.type, gone) - -/obj/structure/aquarium/proc/start_autofeed(datum/source, new_reagent, amount, reagtemp, data, no_react) - SIGNAL_HANDLER - START_PROCESSING(SSobj, src) - UnregisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT) - -/obj/structure/aquarium/process(seconds_per_tick) - if(!reagents.total_volume) - RegisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed)) - return PROCESS_KILL - if(world.time < last_feeding + feeding_interval) - return - last_feeding = world.time - var/list/fishes = get_fishes() - for(var/obj/item/fish/fish as anything in fishes) - fish.feed(reagents) - -/// Returns tracked_fish_by_type but flattened and without the items in the blacklist, also shuffled if shuffle is TRUE. -/obj/structure/aquarium/proc/get_fishes(shuffle = FALSE, blacklist) - . = list() - for(var/fish_type in tracked_fish_by_type) - . += tracked_fish_by_type[fish_type] - . -= blacklist - if(shuffle) - . = shuffle(.) - return . - -/obj/structure/aquarium/proc/request_layer(layer_type) - /** - * base aq layer - * min_offset = this value is returned on bottom layer mode - * min_offset + 0.1 fish1 - * min_offset + 0.2 fish2 - * ... these layers are returned for auto layer mode and tracked by used_layers - * min_offset + max_offset = this value is returned for top layer mode - * min_offset + max_offset + 1 = this is used for glass overlay - */ - //optional todo: hook up sending surface changed on aquarium changing layers - switch(layer_type) - if(AQUARIUM_LAYER_MODE_BEHIND_GLASS) - return layer + AQUARIUM_BELOW_GLASS_LAYER - if(AQUARIUM_LAYER_MODE_BOTTOM) - return layer + AQUARIUM_MIN_OFFSET - if(AQUARIUM_LAYER_MODE_TOP) - return layer + AQUARIUM_MAX_OFFSET - if(AQUARIUM_LAYER_MODE_AUTO) - var/chosen_layer = AQUARIUM_MIN_OFFSET + AQUARIUM_LAYER_STEP - while((chosen_layer in used_layers) && (chosen_layer <= AQUARIUM_MAX_OFFSET)) - chosen_layer += AQUARIUM_LAYER_STEP - used_layers += chosen_layer - return layer + chosen_layer - -/obj/structure/aquarium/proc/free_layer(value) - used_layers -= value - -/obj/structure/aquarium/proc/get_surface_properties() - . = list() - .[AQUARIUM_PROPERTIES_PX_MIN] = aquarium_zone_min_px - .[AQUARIUM_PROPERTIES_PX_MAX] = aquarium_zone_max_px - .[AQUARIUM_PROPERTIES_PY_MIN] = aquarium_zone_min_py - .[AQUARIUM_PROPERTIES_PY_MAX] = aquarium_zone_max_py + RegisterSignal(src, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_aquarium_liquid_changed)) + update_appearance() /obj/structure/aquarium/update_icon() . = ..() ///"aquarium_map" is used for mapping, so mappers can tell what it's. - icon_state = icon_prefix + "_base" + icon_state = base_icon_state + "_base" + +/obj/structure/aquarium/proc/on_aquarium_liquid_changed(datum/source, fluid_type) + SIGNAL_HANDLER + src.fluid_type = fluid_type + update_appearance() /obj/structure/aquarium/update_overlays() . = ..() - if(panel_open) - . += icon_prefix + "_panel" + if(HAS_TRAIT(src, TRAIT_AQUARIUM_PANEL_OPEN)) + . += base_icon_state + "_panel" + var/icon_suffix = fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water" ///The glass overlay - var/suffix = fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water" if(broken) - suffix += "_broken" - . += mutable_appearance(icon, icon_prefix + "_glass_cracks", layer = layer + AQUARIUM_BORDERS_LAYER) - . += mutable_appearance(icon, icon_prefix + "_glass_[suffix]", layer = layer + AQUARIUM_GLASS_LAYER) - . += mutable_appearance(icon, icon_prefix + "_borders", layer = layer + AQUARIUM_BORDERS_LAYER) - -/obj/structure/aquarium/examine(mob/user) - . = ..() - . += span_notice("Alt-click to [panel_open ? "close" : "open"] the control and feed panel.") - if(panel_open && reagents.total_volume) - . += span_notice("You can use a plunger to empty the feed storage.") - -/obj/structure/aquarium/click_alt(mob/living/user) - panel_open = !panel_open - balloon_alert(user, "panel [panel_open ? "open" : "closed"]") - if(panel_open) - reagents.flags |= TRANSPARENT|REFILLABLE - else - reagents.flags &= ~(TRANSPARENT|REFILLABLE) - update_appearance() - return CLICK_ACTION_SUCCESS + icon_suffix += "_broken" + . += mutable_appearance(icon, base_icon_state + "_glass_cracks", layer = layer + AQUARIUM_BORDERS_LAYER) + . += mutable_appearance(icon, base_icon_state + "_glass_[icon_suffix]", layer = layer + AQUARIUM_GLASS_LAYER) + . += mutable_appearance(icon, base_icon_state + "_borders", layer = layer + AQUARIUM_BORDERS_LAYER) /obj/structure/aquarium/wrench_act(mob/living/user, obj/item/tool) . = ..() default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS -/obj/structure/aquarium/plunger_act(obj/item/plunger/P, mob/living/user, reinforced) - if(!panel_open) - return - user.balloon_alert_to_viewers("plunging...") - if(do_after(user, 3 SECONDS, target = src)) - user.balloon_alert_to_viewers("finished plunging") - reagents.expose(get_turf(src), TOUCH) //splash on the floor - reagents.clear_reagents() - -/obj/structure/aquarium/attackby(obj/item/item, mob/living/user, params) - if(broken) - var/obj/item/stack/sheet/glass/glass = item - if(istype(glass)) - if(glass.get_amount() < 2) - balloon_alert(user, "it needs two sheets!") - return - balloon_alert(user, "fixing the aquarium...") - if(do_after(user, 2 SECONDS, target = src)) - glass.use(2) - broken = FALSE - atom_integrity = max_integrity - update_appearance() - return TRUE - else - var/insert_attempt = SEND_SIGNAL(item, COMSIG_TRY_INSERTING_IN_AQUARIUM, src) - switch(insert_attempt) - if(COMSIG_CAN_INSERT_IN_AQUARIUM) - if(!user.transferItemToLoc(item, src)) - user.balloon_alert(user, "stuck to your hand!") - return TRUE - balloon_alert(user, "added to aquarium") - update_appearance() - return TRUE - if(COMSIG_CANNOT_INSERT_IN_AQUARIUM) - balloon_alert(user, "cannot add to aquarium!") - return TRUE - - if(istype(item, /obj/item/fish_feed) && !panel_open) - if(!item.reagents.total_volume) - balloon_alert(user, "[item] is empty!") - return TRUE - var/list/fishes = get_fishes() - for(var/obj/item/fish/fish as anything in fishes) - fish.feed(item.reagents) - balloon_alert(user, "fed the fish") - return TRUE - if(istype(item, /obj/item/aquarium_upgrade)) - var/obj/item/aquarium_upgrade/upgrade = item - if(upgrade.upgrade_from_type != type) - balloon_alert(user, "wrong kind of aquarium!") - return - balloon_alert(user, "upgrading...") - if(!do_after(user, 5 SECONDS, src)) - return - var/obj/structure/aquarium/upgraded_aquarium = new upgrade.upgrade_to_type(loc) - for(var/atom/movable/moving in contents) - moving.forceMove(upgraded_aquarium) - balloon_alert(user, "upgraded") - qdel(upgrade) - qdel(src) +/obj/structure/aquarium/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack/sheet/glass)) return - return ..() - -/obj/structure/aquarium/proc/on_attacked(datum/source, mob/attacker, attack_flags) - var/list/fishes = get_fishes() - //I wish this were an aquarium signal, but the aquarium_content component got in the way. - for(var/obj/item/fish/fish as anything in fishes) - SEND_SIGNAL(fish, COMSIG_FISH_STIRRED) - -/obj/structure/aquarium/interact(mob/user) - if(!broken && user.pulling && isliving(user.pulling)) - var/mob/living/living_pulled = user.pulling - var/datum/component/aquarium_content/content_component = living_pulled.GetComponent(/datum/component/aquarium_content) - if(content_component && content_component.is_ready_to_insert(src)) - try_to_put_mob_in(user) - else if(panel_open) - . = ..() //call base ui_interact - else - admire(user) - -/// Tries to put mob pulled by the user in the aquarium after a delay -/obj/structure/aquarium/proc/try_to_put_mob_in(mob/user) - if(user.pulling && isliving(user.pulling)) - var/mob/living/living_pulled = user.pulling - if(living_pulled.buckled || living_pulled.has_buckled_mobs()) - to_chat(user, span_warning("[living_pulled] is attached to something!")) - return - user.visible_message(span_danger("[user] starts to put [living_pulled] into [src]!")) - if(do_after(user, 10 SECONDS, target = src)) - if(QDELETED(living_pulled) || user.pulling != living_pulled || living_pulled.buckled || living_pulled.has_buckled_mobs()) - return - var/datum/component/aquarium_content/content_component = living_pulled.GetComponent(/datum/component/aquarium_content) - if(content_component || content_component.is_ready_to_insert(src)) - return - user.visible_message(span_danger("[user] stuffs [living_pulled] into [src]!")) - living_pulled.forceMove(src) - update_appearance() - -///Apply mood bonus depending on aquarium status -/obj/structure/aquarium/proc/admire(mob/living/user) - user.balloon_alert(user, "admiring aquarium...") - if(!do_after(user, 5 SECONDS, target = src)) - return - var/alive_fish = 0 - var/dead_fish = 0 - var/list/tracked_fish = get_fishes() - for(var/obj/item/fish/fish in tracked_fish) - if(fish.status == FISH_ALIVE) - alive_fish++ - else - dead_fish++ - - var/morb = HAS_MIND_TRAIT(user, TRAIT_MORBID) - //Check if there are live fish - good mood - //All fish dead - bad mood. - //No fish - nothing. - if(alive_fish > 0) - user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_bad : /datum/mood_event/aquarium_positive) - else if(dead_fish > 0) - user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_good : /datum/mood_event/aquarium_negative) - // Could maybe scale power of this mood with number/types of fish - -/obj/structure/aquarium/ui_data(mob/user) - . = ..() - .["fluidType"] = fluid_type - .["temperature"] = fluid_temp - .["allowBreeding"] = reproduction_and_growth - .["fishData"] = list() - .["feedingInterval"] = feeding_interval / (1 MINUTES) - .["propData"] = list() - for(var/atom/movable/item in contents) - if(isfish(item)) - var/obj/item/fish/fish = item - .["fishData"] += list(list( - "fish_ref" = REF(fish), - "fish_name" = fish.name, - "fish_happiness" = fish.get_happiness_value(), - "fish_icon" = fish::icon, - "fish_icon_state" = fish::icon_state, - "fish_health" = fish.health, - )) - continue - .["propData"] += list(list( - "prop_ref" = REF(item), - "prop_name" = item.name, - "prop_icon" = item::icon, - "prop_icon_state" = item::icon_state, - )) - -/obj/structure/aquarium/ui_static_data(mob/user) - . = ..() - //I guess these should depend on the fluid so lava critters can get high or stuff below water freezing point but let's keep it simple for now. - .["minTemperature"] = min_fluid_temp - .["maxTemperature"] = max_fluid_temp - .["fluidTypes"] = fluid_types - .["heartIcon"] = 'icons/effects/effects.dmi' - -/obj/structure/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - if(.) - return - var/mob/user = usr - switch(action) - if("temperature") - var/temperature = params["temperature"] - if(isnum(temperature)) - fluid_temp = clamp(temperature, min_fluid_temp, max_fluid_temp) - . = TRUE - if("fluid") - if(params["fluid"] in fluid_types) - fluid_type = params["fluid"] - SEND_SIGNAL(src, COMSIG_AQUARIUM_FLUID_CHANGED, fluid_type) - . = TRUE - if("allow_breeding") - reproduction_and_growth = !reproduction_and_growth - . = TRUE - if("feeding_interval") - feeding_interval = params["feeding_interval"] MINUTES - . = TRUE - if("pet_fish") - var/obj/item/fish/fish = locate(params["fish_reference"]) in contents - fish?.pet_fish(user) - if("remove_item") - var/atom/movable/item = locate(params["item_reference"]) in contents - item?.forceMove(drop_location()) - to_chat(user, span_notice("You take out [item] from [src].")) - if("rename_fish") - var/new_name = sanitize_name(params["chosen_name"]) - var/atom/movable/fish = locate(params["fish_reference"]) in contents - if(!fish || !new_name || new_name == fish.name) - return - fish.AddComponent(/datum/component/rename, new_name, fish.desc) - -/obj/structure/aquarium/ui_interact(mob/user, datum/tgui/ui) - . = ..() - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Aquarium", name) - ui.open() + if(!broken) + balloon_alert(user, "aquarium not broken!") + return ITEM_INTERACT_BLOCKING + var/obj/item/stack/sheet/glass/glass = tool + if(glass.get_amount() < 2) + balloon_alert(user, "it needs two sheets!") + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "fixing the aquarium...") + if(!do_after(user, 2 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING + glass.use(2) + broken = FALSE + atom_integrity = max_integrity + update_appearance() + return ITEM_INTERACT_SUCCESS /obj/structure/aquarium/atom_break(damage_flag) . = ..() @@ -397,50 +90,168 @@ else possible_destinations_for_fish = list(droploc) playsound(src, 'sound/effects/glass/glassbr3.ogg', 100, TRUE) - for(var/atom/movable/fish in contents) - fish.forceMove(pick(possible_destinations_for_fish)) + for(var/atom/movable/content as anything in contents) + content.forceMove(pick(possible_destinations_for_fish)) if(fluid_type != AQUARIUM_FLUID_AIR) var/datum/reagents/reagent_splash = new() reagent_splash.add_reagent(/datum/reagent/water, 30) chem_splash(droploc, null, 3, list(reagent_splash)) update_appearance() -#undef AQUARIUM_LAYER_STEP -#undef AQUARIUM_MIN_OFFSET -#undef AQUARIUM_MAX_OFFSET -#undef AQUARIUM_GLASS_LAYER -#undef AQUARIUM_BORDERS_LAYER -#undef AQUARIUM_BELOW_GLASS_LAYER - -/obj/structure/aquarium/lawyer +/obj/structure/aquarium/prefilled anchored = TRUE -/obj/structure/aquarium/lawyer/Initialize(mapload) +/obj/structure/aquarium/prefilled/Initialize(mapload) . = ..() new /obj/item/aquarium_prop/sand(src) new /obj/item/aquarium_prop/seaweed(src) - if(prob(85)) - new /obj/item/fish/goldfish/gill(src) - reagents.add_reagent(/datum/reagent/consumable/nutriment, 2) + new /obj/item/fish/goldfish(src) + new /obj/item/fish/angelfish(src) + new /obj/item/fish/guppy(src) + + //They'll be alive for about 30 minutes with this amount. + reagents.add_reagent(/datum/reagent/consumable/nutriment, 3) + +/obj/item/fish_tank + name = "fish tank" + desc = "A more portable sort of aquarium to store various fishes in, unless they're too big or there're too many of them." + icon = 'icons/obj/aquarium/tanks.dmi' + icon_state = "fish_tank_map" + base_icon_state = "fish_tank" + force = 5 + throwforce = 5 + throw_range = 3 + w_class = WEIGHT_CLASS_BULKY + item_flags = SLOWS_WHILE_IN_HAND + + custom_price = PAYCHECK_CREW * 9 + + ///Tracks the fluid type of our aquarium component. Used for overlays + var/fluid_type = AQUARIUM_FLUID_FRESHWATER + + ///Fish which size exceed this value cannot be inserted + var/maximum_relative_size = 100 + ///Fish cannot be inserted if the sum of the size of all fish in this tank exceeds this value. + var/max_total_size = 220 + ///Tracks the sum of the size of all fish in this tank + var/current_summed_size = 0 + ///Tracks the sum of the weight of all fish in this tank + var/current_summed_weight = 0 + + var/slowdown_coeff = 1 + + ///The minimum fluid temperature of this fish tank + var/min_fluid_temp = MIN_AQUARIUM_TEMP + 12 + ///The maximum fluid temperature of this fish tank + var/max_fluid_temp = MAX_AQUARIUM_TEMP - 32 + ///The reagent capacity of this fish tank + var/reagent_size = 4 + +/obj/item/fish_tank/Initialize(mapload) + . = ..() + update_appearance() + AddComponent(\ + /datum/component/aquarium,\ + min_px = 6,\ + max_px = 26,\ + min_py = 6,\ + min_py = 24,\ + default_beauty = 100,\ + reagents_size = src.reagent_size,\ + min_fluid_temp = src.min_fluid_temp,\ + max_fluid_temp = src.max_fluid_temp,\ + ) + AddComponent(/datum/component/plumbing/aquarium, start = anchored) + RegisterSignal(src, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_aquarium_liquid_changed)) + RegisterSignal(src, COMSIG_AQUARIUM_CAN_INSERT, PROC_REF(can_insert)) + RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_new_fish)) + +/obj/item/fish_tank/update_icon() + . = ..() + ///"aquarium_map" is used for mapping, so mappers can tell what it's. + icon_state = base_icon_state + +/obj/item/fish_tank/proc/on_aquarium_liquid_changed(datum/source, fluid_type) + SIGNAL_HANDLER + src.fluid_type = fluid_type + update_appearance() + +/obj/item/fish_tank/update_overlays() + . = ..() + . += "[base_icon_state]_panel[HAS_TRAIT(src, TRAIT_AQUARIUM_PANEL_OPEN) ? "_open" : ""]" + . += mutable_appearance(icon, "[base_icon_state]_[fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water"]", layer = layer + AQUARIUM_GLASS_LAYER) + . += mutable_appearance(icon, "[base_icon_state]_borders", layer = layer + AQUARIUM_BORDERS_LAYER) + +/obj/item/fish_tank/proc/can_insert(atom/movable/source, obj/item/item, mob/living/user) + SIGNAL_HANDLER + if(!isfish(item)) + return + var/obj/item/fish/fish = item + if(fish.size > maximum_relative_size) + balloon_alert(user, "fish is too big!") + return COMSIG_CANNOT_INSERT_IN_AQUARIUM + if(current_summed_size > max_total_size) + balloon_alert(user, "fish tank is full!") + return COMSIG_CANNOT_INSERT_IN_AQUARIUM + return COMSIG_CAN_INSERT_IN_AQUARIUM + +/obj/item/fish_tank/Entered(atom/movable/entered) + . = ..() + on_new_fish(src, entered) + +/obj/item/fish_tank/proc/on_new_fish(datum/source, atom/movable/movable) + SIGNAL_HANDLER + if(!isfish(movable)) + return + var/obj/item/fish/fish = movable + change_size_weight(fish.size, fish.weight) + RegisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, PROC_REF(on_fish_size_weight_updated)) + +/obj/item/fish_tank/proc/on_fish_size_weight_updated(obj/item/fish/source, new_size, new_weight) + SIGNAL_HANDLER + change_size_weight(new_size - source.size, new_weight - source.weight) + +/obj/item/fish_tank/Exited(atom/movable/gone) + if(isfish(gone)) + var/obj/item/fish/fish = gone + change_size_weight(-fish.size, -fish.weight) + UnregisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT) + return ..() + +/obj/item/fish_tank/proc/change_size_weight(size_change, weight_change) + current_summed_size += size_change + current_summed_weight += weight_change + if(current_summed_size > max_total_size) + ADD_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, INNATE_TRAIT) else - new /obj/item/fish/goldfish/three_eyes/gill(src) - reagents.add_reagent(/datum/reagent/toxin/mutagen, 2) //three eyes goldfish feed on mutagen. + REMOVE_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, INNATE_TRAIT) + if(HAS_TRAIT(src, TRAIT_SPEED_POTIONED) || current_summed_weight < FISH_WEIGHT_SLOWDOWN) + slowdown = 0 + drag_slowdown = 0 + else + slowdown = GET_FISH_SLOWDOWN(current_summed_weight) * slowdown_coeff + drag_slowdown = slowdown * 0.5 + if(ismob(loc)) + var/mob/mob = loc + mob.update_equipment_speed_mods() + force = min(2 + (GET_FISH_WEIGHT_RANK(current_summed_weight) * 3), 21) + throwforce = force -/obj/structure/aquarium/prefilled - anchored = TRUE +///The lawyer's own pet goldfish's fish tank. It used to be an aquarium, but now it can be held and carried around. +/obj/item/fish_tank/lawyer -/obj/structure/aquarium/prefilled/Initialize(mapload) +/obj/item/fish_tank/lawyer/Initialize(mapload) . = ..() new /obj/item/aquarium_prop/sand(src) new /obj/item/aquarium_prop/seaweed(src) - new /obj/item/fish/goldfish(src) - new /obj/item/fish/angelfish(src) - new /obj/item/fish/guppy(src) - - //They'll be alive for about 30 minutes with this amount. - reagents.add_reagent(/datum/reagent/consumable/nutriment, 3) + if(prob(85)) + new /obj/item/fish/goldfish/gill(src) + reagents.add_reagent(/datum/reagent/consumable/nutriment, 3) + else + new /obj/item/fish/goldfish/three_eyes/gill(src) + reagents.add_reagent(/datum/reagent/toxin/mutagen, 3) //three eyes goldfish feed on mutagen. diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm index f1186e2f382..f18f67b9313 100644 --- a/code/modules/fishing/aquarium/aquarium_kit.dm +++ b/code/modules/fishing/aquarium/aquarium_kit.dm @@ -1,15 +1,18 @@ ///Fish feed can -/obj/item/fish_feed +/obj/item/reagent_containers/cup/fish_feed name = "fish feed can" desc = "A refillable can that dispenses nutritious fish feed." icon = 'icons/obj/aquarium/supplies.dmi' icon_state = "fish_feed" w_class = WEIGHT_CLASS_TINY - -/obj/item/fish_feed/Initialize(mapload) - . = ..() - create_reagents(5, OPENCONTAINER) - reagents.add_reagent(/datum/reagent/consumable/nutriment, 2.5) //Default fish diet + spillable = FALSE + volume = 5 + amount_per_transfer_from_this = 2.5 + has_variable_transfer_amount = FALSE + reagent_flags = OPENCONTAINER + reagent_container_liquid_sound = null + list_reagents = list(/obj/item/fish::food = 2.5) //Default fish diet + gulp_size = 1 /** * Stasis fish case container for moving fish between aquariums safely. @@ -69,7 +72,13 @@ name = "imported fish case" /obj/item/storage/fish_case/tiziran/get_fish_type() - return pick(/obj/item/fish/dwarf_moonfish, /obj/item/fish/gunner_jellyfish, /obj/item/fish/needlefish, /obj/item/fish/armorfish) + return pick_weight(list( + /obj/item/fish/moonfish/dwarf = 2, + /obj/item/fish/gunner_jellyfish = 2, + /obj/item/fish/needlefish = 2, + /obj/item/fish/armorfish = 2, + /obj/item/fish/moonfish = 1, + )) ///Subtype bought from the blackmarket at a gratuitously cheap price. The catch? The fish inside it is dead. /obj/item/storage/fish_case/blackmarket @@ -108,20 +117,20 @@ /obj/item/aquarium_kit name = "DIY Aquarium Construction Kit" - desc = "Everything you need to build your own aquarium. Raw materials sold separately." + desc = "Everything you need to build your own aquarium or fish tank. Raw materials sold separately." icon = 'icons/obj/aquarium/supplies.dmi' icon_state = "construction_kit" w_class = WEIGHT_CLASS_TINY /obj/item/aquarium_kit/Initialize(mapload) . = ..() - var/static/list/recipes = list(/datum/crafting_recipe/aquarium) + var/static/list/recipes = list(/datum/crafting_recipe/aquarium, /datum/crafting_recipe/fish_tank) AddElement(/datum/element/slapcrafting, recipes) /obj/item/aquarium_prop name = "generic aquarium prop" desc = "very boring" - icon = 'icons/obj/aquarium/supplies.dmi' + icon = 'icons/obj/aquarium/tanks.dmi' w_class = WEIGHT_CLASS_TINY custom_materials = list(/datum/material/plastic = COIN_MATERIAL_AMOUNT) @@ -132,15 +141,21 @@ . = ..() //It's important that we register the signals before the component is attached. RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(generate_aquarium_appearance)) - AddComponent(/datum/component/aquarium_content, beauty = beauty) + AddComponent(/datum/component/aquarium_content) ADD_TRAIT(src, TRAIT_UNIQUE_AQUARIUM_CONTENT, INNATE_TRAIT) -/obj/item/aquarium_prop/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual) + RegisterSignal(src, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, PROC_REF(get_aquarium_beauty)) + +/obj/item/aquarium_prop/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual, atom/movable/aquarium) SIGNAL_HANDLER - visual.icon = icon - visual.icon_state = icon_state + visual.icon = aquarium.icon + visual.icon_state = "[icon_state][isitem(aquarium) ? "_fish_tank" : ""]" visual.layer_mode = layer_mode +/obj/item/aquarium_prop/proc/get_aquarium_beauty(datum/source, list/beauty_holder) + SIGNAL_HANDLER + beauty_holder += beauty + /obj/item/aquarium_prop/rocks name = "decorative rocks" desc = "A bunch of tiny plastic rocks for decorating an aquarium. Surely you could have just used real pebbles?" diff --git a/code/modules/fishing/aquarium/aquarium_upgrades.dm b/code/modules/fishing/aquarium/aquarium_upgrades.dm index c73e6e9d230..042074500af 100644 --- a/code/modules/fishing/aquarium/aquarium_upgrades.dm +++ b/code/modules/fishing/aquarium/aquarium_upgrades.dm @@ -11,8 +11,25 @@ /// typepath of the new aquarium subtype created. var/upgrade_to_type = /obj/structure/aquarium +/obj/item/aquarium_upgrade/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!HAS_TRAIT(interacting_with, TRAIT_IS_AQUARIUM)) + return NONE + if(upgrade_from_type != interacting_with.type) + interacting_with.balloon_alert(user, "wrong kind of aquarium!") + return ITEM_INTERACT_BLOCKING + interacting_with.balloon_alert(user, "upgrading...") + if(!PERFORM_ALL_TESTS(aquarium_upgrade) && !do_after(user, 5 SECONDS, interacting_with)) + return ITEM_INTERACT_BLOCKING + var/atom/movable/upgraded_aquarium = new upgrade_to_type(interacting_with.drop_location()) + //This should transfer all the fish, reagents and settings from the aquarium component + interacting_with.TransferComponents(upgraded_aquarium) + upgraded_aquarium.balloon_alert(user, "upgraded") + qdel(src) + qdel(interacting_with) + return ITEM_INTERACT_SUCCESS + /obj/item/aquarium_upgrade/bioelec_gen - name = "Aquarium Bioelectricity Kit" + name = "aquarium bioelectricity kit" desc = "All the required components to allow an aquarium to harness energy bioelectric fish." icon_state = "bioelec_kit" upgrade_to_type = /obj/structure/aquarium/bioelec_gen @@ -22,9 +39,13 @@ desc = "An unconventional type of generator that boosts and harvests the energy produced by bioelectric fish." icon_state = "bioelec_map" - icon_prefix = "bioelec" + base_icon_state = "bioelec" + + default_beauty = 0 - current_beauty = 0 +/obj/structure/aquarium/bioelec_gen/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_BIOELECTRIC_GENERATOR, INNATE_TRAIT) /obj/structure/aquarium/bioelec_gen/zap_act(power, zap_flags) var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE @@ -37,3 +58,23 @@ . += span_boldwarning("WARNING! WARNING! WARNING!") . += span_warning("The bioelectric potential of the fish inside is magnified to dangerous levels by the generator.") . += span_notice("Tesla coils are required to collect this magnified energy... and you'll want a grounding rod to protect yourself as well.") + +/obj/item/aquarium_upgrade/bluespace_tank + name = "bluespace fish tank kit" + desc = "The required components to upgrade your portable fish tank into bottomless, handheld aquarium." + icon_state = "bluespace_kit" + upgrade_from_type = /obj/item/fish_tank + upgrade_to_type = /obj/item/fish_tank/bluespace + +/obj/item/fish_tank/bluespace + name = "bluespace fish tank" + desc = "All the capacity of a bulky room aquarium, squeezed in a bag-sized rectangular cuboid." + icon_state = "fish_tank_bluespace_map" + base_icon_state = "fish_tank_bluespace" + w_class = WEIGHT_CLASS_NORMAL + maximum_relative_size = INFINITY + max_total_size = 2000 + slowdown_coeff = 0.15 + min_fluid_temp = MIN_AQUARIUM_TEMP + max_fluid_temp = MAX_AQUARIUM_TEMP + reagent_size = 6 diff --git a/code/modules/fishing/aquarium/fish_analyzer.dm b/code/modules/fishing/aquarium/fish_analyzer.dm index 617d8b600ba..3df28e326ac 100644 --- a/code/modules/fishing/aquarium/fish_analyzer.dm +++ b/code/modules/fishing/aquarium/fish_analyzer.dm @@ -59,18 +59,15 @@ . += emissive_appearance(icon, "fish_analyzer_emissive", src) /obj/item/fish_analyzer/interact_with_atom(atom/target, mob/living/user, list/modifiers) - if(!isfish(target) && !isaquarium(target)) + if(!isfish(target) && !HAS_TRAIT(target, TRAIT_IS_AQUARIUM)) return NONE if(!user.can_read(src) || user.is_blind()) return ITEM_INTERACT_BLOCKING - if(isfish(target) || istype(target, /obj/structure/aquarium)) - scanned_item = WEAKREF(target) - SEND_SIGNAL(src, COMSIG_FISH_ANALYZER_ANALYZE_STATUS, target, user) - ui_interact(user) - return ITEM_INTERACT_SUCCESS - - return NONE + scanned_item = WEAKREF(target) + SEND_SIGNAL(src, COMSIG_FISH_ANALYZER_ANALYZE_STATUS, target, user) + ui_interact(user) + return ITEM_INTERACT_SUCCESS /obj/item/fish_analyzer/ui_interact(mob/user, datum/tgui/ui) if(isnull(scanned_item?.resolve())) @@ -92,13 +89,13 @@ data["fish_scanned"] = TRUE return extract_fish_info(data, scanned_object) - var/obj/structure/aquarium/aquarium = scanned_object + var/atom/movable/aquarium = scanned_object for(var/obj/item/fish/fishie in aquarium) - extract_fish_info(data, fishie, aquarium) + extract_fish_info(data, fishie) return data -/obj/item/fish_analyzer/proc/extract_fish_info(list/data, obj/item/fish/fishie, obj/structure/aquarium/aquarium) +/obj/item/fish_analyzer/proc/extract_fish_info(list/data, obj/item/fish/fishie) var/list/fish_traits = list() var/list/fish_evolutions = list() @@ -129,12 +126,12 @@ "fish_min_temp" = fishie.required_temperature_min, "fish_max_temp" = fishie.required_temperature_max, "fish_hunger" = HAS_TRAIT(fishie, TRAIT_FISH_NO_HUNGER) ? 0 : 1 - fishie.get_hunger(), - "fish_fluid_compatible" = aquarium ? compatible_fluid_type(fishie.required_fluid_type, aquarium.fluid_type) : null, + "fish_fluid_compatible" = fishie.fish_flags & FISH_FLAG_SAFE_FLUID, "fish_fluid_type" = fishie.required_fluid_type, "fish_breed_timer" = round(max(fishie.breeding_wait - world.time, 0) / 10), "fish_traits" = fish_traits, "fish_evolutions" = fish_evolutions, - "fish_suitable_temp" = aquarium ? ISINRANGE(aquarium.fluid_temp, fishie.required_temperature_min, fishie.required_temperature_max) : null + "fish_suitable_temp" = fishie.fish_flags & FISH_FLAG_SAFE_TEMPERATURE, )) return data diff --git a/code/modules/fishing/bait.dm b/code/modules/fishing/bait.dm index aad7a8d3d1d..275f387cbda 100644 --- a/code/modules/fishing/bait.dm +++ b/code/modules/fishing/bait.dm @@ -63,7 +63,7 @@ /obj/item/fishing_lure name = "artificial minnow" - desc = "A fishing lure meant to attract smaller omnivore fish." + desc = "A fishing lure that may attract small fish. Too tiny, too large, or too picky prey won't be interested in it, though." icon = 'icons/obj/fishing.dmi' icon_state = "minnow" w_class = WEIGHT_CLASS_SMALL @@ -129,8 +129,8 @@ return multiplier /obj/item/fishing_lure/plug - name = "big plug lure" - desc = "A fishing lure used to catch larger omnivore fish." + name = "artificial plug lure" + desc = "A bigger fishing lure that may attract larger fish. Tiny or picky prey will remain uninterested." icon_state = "plug" /obj/item/fishing_lure/plug/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties) @@ -164,7 +164,7 @@ /obj/item/fishing_lure/spoon name = "\improper Indy spoon lure" - desc = "A lustrous piece of metal mimicking the scales of a fish. Good for catching small to medium freshwater omnivore fish." + desc = "A lustrous piece of metal mimicking the scales of a fish. It specializes in catching small-to-medium-sized fish that live in freshwater." icon_state = "spoon" spin_frequency = list(1.25 SECONDS, 2.25 SECONDS) @@ -184,7 +184,7 @@ /obj/item/fishing_lure/artificial_fly name = "\improper Silkbuzz artificial fly" - desc = "A fishing lure resembling a large wooly fly. Good for catching all sort of picky fish." + desc = "A fishing lure resembling a large wooly fly. Unlike most other lures, it's fancy enough to catch the interest of picky fish, but only those." icon_state = "artificial_fly" spin_frequency = list(1.1 SECONDS, 2 SECONDS) @@ -196,7 +196,7 @@ /obj/item/fishing_lure/led name = "\improper LED fishing lure" - desc = "A heavy, waterproof and fish-looking LED stick, used to catch abyssal and demersal fish alike." + desc = "A heavy, waterproof and fish-looking LED stick, specialized to catch only nocturnal and deep-dwelling fish." icon_state = "led" spin_frequency = list(3 SECONDS, 3.8 SECONDS) @@ -224,7 +224,7 @@ /obj/item/fishing_lure/lucky_coin name = "\improper Maneki-Coin lure" - desc = "A faux-gold lure used to attract shiny-loving fish." + desc = "A faux-gold lure. Catches the attention of fishies that love shinies. Not nearly tasty-looking enough for anything else." icon_state = "lucky_coin" spin_frequency = list(1.5 SECONDS, 2.7 SECONDS) @@ -244,7 +244,7 @@ /obj/item/fishing_lure/algae name = "plastic algae lure" - desc = "A soft clump of fake algae used to attract herbivore water critters." + desc = "A soft clump of fake algae. Herbivores love it. Nothing else does, not even omnivores." icon_state = "algae" spin_frequency = list(3 SECONDS, 5 SECONDS) @@ -256,7 +256,7 @@ /obj/item/fishing_lure/grub name = "\improper Twister Worm lure" - desc = "A soft plastic lure with the body of a grub and a twisting tail. Good for panfish and other small omnivore fish." + desc = "A soft plastic lure with the body of a grub and a twisting tail. Specialized for catching small fish, as long as they aren't herbivores, picky, or picky herbivores." icon_state = "grub" spin_frequency = list(1 SECONDS, 2.7 SECONDS) @@ -270,7 +270,7 @@ /obj/item/fishing_lure/buzzbait name = "\improper Electric-Buzz lure" - desc = "A metallic, colored clanked attached to a series of cables that somehow attract shock-worthy fish." + desc = "A metallic, colored clanker attached to a series of cables that somehow attract shock-worthy fish." icon_state = "buzzbait" spin_frequency = list(0.8 SECONDS, 1.7 SECONDS) @@ -282,7 +282,7 @@ /obj/item/fishing_lure/spinnerbait name = "spinnerbait lure" - desc = "A versatile lure, good for catching all sort of predatory freshwater fish." + desc = "A spinny, vulnerable lure, great for attracting freshwater predators, though omnivores won't be interested in it." icon_state = "spinnerbait" spin_frequency = list(2 SECONDS, 4 SECONDS) @@ -299,7 +299,7 @@ /obj/item/fishing_lure/daisy_chain name = "daisy chain lure" - desc = "A lure resembling a small school of fish, good for catching several saltwater predators." + desc = "A lure resembling a small school of fish. Saltwater predators love it, but not much else will." icon_state = "daisy_chain" spin_frequency = list(2 SECONDS, 4 SECONDS) diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 124dda7dd0a..b43a2bf439a 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -176,10 +176,11 @@ base_icon_state = icon_state //It's important that we register the signals before the component is attached. RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, PROC_REF(update_aquarium_animation)) - RegisterSignal(src, AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) + RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(update_aquarium_appearance)) - AddComponent(/datum/component/aquarium_content, list(COMSIG_FISH_STIRRED), beauty) + AddComponent(/datum/component/aquarium_content, list(COMSIG_ATOM_WAS_ATTACKED)) + RegisterSignal(src, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, PROC_REF(get_aquarium_beauty)) RegisterSignal(src, COMSIG_ATOM_ON_LAZARUS_INJECTOR, PROC_REF(use_lazarus)) if(fish_flags & FISH_DO_FLOP_ANIM) RegisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START, PROC_REF(on_temp_animation)) @@ -212,7 +213,7 @@ if(src == held_item) context[SCREENTIP_CONTEXT_LMB] = "Pet" return CONTEXTUAL_SCREENTIP_SET - if(istype(held_item, /obj/item/fish_feed)) + if(istype(held_item, /obj/item/reagent_containers/cup/fish_feed)) context[SCREENTIP_CONTEXT_LMB] = "Feed" return CONTEXTUAL_SCREENTIP_SET if(istype(held_item, /obj/item/fish_analyzer)) @@ -237,7 +238,7 @@ span_notice("You release [src] into [interacting_with]. [goodbye_text]"), \ span_notice("You hear a splash.")) playsound(interacting_with, 'sound/effects/splash.ogg', 50) - SEND_SIGNAL(interacting_with, COMSIG_FISH_RELEASED_INTO, src) + SEND_SIGNAL(interacting_with, COMSIG_FISH_RELEASED_INTO, src, user) qdel(src) return ITEM_INTERACT_SUCCESS @@ -419,7 +420,7 @@ return ..() /obj/item/fish/attackby(obj/item/item, mob/living/user, params) - if(!istype(item, /obj/item/fish_feed)) + if(!istype(item, /obj/item/reagent_containers/cup/fish_feed)) return ..() if(!item.reagents.total_volume) balloon_alert(user, "[item.name] is empty!") @@ -549,7 +550,7 @@ make_edible() if(weight >= FISH_WEIGHT_SLOWDOWN && !HAS_TRAIT(src, TRAIT_SPEED_POTIONED)) - slowdown = round(((weight/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1) + slowdown = GET_FISH_SLOWDOWN(weight) drag_slowdown = round(slowdown * 0.5, 1) else slowdown = 0 @@ -592,14 +593,6 @@ AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, amount, time, screentip_verb = "Cut") return amount //checked by a unit test -/** - * Weight, unlike size, is a bit more exponential, but the world isn't perfect, so isn't my code. - * Anyway, this returns a gross estimate of the "rank" of "category" for our fish weight, based on how - * weight generaly scales up (250, 500, 1000, 2000, 4000 etc...) - */ -/obj/item/fish/proc/get_weight_rank() - return max(round(1 + log(2, weight/FISH_WEIGHT_FORCE_DIVISOR), 1), 1) - ///Reset weapon-related variables of this items and recalculates those values based on the fish weight and size. /obj/item/fish/proc/update_fish_force() if(force >= 15 && hitsound == SFX_ALT_FISH_SLAP) @@ -619,7 +612,7 @@ bare_wound_bonus = initial(bare_wound_bonus) toolspeed = initial(toolspeed) - var/weight_rank = get_weight_rank() + var/weight_rank = GET_FISH_WEIGHT_RANK(weight) throw_range -= weight_rank get_force_rank() @@ -828,14 +821,13 @@ ///Proc that should be called when the fish is fed. By default, it grows the fish depending on various variables. /obj/item/fish/proc/sate_hunger() - if(isaquarium(loc)) - var/obj/structure/aquarium/aquarium = loc - if(!aquarium.reproduction_and_growth) - return + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) + last_feeding = world.time + return var/hunger = get_hunger() if(hunger < 0.05) //don't bother growing for very small amounts. + last_feeding = world.time return - last_feeding = world.time var/new_size = size var/new_weight = weight var/hunger_mult @@ -852,7 +844,7 @@ new_size += CEILING((maximum_size - size) * base_mult / (w_class * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1) new_size = min(new_size, maximum_size) if(weight < maximum_weight) - new_weight += CEILING((maximum_weight - weight) * base_mult / (get_weight_rank() * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1) + new_weight += CEILING((maximum_weight - weight) * base_mult / (GET_FISH_WEIGHT_RANK(weight) * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1) new_weight = min(new_weight, maximum_weight) if(new_size != size || new_weight != weight) update_size_and_weight(new_size, new_weight) @@ -869,9 +861,8 @@ return // Do additional stuff - var/in_aquarium = isaquarium(loc) // Start flopping if outside of fish container - var/should_be_flopping = status == FISH_ALIVE && !HAS_TRAIT(src, TRAIT_FISH_STASIS) && !in_aquarium + var/should_be_flopping = status == FISH_ALIVE && !HAS_TRAIT(src, TRAIT_FISH_STASIS) && loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM) if(should_be_flopping) start_flopping() @@ -910,7 +901,7 @@ stop_flopping() if(!silent) var/message = span_notice(replacetext(death_text, "%SRC", "[src]")) - if(isaquarium(loc)) + if(loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) loc.visible_message(message) else visible_message(message) @@ -995,47 +986,44 @@ visual.icon_state = dedicated_in_aquarium_icon_state || "[initial(icon_state)]_small" visual.color = aquarium_vc_color -/obj/item/fish/proc/randomize_aquarium_position(datum/source, obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) +/obj/item/fish/proc/randomize_aquarium_position(datum/source, atom/movable/current_aquarium, obj/effect/aquarium/visual) SIGNAL_HANDLER - var/list/aq_properties = current_aquarium.get_surface_properties() var/avg_width = round(sprite_width * 0.5) var/avg_height = round(sprite_height * 0.5) - var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16 - var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16 - var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16 - var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_height - 16 + var/px_min = visual.aquarium_zone_min_px + avg_width - 16 + var/px_max = visual.aquarium_zone_max_px - avg_width - 16 + var/py_min = visual.aquarium_zone_min_py + avg_height - 16 + var/py_max = visual.aquarium_zone_max_py - avg_height - 16 visual.pixel_x = visual.base_pixel_x = rand(px_min,px_max) visual.pixel_y = visual.base_pixel_y = rand(py_min,py_max) -/obj/item/fish/proc/get_aquarium_animation() - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium) || aquarium.fluid_type == AQUARIUM_FLUID_AIR || status == FISH_DEAD) - return AQUARIUM_ANIMATION_FISH_DEAD - else - return AQUARIUM_ANIMATION_FISH_SWIM - -/obj/item/fish/proc/update_aquarium_animation(datum/source, current_animation, obj/structure/current_aquarium, obj/effect/visual) +/obj/item/fish/proc/update_aquarium_animation(datum/source, current_animation, obj/effect/visual, fluid_type) SIGNAL_HANDLER - var/animation = get_aquarium_animation() + var/animation = get_aquarium_animation(fluid_type) if(animation == current_animation) return switch(animation) if(AQUARIUM_ANIMATION_FISH_SWIM) - swim_animation(current_aquarium, visual) + swim_animation(visual) if(AQUARIUM_ANIMATION_FISH_DEAD) - dead_animation(current_aquarium, visual) + dead_animation(visual) + +/obj/item/fish/proc/get_aquarium_animation(fluid_type) + if(fluid_type == AQUARIUM_FLUID_AIR || status == FISH_DEAD) + return AQUARIUM_ANIMATION_FISH_DEAD + else + return AQUARIUM_ANIMATION_FISH_SWIM /// Create looping random path animation, pixel offsets parameters include offsets already -/obj/item/fish/proc/swim_animation(obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) +/obj/item/fish/proc/swim_animation(obj/effect/aquarium/visual) var/avg_width = round(sprite_width / 2) var/avg_height = round(sprite_height / 2) - var/list/aq_properties = current_aquarium.get_surface_properties() - var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16 - var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16 - var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16 - var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16 + var/px_min = visual.aquarium_zone_min_px + avg_width - 16 + var/px_max = visual.aquarium_zone_max_px - avg_width - 16 + var/py_min = visual.aquarium_zone_min_py + avg_height - 16 + var/py_max = visual.aquarium_zone_max_py - avg_width - 16 var/origin_x = visual.base_pixel_x var/origin_y = visual.base_pixel_y @@ -1060,22 +1048,36 @@ animate(transform = dir_mx, time = 0, loop = -1) animate(pixel_x = target_x, pixel_y = target_y, time = eyeballed_time, loop = -1) -/obj/item/fish/proc/dead_animation(obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) +/obj/item/fish/proc/dead_animation(obj/effect/aquarium/visual) //Set base_pixel_y to lowest possible value var/avg_height = round(sprite_height / 2) - var/list/aq_properties = current_aquarium.get_surface_properties() - var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16 + var/py_min = visual.aquarium_zone_min_py + avg_height - 16 visual.base_pixel_y = py_min animate(visual, pixel_y = py_min, time = 1) //flop to bottom and end current animation. +///Malus to the beauty value if the fish content is dead +#define DEAD_FISH_BEAUTY -500 +///Prevents more impressive fishes from providing a positive beauty even when dead. +#define MAX_DEAD_FISH_BEAUTY -200 +///Some fish are already so ugly, they can't get much worse when dead +#define MIN_DEAD_FISH_BEAUTY -600 + +/obj/item/fish/proc/get_aquarium_beauty(datum/source, list/beauty_holder) + SIGNAL_HANDLER + var/actual_beauty = beauty + if(status == FISH_DEAD) + actual_beauty = clamp(beauty + DEAD_FISH_BEAUTY, MIN_DEAD_FISH_BEAUTY, MAX_DEAD_FISH_BEAUTY) + + beauty_holder += actual_beauty + +#undef DEAD_FISH_BEAUTY +#undef MIN_DEAD_FISH_BEAUTY +#undef MAX_DEAD_FISH_BEAUTY + /// Checks if our current environment lets us live. /obj/item/fish/proc/proper_environment(temp_range_min = required_temperature_min, temp_range_max = required_temperature_max) - var/obj/structure/aquarium/aquarium = loc - if(istype(aquarium)) - if(!compatible_fluid_type(required_fluid_type, aquarium.fluid_type)) - if(aquarium.fluid_type != AQUARIUM_FLUID_AIR || !HAS_TRAIT(src, TRAIT_FISH_AMPHIBIOUS)) - return FALSE - if(!ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) + if(loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) + if(!(fish_flags & FISH_FLAG_SAFE_TEMPERATURE) || !(fish_flags & FISH_FLAG_SAFE_FLUID)) return FALSE return TRUE @@ -1129,54 +1131,48 @@ bites_amount -= amount generate_fish_reagents(amount) +/// Returns tracked_fish_by_type but flattened and without the items in the blacklist, also shuffled if shuffle is TRUE. +/obj/item/fish/proc/get_aquarium_fishes(shuffle = FALSE, blacklist) + . = list() + for(var/obj/item/fish/fish in loc) + . += fish + . -= blacklist + if(shuffle) + . = shuffle(.) + return . + /obj/item/fish/proc/ready_to_reproduce(being_targeted = FALSE) - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return FALSE if(being_targeted && HAS_TRAIT(src, TRAIT_FISH_NO_MATING)) return FALSE - if(!being_targeted && length(aquarium.get_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION) + if(!being_targeted && length(get_aquarium_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION) return FALSE - return aquarium.reproduction_and_growth && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait + return !HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH) && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait /obj/item/fish/proc/try_to_reproduce() - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return FALSE var/obj/item/fish/second_fish - /** - * Fishes with this trait cannot mate, but could still reproduce asexually, so don't early return. - * Also mating takes priority over that. - */ + ///Fishes with this trait cannot mate, but could still reproduce asexually, so don't early return. if(!HAS_TRAIT(src, TRAIT_FISH_NO_MATING)) var/list/available_fishes = list() - var/types_to_mate_with = aquarium.tracked_fish_by_type - if(!HAS_TRAIT(src, TRAIT_FISH_CROSSBREEDER)) - var/list/types_to_check = list(src) - if(compatible_types) - types_to_check |= compatible_types - types_to_mate_with = types_to_mate_with & types_to_check - - for(var/obj/item/fish/fish_type as anything in types_to_mate_with) - var/list/type_fishes = types_to_mate_with[fish_type] - if(length(type_fishes) >= initial(fish_type.stable_population)) - continue - available_fishes += type_fishes - - available_fishes -= src //no self-mating. + SEND_SIGNAL(loc, COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES, src, available_fishes) if(length(available_fishes)) - for(var/obj/item/fish/other_fish as anything in shuffle(available_fishes)) + //make sure we check if the fish can reproduce with itself last, since that should've lower priority + available_fishes = shuffle(available_fishes) - src + available_fishes += src + for(var/obj/item/fish/other_fish as anything in available_fishes) if(other_fish.ready_to_reproduce(TRUE)) second_fish = other_fish break - if(!second_fish) + if(!second_fish || second_fish == src) //check if the fish can self-reproduce in these cases. if(!HAS_TRAIT(src, TRAIT_FISH_SELF_REPRODUCE)) return FALSE - if(length(aquarium.tracked_fish_by_type[type]) >= stable_population) - return FALSE + second_fish = null //set it to null, since this will make the following operations a bit easier if(PERFORM_ALL_TESTS(fish_breeding) && second_fish && !length(evolution_types)) return create_offspring(second_fish.type, second_fish) @@ -1186,13 +1182,13 @@ var/list/possible_evolutions = list() for(var/evolution_type in evolution_types) var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type] - if(evolution.check_conditions(src, second_fish, aquarium)) + if(evolution.check_conditions(src, second_fish, loc)) possible_evolutions += evolution if(second_fish?.evolution_types) var/secondary_evolutions = (second_fish.evolution_types - evolution_types) for(var/evolution_type in secondary_evolutions) var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type] - if(evolution.check_conditions(second_fish, src, aquarium)) + if(evolution.check_conditions(second_fish, src, loc)) possible_evolutions += evolution var/list/types = spawn_types || list(type) @@ -1203,7 +1199,7 @@ var/list/second_fish_types = second_fish.spawn_types || list(second_fish.type) var/recessive = HAS_TRAIT(src, TRAIT_FISH_RECESSIVE) var/recessive_partner = HAS_TRAIT(second_fish, TRAIT_FISH_RECESSIVE) - if(length(aquarium.tracked_fish_by_type[type]) >= stable_population) + if(fish_flags & FISH_FLAG_OVERPOPULATED) if(recessive_partner && !recessive) return FALSE chosen_type = pick(second_fish_types) @@ -1333,7 +1329,7 @@ var/fish_zap_range = 1 var/fish_zap_power = 1 KILO JOULES // ~5 damage, just a little friendly "yeeeouch!" var/fish_zap_flags = ZAP_MOB_DAMAGE - if(istype(loc, /obj/structure/aquarium/bioelec_gen)) + if(HAS_TRAIT(loc, TRAIT_BIOELECTRIC_GENERATOR)) fish_zap_range = 5 fish_zap_power = GET_FISH_ELECTROGENESIS(src) if(HAS_TRAIT(src, TRAIT_FISH_ON_TESLIUM)) @@ -1376,13 +1372,13 @@ happiness_value++ if(get_hunger() < 0.5) happiness_value++ - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium)) - return happiness_value - if(compatible_fluid_type(required_fluid_type, aquarium.fluid_type)) - happiness_value++ - if(ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) - happiness_value++ + if(loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) + if(fish_flags & FISH_FLAG_SAFE_FLUID) + happiness_value++ + if(fish_flags & FISH_FLAG_SAFE_TEMPERATURE) + happiness_value++ + else if(proper_environment()) + happiness_value += 2 if(bites_amount) // ouch happiness_value -= 2 if(health < initial(health) * 0.6) @@ -1394,7 +1390,7 @@ pet_fish(user) /obj/item/fish/proc/pet_fish(mob/living/user) - var/in_aquarium = isaquarium(loc) + var/in_aquarium = loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM) if(status == FISH_DEAD) to_chat(user, span_warning("You try to pet [src], but [p_theyre()] motionless!")) return FALSE diff --git a/code/modules/fishing/fish/chasm_detritus.dm b/code/modules/fishing/fish/chasm_detritus.dm index 9595c552e18..50b35f7d074 100644 --- a/code/modules/fishing/fish/chasm_detritus.dm +++ b/code/modules/fishing/fish/chasm_detritus.dm @@ -43,11 +43,11 @@ GLOBAL_LIST_INIT_TYPED(chasm_detritus_types, /datum/chasm_detritus, init_chasm_d ), ) -/datum/chasm_detritus/proc/dispense_detritus(atom/spawn_location, turf/fishing_spot) +/datum/chasm_detritus/proc/dispense_detritus(atom/spawn_location, atom/fishing_spot) if(prob(default_contents_chance)) var/default_spawn = pick(default_contents[default_contents_key]) return new default_spawn(spawn_location) - return find_chasm_contents(fishing_spot, spawn_location) + return find_chasm_contents(get_turf(fishing_spot), spawn_location) /// Returns the chosen detritus from the given list of things to choose from /datum/chasm_detritus/proc/determine_detritus(list/chasm_stuff) diff --git a/code/modules/fishing/fish/fish_evolution.dm b/code/modules/fishing/fish/fish_evolution.dm index 52708add566..8dd9bb0ffcd 100644 --- a/code/modules/fishing/fish/fish_evolution.dm +++ b/code/modules/fishing/fish/fish_evolution.dm @@ -43,20 +43,14 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) * Keep in mind the mate and aquarium arguments may be null if * the fish is self-reproducing or this evolution is a result of a fish_growth component */ -/datum/fish_evolution/proc/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/proc/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) SHOULD_CALL_PARENT(TRUE) - if(aquarium) - //chances are halved if only one parent has this evolution. - var/real_probability = (mate && (type in mate.evolution_types)) ? probability : probability/2 - if(HAS_TRAIT(source, TRAIT_FISH_MUTAGENIC) || (mate && HAS_TRAIT(mate, TRAIT_FISH_MUTAGENIC))) - real_probability *= 3 - if(!prob(real_probability)) - return FALSE - if(!ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) - return FALSE - else if(!source.proper_environment(required_temperature_min, required_temperature_max)) - return FALSE - return TRUE + //the fish don't reproduce outside of aquariums but can still grow there, so we just check if the temperature is right. + if(!aquarium) + return source.proper_environment(required_temperature_min, required_temperature_max) + if(SEND_SIGNAL(aquarium, COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS, source, mate, src) & COMPONENT_ALLOW_EVOLUTION) + return TRUE + return FALSE ///This is called when the evolution is set as the result type of a fish_growth component /datum/fish_evolution/proc/growth_checks(obj/item/fish/source, seconds_per_tick, growth) @@ -66,20 +60,17 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) return COMPONENT_DONT_GROW if(source.get_hunger() >= 0.5) //too hungry to grow return COMPONENT_DONT_GROW - var/obj/structure/aquarium/aquarium = source.loc - if(istype(aquarium) && !aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(source.loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW - else - aquarium = null - if(!check_conditions(source, aquarium = aquarium)) + if(!check_conditions(source)) return COMPONENT_DONT_GROW ///Called by the fish analyzer right click function. Returns a text string used as tooltip. /datum/fish_evolution/proc/get_evolution_tooltip() . = "" if(required_temperature_min > 0 || required_temperature_max < INFINITY) - var/max_temp = required_temperature_max < INFINITY ? " and [required_temperature_max]" : "" - . = "An aquarium temperature between [required_temperature_min][max_temp] is required." + var/max_temp = required_temperature_max < INFINITY ? " to [required_temperature_max]" : "" + . = "An aquarium temperature of [required_temperature_min][max_temp] is required." if(conditions_note) . += " [conditions_note]" return . @@ -90,7 +81,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) new_traits = list(/datum/fish_trait/lubed) conditions_note = "The fish must be fed lube beforehand." -/datum/fish_evolution/lubefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/lubefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if(!HAS_TRAIT(source, TRAIT_FISH_FED_LUBE)) return FALSE return ..() @@ -109,7 +100,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) conditions_note = "The fish (and its mate) needs to be unusually big both in size and weight." show_result_on_wiki = FALSE -/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if((source.size < 120 || source.weight < 3000) || (mate && (mate.size < 120 || mate.weight < 3000))) return FALSE return ..() @@ -137,7 +128,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) new_traits = list(/datum/fish_trait/predator, /datum/fish_trait/aggressive) conditions_note = "The fish needs to be unusually big and aggressive" -/datum/fish_evolution/chainsawfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/chainsawfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) var/double_avg_size = /obj/item/fish/goldfish::average_size * 2 var/double_avg_weight = /obj/item/fish/goldfish::average_weight * 2 if(source.size >= double_avg_size && source.weight >= double_avg_weight && (/datum/fish_trait/aggressive in source.fish_traits)) @@ -149,7 +140,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) new_fish_type = /obj/item/fish/pike/armored conditions_note = "The fish needs to have the stinger trait" -/datum/fish_evolution/armored_pike/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/armored_pike/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if(HAS_TRAIT(source, TRAIT_FISH_STINGER)) return ..() return FALSE @@ -165,7 +156,32 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) conditions_note = "The final stage of fritterfish growth. It gotta be big!" show_result_on_wiki = FALSE -/datum/fish_evolution/nessiefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/nessiefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if(source.size >= (/obj/item/fish/fryish/fritterish::average_size * 1.5) && source.size >= (/obj/item/fish/fryish/fritterish::average_weight * 1.5)) return ..() return FALSE + +/datum/fish_evolution/moonfish + probability = 200 //guaranteed if the conditions are met + new_fish_type = /obj/item/fish/moonfish + conditions_note = "Requires the dwarf moonfish to be big enough." + +/datum/fish_evolution/moonfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) + if(source.size < (/obj/item/fish/moonfish/dwarf::average_size * 1.5) && source.size < (/obj/item/fish/moonfish/dwarf::average_weight * 1.5)) + return ..() + if(mate && (mate.size < (/obj/item/fish/moonfish::average_size * 1.3) && mate.size < (/obj/item/fish/moonfish::average_weight * 1.3))) + return FALSE + return FALSE + +/datum/fish_evolution/dwarf_moonfish + probability = 200 //guaranteed if the conditions are met + new_fish_type = /obj/item/fish/moonfish/dwarf + conditions_note = "Requires the moonfish to be small enough." + +/datum/fish_evolution/dwarf_moonfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) + if(source.size > (/obj/item/fish/moonfish::average_size * 0.66) && source.size > (/obj/item/fish/moonfish::average_weight * 0.66)) + return FALSE + if(mate && (mate.size > (/obj/item/fish/moonfish::average_size * 0.7) && mate.size > (/obj/item/fish/moonfish::average_weight * 0.7))) + return FALSE + return ..() + diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index 75fa7b5f981..aa045c6308b 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -184,7 +184,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/nocturnal/proc/check_light(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(isturf(source.loc) || isaquarium(source)) + if(source.loc && (HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM) || isturf(source.loc))) var/turf/turf = get_turf(source) var/light_amount = turf.get_lumcount() if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD) @@ -293,7 +293,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/emulsijack/proc/emulsify(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(!isaquarium(source.loc)) + if(!source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return var/emulsified = FALSE for(var/obj/item/fish/victim in source.loc) @@ -332,7 +332,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/necrophage/proc/eat_dead_fishes(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(source.get_hunger() > 0.75 || !isaquarium(source.loc)) + if(source.get_hunger() > 0.75 || !source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return for(var/obj/item/fish/victim in source.loc) if(victim.status != FISH_DEAD || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH)) @@ -400,7 +400,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) return source.set_status(FISH_ALIVE) var/message = span_nicegreen("[source] twitches. It's alive!") - if(isaquarium(source.loc)) + if(source.loc && HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) source.loc.visible_message(message) else source.visible_message(message) @@ -425,10 +425,9 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/predator/proc/eat_fishes(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(source.get_hunger() > 0.75 || !isaquarium(source.loc)) + if(source.get_hunger() > 0.75 || !source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return - var/obj/structure/aquarium/aquarium = source.loc - for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source)) + for(var/obj/item/fish/victim as anything in source.get_aquarium_fishes(TRUE, source)) if(victim.size < source.size * 0.7) // It's a big fish eat small fish world continue if(victim.status != FISH_ALIVE || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH) || SPT_PROB(80, seconds_per_tick)) @@ -531,13 +530,12 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/aggressive/proc/try_attack_fish(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(!isaquarium(source.loc) || !SPT_PROB(1, seconds_per_tick)) + if(!source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM) || !SPT_PROB(1, seconds_per_tick)) return - var/obj/structure/aquarium/aquarium = source.loc - for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source)) + for(var/obj/item/fish/victim as anything in source.get_aquarium_fishes(TRUE, source)) if(victim.status != FISH_ALIVE) continue - aquarium.visible_message(span_warning("[source] violently [pick("whips", "bites", "attacks", "slams")] [victim]")) + source.loc.visible_message(span_warning("[source] violently [pick("whips", "bites", "attacks", "slams")] [victim]")) var/damage = round(rand(4, 20) * (source.size / victim.size)) //smaller fishes take extra damage. victim.adjust_health(victim.health - damage) return @@ -802,6 +800,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/camouflage/proc/reset_alpha(obj/item/fish/source) SIGNAL_HANDLER + if(QDELETED(source)) + return var/init_alpha = initial(source.alpha) if(init_alpha != source.alpha) - animate(source.alpha, alpha = init_alpha, time = 1.2 SECONDS, easing = CIRCULAR_EASING|EASE_OUT) + animate(source, alpha = init_alpha, time = 1.2 SECONDS, easing = CIRCULAR_EASING|EASE_OUT) diff --git a/code/modules/fishing/fish/types/air_space.dm b/code/modules/fishing/fish/types/air_space.dm index 73c7b82c29d..3b053dc4c25 100644 --- a/code/modules/fishing/fish/types/air_space.dm +++ b/code/modules/fishing/fish/types/air_space.dm @@ -167,19 +167,18 @@ AddComponent(/datum/component/fish_growth, /mob/living/basic/carp/advanced, growth_rate) -/obj/item/fish/baby_carp/proc/growth_checks(datum/source, seconds_per_tick) +/obj/item/fish/baby_carp/proc/growth_checks(datum/source, seconds_per_tick, growth, result_path) SIGNAL_HANDLER var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency) if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow. return COMPONENT_DONT_GROW - if(!isaquarium(loc)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return - var/obj/structure/aquarium/aquarium = loc - if(!aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW - if(length(aquarium.get_fishes()) > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate. + if(length(get_aquarium_fishes()) > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate. return COMPONENT_DONT_GROW /obj/item/fish/baby_carp/proc/on_growth(datum/source, mob/living/basic/carp/result) diff --git a/code/modules/fishing/fish/types/freshwater.dm b/code/modules/fishing/fish/types/freshwater.dm index d11b7cb2219..16687bc6497 100644 --- a/code/modules/fishing/fish/types/freshwater.dm +++ b/code/modules/fishing/fish/types/freshwater.dm @@ -196,13 +196,12 @@ else deltimer(del_timerid) -/obj/item/fish/tadpole/proc/growth_checks(datum/source, seconds_per_tick, growth) +/obj/item/fish/tadpole/proc/growth_checks(datum/source, seconds_per_tick, growth, result_path) SIGNAL_HANDLER var/hunger = get_hunger() if(hunger >= 0.7) //too hungry to grow return COMPONENT_DONT_GROW - var/obj/structure/aquarium/aquarium = loc - if(istype(aquarium) && !aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW /obj/item/fish/tadpole/proc/on_growth(datum/source, mob/living/basic/frog/result) @@ -234,3 +233,23 @@ /obj/item/fish, /obj/item/fishing_lure, //they love lures in general. ) + +///Memetic fish from a paleontologically inaccurate, goofy replica of a specimen. Sells decently for its size. +/obj/item/fish/sacabambaspis + name = "sacabambaspis" + fish_id = "sacabambaspis" + desc = "A jawless fish ought to be extinct by the end of the Ordovician period. Some speculate alien intervention may have been behind its survival and inevitable evolution as a dweller of hot springs." + icon_state = "sacabambaspis" + sprite_width = 5 + sprite_height = 3 + stable_population = 7 + average_size = 27 + average_weight = 500 + required_temperature_min = MIN_AQUARIUM_TEMP+20 + required_temperature_max = MIN_AQUARIUM_TEMP+45 + random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS + beauty = FISH_BEAUTY_GOOD + fish_traits = list(/datum/fish_trait/necrophage, /datum/fish_trait/wary) + +/obj/item/fish/sacabambaspis/get_export_price(price, percent) + return ..() * 4.5 diff --git a/code/modules/fishing/fish/types/mining.dm b/code/modules/fishing/fish/types/mining.dm index 41e240889da..3a1d6ff8310 100644 --- a/code/modules/fishing/fish/types/mining.dm +++ b/code/modules/fishing/fish/types/mining.dm @@ -68,7 +68,7 @@ if(.) anger -= min(anger, 6.5) -/obj/item/fish/chasm_crab/proc/growth_checks(datum/source, seconds_per_tick, growth) +/obj/item/fish/chasm_crab/proc/growth_checks(datum/source, seconds_per_tick, growth, result_path) SIGNAL_HANDLER var/hunger = get_hunger() if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow. @@ -78,15 +78,14 @@ if(hunger >= 0.4) //I'm hungry and angry anger += growth * 0.6 - if(!isaquarium(loc)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return - var/obj/structure/aquarium/aquarium = loc - if(!aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW - if(!locate(/obj/item/aquarium_prop) in aquarium) //the aquarium deco is quite barren + if(!locate(/obj/item/aquarium_prop) in loc) //the aquarium deco is quite barren anger += growth * 0.25 - var/fish_count = length(aquarium.get_fishes()) + var/fish_count = length(get_aquarium_fishes()) if(!ISINRANGE(fish_count, 3, AQUARIUM_MAX_BREEDING_POPULATION * 0.5)) //too lonely or overcrowded anger += growth * 0.3 if(fish_count > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate. diff --git a/code/modules/fishing/fish/types/tiziran.dm b/code/modules/fishing/fish/types/tiziran.dm index 5f90cedc63d..7cc3ea94e68 100644 --- a/code/modules/fishing/fish/types/tiziran.dm +++ b/code/modules/fishing/fish/types/tiziran.dm @@ -1,20 +1,71 @@ //Tiziran Fish. -/obj/item/fish/dwarf_moonfish +/obj/item/fish/moonfish + name = "zagoskian moonfish" + fish_id = "moonfish" + desc = "A disc-shaped fish native of the less shallow areas of Tizira's oceans, roughly the size of a tuna. Highly prized in lizard cuisine for their large eggs." + icon_state = "tizira_moonfish" + sprite_height = 7 + sprite_width = 7 + fillet_type = /obj/item/food/fishmeat/moonfish + required_fluid_type = AQUARIUM_FLUID_SALTWATER + stable_population = 2 + average_size = 95 + average_weight = 2000 + required_temperature_min = MIN_AQUARIUM_TEMP+20 + required_temperature_max = MIN_AQUARIUM_TEMP+30 + beauty = FISH_BEAUTY_GOOD + weight_size_deviation = 0.1 + fishing_difficulty_modifier = 10 + random_case_rarity = FISH_RARITY_RARE + fish_traits = list(/datum/fish_trait/predator) + compatible_types = list(/obj/item/fish/moonfish, /obj/item/fish/moonfish/dwarf) + evolution_types = list(/datum/fish_evolution/dwarf_moonfish) + favorite_bait = list( + /obj/item/fish/armorfish, + /obj/item/fish/needlefish, + /obj/item/fish/gunner_jellyfish, + ) + var/egg_laying_time = 2.75 MINUTES + +/obj/item/fish/moonfish/Initialize(mapload, apply_qualities = TRUE) + . = ..() + AddComponent(/datum/component/fish_growth, /obj/item/food/moonfish_eggs, egg_laying_time, use_drop_loc = FALSE, del_on_grow = FALSE, inherit_name = FALSE) + RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(egg_checks)) + +///Stop laying eggs if we're in an unsafe environment, starving of if there are simply too many eggs already. +/obj/item/fish/moonfish/proc/egg_checks(datum/source, seconds_per_tick, growth, result_path) + if(result_path != /obj/item/food/moonfish_eggs) //Don't stop the growth of the dwarf subtype. + return + if(!proper_environment() || is_starving()) + return COMPONENT_DONT_GROW + var/count = 0 + for(var/obj/item/food/moonfish_eggs/egg in loc) + count ++ + if(count > 10) + return COMPONENT_DONT_GROW + +/obj/item/fish/moonfish/dwarf name = "dwarf moonfish" fish_id = "dwarf_moonfish" desc = "Ordinarily in the wild, the Zagoskian moonfish is around the size of a tuna, however through selective breeding a smaller breed suitable for being kept as an aquarium pet has been created." icon_state = "dwarf_moonfish" sprite_height = 6 sprite_width = 6 - required_fluid_type = AQUARIUM_FLUID_SALTWATER - stable_population = 2 - fillet_type = /obj/item/food/fishmeat/moonfish - average_size = 60 - average_weight = 1000 - required_temperature_min = MIN_AQUARIUM_TEMP+20 - required_temperature_max = MIN_AQUARIUM_TEMP+30 - beauty = FISH_BEAUTY_GOOD + stable_population = 3 + average_size = 50 + average_weight = 950 + fishing_difficulty_modifier = 0 + egg_laying_time = 4.25 MINUTES + random_case_rarity = FISH_RARITY_BASIC + fish_traits = list() + evolution_types = list(/datum/fish_evolution/moonfish) + +/obj/item/fish/moonfish/dwarf/update_size_and_weight(new_size = average_size, new_weight = average_weight, update_materials = TRUE) + . = ..() + var/multiplier = (size / (average_size * 1.5)) * (weight / (average_weight * 1.5)) + + AddComponent(/datum/component/fish_growth, /datum/fish_evolution/moonfish, 2.5 MINUTES * multiplier, use_drop_loc = FALSE) /obj/item/fish/gunner_jellyfish name = "gunner jellyfish" @@ -30,6 +81,10 @@ required_temperature_min = MIN_AQUARIUM_TEMP+24 required_temperature_max = MIN_AQUARIUM_TEMP+32 beauty = FISH_BEAUTY_GOOD + favorite_bait = list( + /obj/item/fish/armorfish, + /obj/item/fish/needlefish, + ) /obj/item/fish/gunner_jellyfish/Initialize(mapload, apply_qualities = TRUE) . = ..() diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm index 79a43fc6052..ab72b0b0fb2 100644 --- a/code/modules/fishing/fishing_equipment.dm +++ b/code/modules/fishing/fishing_equipment.dm @@ -12,8 +12,8 @@ icon = 'icons/obj/fishing.dmi' icon_state = "reel_blue" w_class = WEIGHT_CLASS_SMALL - ///A list of traits that this fishing line has, checked by fish traits and the minigame. - var/list/fishing_line_traits + ///A bitfield of traits that this fishing line has, checked by fish traits and the minigame. + var/fishing_line_traits /// Color of the fishing line var/line_color = COLOR_GRAY ///The description given to the autowiki @@ -118,10 +118,10 @@ else destination = user throw_callback = CALLBACK(src, PROC_REF(clear_hitby_signal), movable_target) - RegisterSignal(movable_target, COMSIG_ATOM_PREHITBY, PROC_REF(catch_it_chucklenut)) + RegisterSignal(movable_target, COMSIG_MOVABLE_PRE_IMPACT, PROC_REF(catch_it_chucklenut)) if(!movable_target.safe_throw_at(destination, source.cast_range, 2, callback = throw_callback, gentle = please_be_gentle)) - UnregisterSignal(movable_target, COMSIG_ATOM_PREHITBY) + UnregisterSignal(movable_target, COMSIG_MOVABLE_PRE_IMPACT) else playsound(src, 'sound/items/weapons/batonextend.ogg', 50, TRUE) @@ -129,12 +129,13 @@ SIGNAL_HANDLER var/mob/living/user = throwingdatum.initial_target.resolve() if(QDELETED(user) || hit_atom != user) - return - if(user.try_catch_item(source, skip_throw_mode_check = TRUE, try_offhand = TRUE)) - return COMSIG_HIT_PREVENTED + return NONE + if(!user.try_catch_item(source, skip_throw_mode_check = TRUE, try_offhand = TRUE)) + return NONE + return COMPONENT_MOVABLE_IMPACT_NEVERMIND /obj/item/fishing_line/auto_reel/proc/clear_hitby_signal(obj/item/item) - UnregisterSignal(item, COMSIG_ATOM_PREHITBY) + UnregisterSignal(item, COMSIG_MOVABLE_PRE_IMPACT) // Hooks @@ -145,11 +146,11 @@ icon_state = "hook" w_class = WEIGHT_CLASS_TINY - /// A list of traits that this fishing hook has, checked by fish traits and the minigame - var/list/fishing_hook_traits + /// A bitfield of traits that this fishing hook has, checked by fish traits and the minigame + var/fishing_hook_traits /// icon state added to main rod icon when this hook is equipped var/rod_overlay_icon_state = "hook_overlay" - /// What subtype of `/obj/item/chasm_detritus` do we fish out of chasms? Defaults to `/obj/item/chasm_detritus`. + /// What subtype of `/datum/chasm_detritus` do we fish out of chasms? Defaults to `/datum/chasm_detritus`. var/chasm_detritus_type = /datum/chasm_detritus ///The description given to the autowiki var/wiki_desc = "A generic fishing hook. You won't be able to fish without one." @@ -372,7 +373,7 @@ new /obj/item/storage/box/fishing_hooks/master(src) new /obj/item/storage/box/fishing_lines/master(src) new /obj/item/bait_can/super_baits(src) - new /obj/item/fish_feed(src) + new /obj/item/reagent_containers/cup/fish_feed(src) new /obj/item/aquarium_kit(src) new /obj/item/fish_analyzer(src) @@ -477,5 +478,101 @@ Do not spin while the light is still red.

\ That's all, best of luck to your angling journey." +///A modified mining capsule from the black market and sometimes random loot. +/obj/item/survivalcapsule/fishing + name = "fishing spot capsule" + desc = "An illegally modified mining capsule containing a small fishing spot connected to some faraway place." + icon_state = "capsule_fishing" + initial_language_holder = /datum/language_holder/speaking_machine + verb_say = "beeps" + verb_yell = "blares" + voice_filter = "alimiter=0.9,acompressor=threshold=0.3:ratio=40:attack=15:release=350:makeup=1.5,highpass=f=1000,rubberband=pitch=1.5" + template_id = "fishing_default" + yeet_back = FALSE + +/obj/item/survivalcapsule/fishing/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT) + register_context() + + if(SStts.tts_enabled) //This capsule informs you on why it cannot be deployed in a sliiiiightly different way. + voice = pick(SStts.available_speakers) + +/obj/item/survivalcapsule/fishing/add_context(atom/source, list/context, obj/item/held_item, mob/user) + if(!held_item || held_item == src) + context[SCREENTIP_CONTEXT_RMB] = "Change fishing spot" + return CONTEXTUAL_SCREENTIP_SET + +/obj/item/survivalcapsule/fishing/examine(mob/user) + . = ..() + . += span_info("[EXAMINE_HINT("Right-Click")] to change the selected fishing spot when held.") + +/obj/item/survivalcapsule/fishing/examine_more(mob/user) + . = ..() + . += span_tinynotice("A tiny print on the side reads: \"Use a cryptographic sequencer to disable safeties\".") + +/obj/item/survivalcapsule/fishing/emag_act(mob/user, obj/item/card/emag/emag_card) + if(obj_flags & EMAGGED) + return FALSE + obj_flags |= EMAGGED + balloon_alert(user, "safeties disabled") + playsound(src, SFX_SPARKS, 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + return TRUE + +/obj/item/survivalcapsule/fishing/attack_self_secondary(mob/living/user) + . = ..() + if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN) + return + . = SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(used) + return + var/list/choices = list() + var/list/spot_ids_by_name = list() + for(var/datum/map_template/shelter/fishing/spot as anything in typesof(/datum/map_template/shelter/fishing)) + if(!spot::safe && !(obj_flags & EMAGGED)) + continue + choices[spot::name] = image('icons/hud/radial_fishing.dmi', spot::radial_icon) + spot_ids_by_name[spot::name] = spot::shelter_id + var/choice = show_radial_menu(user, src, choices, radius = 38, custom_check = CALLBACK(src, TYPE_PROC_REF(/atom, can_interact), user), tooltips = TRUE) + if(!choice || used || !can_interact(user)) + return + template_id = spot_ids_by_name[choice] + template = SSmapping.shelter_templates[template_id] + to_chat(user, span_notice("You change [src]'s selected fishing spot to [choice].")) + playsound(src, 'sound/items/pen_click.ogg', 20, TRUE, -3) + return + +/obj/item/survivalcapsule/fishing/get_ignore_flags() + . = ..() + if(obj_flags & EMAGGED) + . += CAPSULE_IGNORE_ANCHORED_OBJECTS|CAPSULE_IGNORE_BANNED_OBJECTS + +/obj/item/survivalcapsule/fishing/fail_feedback(status) + switch(status) + if(SHELTER_DEPLOY_BAD_AREA) + say("I refuse to deploy in this area.") + if(SHELTER_DEPLOY_BAD_TURFS) + say("The walls are too close! I need [template.width]x[template.height] area to deploy.") + if(SHELTER_DEPLOY_ANCHORED_OBJECTS) + say("Get these anchored objects out of the way! I need [template.width]x[template.height] area to deploy.") + if(SHELTER_DEPLOY_BANNED_OBJECTS) + say("Remove all cables and pipes around me in a [template.width]x[template.height] area or I won't deploy.") + if(SHELTER_DEPLOY_OUTSIDE_MAP) + say("For fucks sake, deploy me somewhere less far fatched!") + +/obj/item/survivalcapsule/fishing/trigger_admin_alert(mob/triggerer, turf/trigger_loc) + var/datum/map_template/shelter/fishing/spot = template + if(spot.safe) //Don't log if the fishing spot is safe + return + + var/area/area = get_area(src) + + if(!area.outdoors) + message_admins("[ADMIN_LOOKUPFLW(triggerer)] activated an unsafe fishing capsule at [ADMIN_VERBOSEJMP(trigger_loc)]") + log_admin("[key_name(triggerer)] activated an unsafe fishing capsule at [AREACOORD(trigger_loc)]") + +/obj/item/survivalcapsule/fishing/hacked + obj_flags = parent_type::obj_flags | EMAGGED + #undef MAGNET_HOOK_BONUS_MULTIPLIER #undef RESCUE_HOOK_FISH_MULTIPLIER diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm index 3873fcb7668..a8fa4d26ad8 100644 --- a/code/modules/fishing/fishing_minigame.dm +++ b/code/modules/fishing/fishing_minigame.dm @@ -49,11 +49,13 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) var/reward_path = FISHING_DUD /// Minigame difficulty var/difficulty = FISHING_DEFAULT_DIFFICULTY - // Current phase + /// Current phase var/phase = WAIT_PHASE - // Timer for the next phase + /// Timer for the next phase var/next_phase_timer - // The last time we clicked during the baiting phase + /// The lower and upper bounds of the waiting phase timer + var/list/wait_time_range = list(3 SECONDS, 25 SECONDS) + /// The last time we clicked during the baiting phase var/last_baiting_click /// Fishing mob var/mob/user @@ -126,6 +128,10 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) RegisterSignal(fish_source, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, PROC_REF(interrupt_challenge)) fish_source.RegisterSignal(user, COMSIG_MOB_COMPLETE_FISHING, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed)) background = comp.fish_source.background + if(comp.fish_source.wait_time_range) + wait_time_range = comp.fish_source.wait_time_range + if(float.spin_frequency) //Using a fishing lure narrows the range a bit, for better or worse. + wait_time_range = list(wait_time_range[1] + 8 SECONDS, wait_time_range[2] - 8 SECONDS) SEND_SIGNAL(user, COMSIG_MOB_BEGIN_FISHING, src) SEND_SIGNAL(rod, COMSIG_ROD_BEGIN_FISHING, src) GLOB.fishing_challenges_by_user[user] = src @@ -209,7 +215,7 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) /datum/fishing_challenge/proc/start(mob/living/user) /// Create fishing line visuals if(!used_rod.internal) - fishing_line = used_rod.create_fishing_line(float, user, target_py = 5) + fishing_line = used_rod.create_fishing_line(float, user, target_py = float.pixel_y + 4) if(isnull(fishing_line)) //couldn't create a fishing line, probably because we don't have a good line of sight. qdel(src) return @@ -369,7 +375,7 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) if(penalty) wait_time = min(timeleft(next_phase_timer) + rand(3 SECONDS, 5 SECONDS), 30 SECONDS) else - wait_time = float.spin_frequency ? rand(11 SECONDS, 17 SECONDS) : rand(3 SECONDS, 25 SECONDS) + wait_time = rand(wait_time_range[1], wait_time_range[2]) if(special_effects & FISHING_MINIGAME_AUTOREEL && wait_time >= 15 SECONDS) wait_time = max(wait_time - 7.5 SECONDS, 15 SECONDS) deltimer(next_phase_timer) @@ -423,6 +429,8 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) send_alert("seed!!!") if(FISH_ICON_BOTTLE) send_alert("bottle!!!") + if(FISH_ICON_ORGAN) + send_alert("organ!!!") else send_alert("!!!") animate(float, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE) @@ -511,6 +519,8 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) else mover = new /datum/fish_movement(src) + SEND_SIGNAL(src, COMSIG_FISHING_CHALLENGE_MOVER_INITIALIZED, mover) + if(auto_reel) completion *= 1.3 else diff --git a/code/modules/fishing/fishing_portal_machine.dm b/code/modules/fishing/fishing_portal_machine.dm index 8b2f1a34e93..ad637031556 100644 --- a/code/modules/fishing/fishing_portal_machine.dm +++ b/code/modules/fishing/fishing_portal_machine.dm @@ -19,6 +19,8 @@ var/long_range_link = FALSE /// contains ALL fishing destinations. var/all_destinations = FALSE + /// If the current active fishing spot is from multitool linkage, this value is the atom it would originally belong to. + var/atom/current_linked_atom /obj/machinery/fishing_portal_generator/Initialize(mapload) . = ..() @@ -202,7 +204,7 @@ if(machine_stat & NOPOWER) balloon_alert(user, "no power!") return ITEM_INTERACT_BLOCKING - if(!istype(selected_source, /datum/fish_source/portal)) //likely from a linked fishing spot + if(!all_destinations && !istype(selected_source, /datum/fish_source/portal)) //likely from a linked fishing spot var/abort = TRUE for(var/atom/spot as anything in linked_fishing_spots) if(linked_fishing_spots[spot] != selected_source) @@ -215,6 +217,7 @@ abort = FALSE if(!abort) RegisterSignal(spot, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_fishing_spot_z_level_changed)) + current_linked_atom = spot break if(abort && !all_destinations) balloon_alert(user, "cannot reach linked!") @@ -233,6 +236,7 @@ for(var/atom/spot as anything in linked_fishing_spots) if(linked_fishing_spots[spot] == active.fish_source) UnregisterSignal(spot, COMSIG_MOVABLE_Z_CHANGED) + current_linked_atom = null QDEL_NULL(active) REMOVE_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT) diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 8b24e34b9e5..f554a475ad4 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -122,7 +122,7 @@ if(hook) equipped_stuff += "[icon2html(hook, user)] [hook.name]" if(bait) - equipped_stuff += "[icon2html(bait, user)] [bait] as bait." + equipped_stuff += "[icon2html(bait, user)] [bait]" if(length(equipped_stuff)) . += span_notice("It has \a [english_list(equipped_stuff)] equipped.") if(!bait) @@ -332,7 +332,7 @@ QDEL_NULL(fishing_line) var/beam_color = line?.line_color || default_line_color fishing_line = new(firer, target, icon_state = "fishing_line", beam_color = beam_color, emissive = FALSE, override_target_pixel_y = target_py) - fishing_line.lefthand = firer.get_held_index_of_item(src) % 2 == 1 + fishing_line.lefthand = IS_LEFT_INDEX(firer.get_held_index_of_item(src)) RegisterSignal(fishing_line, COMSIG_BEAM_BEFORE_DRAW, PROC_REF(check_los)) RegisterSignal(fishing_line, COMSIG_QDELETING, PROC_REF(clear_line)) INVOKE_ASYNC(fishing_line, TYPE_PROC_REF(/datum/beam/, Start)) diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index 71780e76f11..9cde6df476a 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -43,6 +43,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /obj/item/fish/stingray = FISH_ICON_WEAPON, /obj/item/fish/swordfish = FISH_ICON_WEAPON, /obj/item/fish/zipzap = FISH_ICON_ELECTRIC, + /obj/item/fishing_rod = FISH_ICON_COIN, /obj/item/instrument/trumpet/spectral = FISH_ICON_BONE, /obj/item/instrument/saxophone/spectral = FISH_ICON_BONE, /obj/item/instrument/trombone/spectral = FISH_ICON_BONE, @@ -53,11 +54,13 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /obj/item/stack/sheet/bone = FISH_ICON_BONE, /obj/item/stack/sheet/mineral = FISH_ICON_GEM, /obj/item/stack/ore = FISH_ICON_GEM, + /obj/item/survivalcapsule/fishing = FISH_ICON_COIN, /obj/structure/closet/crate = FISH_ICON_COIN, /obj/structure/mystery_box = FISH_ICON_COIN, )) return_list[FISHING_RANDOM_SEED] = FISH_ICON_SEED + return_list[FISHING_RANDOM_ORGAN] = FISH_ICON_ORGAN return return_list /** @@ -91,6 +94,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) var/radial_state = "default" ///When selected by the fishing portal, this will be the icon_state of the overlay shown on the machine. var/overlay_state = "portal_aquarium" + ///If set, this overrides the upper and lower bounds of how long you should wait during the waiting phase of the minigame. + var/list/wait_time_range /// Mindless mobs that can fish will never pull up items on this list var/static/list/profound_fisher_blacklist = typecacheof(list( @@ -117,6 +122,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) for(var/path in fish_counts) if(!(path in fish_table)) stack_trace("path [path] found in the 'fish_counts' list but not in the 'fish_table'") + if(wait_time_range && length(wait_time_range) != 2) + stack_trace("wait_time_range for [type] is set but has length different than two") /datum/fish_source/Destroy() exploded_turfs = null @@ -240,15 +247,14 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) UnregisterSignal(user, COMSIG_MOB_COMPLETE_FISHING) if(!success) return - var/turf/fishing_spot = get_turf(challenge.float) - var/atom/movable/reward = dispense_reward(challenge.reward_path, user, fishing_spot) + var/atom/movable/reward = dispense_reward(challenge.reward_path, user, challenge.location) if(reward) user.add_mob_memory(/datum/memory/caught_fish, protagonist = user, deuteragonist = reward.name) SEND_SIGNAL(challenge.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, reward, user) challenge.used_rod.on_reward_caught(reward, user) /// Gives out the reward if possible -/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, turf/fishing_spot) +/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, atom/fishing_spot) var/atom/movable/reward = simple_dispense_reward(reward_path, get_turf(fisherman), fishing_spot) if(!reward) //balloon alert instead fisherman.balloon_alert(fisherman, pick(duds)) @@ -265,7 +271,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) return reward ///Simplified version of dispense_reward that doesn't need a fisherman. -/datum/fish_source/proc/simple_dispense_reward(reward_path, atom/spawn_location, turf/fishing_spot) +/datum/fish_source/proc/simple_dispense_reward(reward_path, atom/spawn_location, atom/fishing_spot) if(isnull(reward_path)) return null var/area/area = get_area(fishing_spot) @@ -296,7 +302,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time) /// Spawns a reward from a atom path right where the fisherman is. Part of the dispense_reward() logic. -/datum/fish_source/proc/spawn_reward(reward_path, atom/spawn_location, turf/fishing_spot) +/datum/fish_source/proc/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) if(reward_path == FISHING_DUD) return if(ispath(reward_path, /datum/chasm_detritus)) @@ -310,8 +316,12 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) return reward /// Returns the fish table, with with the unavailable items from fish_counts removed. -/datum/fish_source/proc/get_fish_table() +/datum/fish_source/proc/get_fish_table(from_explosion = FALSE) var/list/table = fish_table.Copy() + //message bottles cannot spawn from explosions. They're meant to be one-time messages (rarely) and photos from past rounds + //and it would suck if the pool of bottle messages were constantly being emptied by explosive fishing. + if(from_explosion) + table -= /obj/effect/spawner/message_in_a_bottle for(var/result in table) if(!isnull(fish_counts[result]) && fish_counts[result] <= 0) table -= result @@ -466,7 +476,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /datum/fish_source/proc/spawn_reward_from_explosion(atom/location, severity) if(!(fish_source_flags & FISH_SOURCE_FLAG_EXPLOSIVE_MALUS)) - explosive_spawn(location, severity) + explosive_spawn(isturf(location) ? location : location.drop_location(), severity) return if(isnull(exploded_turfs)) exploded_turfs = list() @@ -485,7 +495,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) for(var/i in 1 to (severity + 2)) if(!prob((100 + 100 * severity)/i * multiplier)) continue - var/reward_loot = pick_weight(get_fish_table()) + var/reward_loot = pick_weight(get_fish_table(from_explosion = TRUE)) var/atom/movable/reward = simple_dispense_reward(reward_loot, location, location) if(isnull(reward)) continue diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index a21f3f008e8..1828091f061 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -27,6 +27,7 @@ fish_count_regen = list( /obj/item/fish/clownfish/lube = 3 MINUTES, /obj/item/fish/swordfish = 5 MINUTES, + /obj/structure/mystery_box/fishing = 32 MINUTES, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5 fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS @@ -128,6 +129,10 @@ /obj/item/fish/greenchromis = 10, /obj/item/fish/squid = 8, /obj/item/fish/plaice = 8, + /obj/item/survivalcapsule/fishing = 1, + ) + fish_counts = list( + /obj/item/survivalcapsule/fishing = 1, ) catalog_description = "Beach dimension (Fishing portal generator)" radial_name = "Beach" @@ -160,14 +165,15 @@ /obj/effect/spawner/message_in_a_bottle = 2, /obj/item/fish/lanternfish = 5, /obj/item/fish/firefish = 5, - /obj/item/fish/dwarf_moonfish = 5, /obj/item/fish/gunner_jellyfish = 5, + /obj/item/fish/moonfish/dwarf = 4, /obj/item/fish/needlefish = 5, /obj/item/fish/armorfish = 5, /obj/item/fish/zipzap = 5, /obj/item/fish/stingray = 4, /obj/item/fish/monkfish = 4, /obj/item/fish/swordfish = 3, + /obj/item/fish/moonfish = 1, ) fish_counts = list( /obj/item/fish/swordfish = 2, @@ -271,7 +277,7 @@ ///In the spirit of randomness, we skew a few values here and there /datum/fish_source/portal/random/pre_challenge_started(obj/item/fishing_rod/rod, mob/user, datum/fishing_challenge/challenge) - challenge.bait_bounce_mult = clamp(challenge.bait_bounce_mult + (rand(-3, 3) * 0.1), 0.1, 1) + challenge.bait_bounce_mult = max(challenge.bait_bounce_mult + rand(-3, 3) * 0.1, 0.1) challenge.completion_loss = max(challenge.completion_loss + rand(-2, 2), 0) challenge.completion_gain = max(challenge.completion_gain + rand(-1, 1), 2) challenge.mover.short_jump_velocity_limit += rand(-100, 100) @@ -280,9 +286,15 @@ for(var/effect in active_effects) if(prob(30)) challenge.special_effects |= effect + RegisterSignal(challenge, COMSIG_FISHING_CHALLENGE_MOVER_INITIALIZED, PROC_REF(randomize_mover_velocity)) + +/datum/fish_source/portal/random/proc/randomize_mover_velocity(datum/source, datum/fish_movement/mover) + SIGNAL_HANDLER + mover.short_jump_velocity_limit += rand(-100, 100) + mover.long_jump_velocity_limit += rand(-100, 100) ///Cherry on top, fish caught from the randomizer portal also have (almost completely) random traits -/datum/fish_source/portal/random/spawn_reward(reward_path, atom/movable/spawn_location, turf/fishing_spot) +/datum/fish_source/portal/random/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) if(!ispath(reward_path, /obj/item/fish)) return ..() @@ -295,7 +307,7 @@ var/obj/item/fish/caught_fish = new reward_path(spawn_location, FALSE) var/list/new_traits = list() - for(var/iteration in rand(1, 4)) + for(var/iteration in 1 to rand(1, 4)) new_traits |= pick_weight(weighted_traits) caught_fish.inherit_traits(new_traits) caught_fish.randomize_size_and_weight(deviation = 0.3) @@ -347,7 +359,9 @@ fish_counts = list( /obj/structure/closet/crate/necropolis/tendril = 1 ) - + fish_count_regen = list( + /obj/structure/closet/crate/necropolis/tendril = 27 MINUTES, + ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10 fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS @@ -358,6 +372,7 @@ /datum/fish_source/lavaland/icemoon catalog_description = "Liquid plasma vents" + radial_state = "plasma" fish_table = list( FISHING_DUD = 5, /obj/item/fish/chasm_crab/ice = 30, @@ -374,6 +389,10 @@ /obj/item/stack/sheet/mineral/adamantine = 3, /obj/item/stack/sheet/mineral/runite = 2, ) + fish_count_regen = list( + /obj/item/stack/sheet/mineral/adamantine = 8 MINUTES, + /obj/item/stack/sheet/mineral/runite = 10 MINUTES, + ) overlay_state = "portal_plasma" /datum/fish_source/moisture_trap @@ -397,9 +416,11 @@ /obj/item/fish/sludgefish = 18, /obj/item/fish/slimefish = 4, /obj/item/storage/wallet/money = 2, + /obj/item/survivalcapsule/fishing = 1, ) fish_counts = list( /obj/item/storage/wallet/money = 2, + /obj/item/survivalcapsule/fishing = 1, ) fishing_difficulty = FISHING_EASY_DIFFICULTY //For beginners @@ -559,7 +580,7 @@ return return ..() -/datum/fish_source/hydro_tray/spawn_reward(reward_path, mob/fisherman, turf/fishing_spot) +/datum/fish_source/hydro_tray/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) if(reward_path != FISHING_RANDOM_SEED) var/mob/living/created_reward = ..() if(istype(created_reward)) @@ -600,6 +621,9 @@ fish_counts = list( /mob/living/basic/carp/mega = 2, ) + fish_count_regen = list( + /mob/living/basic/carp/mega = 9 MINUTES, + ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 18 /datum/fish_source/deepfryer @@ -621,5 +645,91 @@ fish_count_regen = list( /obj/item/fish/fryish = 2 MINUTES, /obj/item/fish/fryish/fritterish = 6 MINUTES, + /obj/item/fish/fryish/nessie = 22 MINUTES, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 13 + +/datum/fish_source/surgery + catalog_description = "Surgery" + radial_state = "innards" + overlay_state = "portal_syndicate" //Didn't feel like spriting a new overlay. It's just all red anyway. + background = "background_lavaland" //Kinda red. + fish_table = list(FISHING_RANDOM_ORGAN = 10) + //This should get you below zero difficulty and skip the minigame phase, unless you're wearing something that counteracts this. + fishing_difficulty = -20 + //The range for waiting is also a bit narrower, so it cannot take as few as 3 seconds or as many as 25 to snatch an organ. + wait_time_range = list(6 SECONDS, 12 SECONDS) + +/datum/fish_source/surgery/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) + if(istype(fishing_spot, /obj/machinery/fishing_portal_generator)) + var/obj/machinery/fishing_portal_generator/portal = fishing_spot + fishing_spot = portal.current_linked_atom + if(!iscarbon(fishing_spot)) + var/random_type = pick(subtypesof(/obj/item/organ) - GLOB.prototype_organs) + return new random_type(spawn_location) + + var/mob/living/carbon/carbon = fishing_spot + var/list/possible_organs = list() + for(var/datum/surgery/organ_manipulation/operation in carbon.surgeries) + var/datum/surgery_step/manipulate_organs/manip_step = GLOB.surgery_steps[operation.steps[operation.status]] + if(!istype(manip_step)) + continue + for(var/obj/item/organ/organ in operation.operated_bodypart) + if(organ.organ_flags & ORGAN_UNREMOVABLE || !manip_step.can_use_organ(organ)) + continue + possible_organs |= organ + + if(!length(possible_organs)) + return null + var/obj/item/organ/chosen = pick(possible_organs) + chosen.Remove(chosen.owner) + chosen.forceMove(spawn_location) + return chosen + +/datum/fish_source/surgery/generate_wiki_contents(datum/autowiki/fish_sources/wiki) + var/list/data = list() + + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = "Organs", + FISH_SOURCE_AUTOWIKI_DUD = "", + FISH_SOURCE_AUTOWIKI_WEIGHT = 100, + FISH_SOURCE_AUTOWIKI_NOTES = "A random organ from an ongoing organ manipulation surgery.", + )) + + return data + +/datum/fish_source/hot_spring + catalog_description = "Hot Springs" + radial_state = "onsen" + overlay_state = "portal_ocean" + fish_table = list( + FISHING_DUD = 20, + /obj/item/fish/bumpy = 10, + /obj/item/fish/sacabambaspis = 10, + /mob/living/basic/frog = 2, + /obj/item/fishing_rod/telescopic/master = 1, + ) + fish_counts = list( + /obj/item/fish/sacabambaspis = 5, + /obj/item/fishing_rod/telescopic/master = 2, + ) + fish_count_regen = list( + /obj/item/fish/sacabambaspis = 4 MINUTES, + ) + fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20 + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS + +/datum/fish_source/tizira + catalog_description = "Tiziran Sea" + radial_state = "planet" + overlay_state = "portal_beach" + fish_table = list( + FISHING_DUD = 10, + /obj/item/fish/needlefish = 5, + /obj/item/fish/armorfish = 5, + /obj/item/fish/gunner_jellyfish = 4, + /obj/item/fish/moonfish/dwarf = 2, + /obj/item/fish/moonfish = 2, + ) + fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10 + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS diff --git a/code/modules/food_and_drinks/machinery/coffeemaker.dm b/code/modules/food_and_drinks/machinery/coffeemaker.dm index bb532b31624..3a9e7a1af9f 100644 --- a/code/modules/food_and_drinks/machinery/coffeemaker.dm +++ b/code/modules/food_and_drinks/machinery/coffeemaker.dm @@ -52,6 +52,7 @@ /obj/machinery/coffeemaker/Destroy() QDEL_NULL(coffeepot) QDEL_NULL(cartridge) + remove_shared_particles(/particles/smoke) return ..() /obj/machinery/coffeemaker/Exited(atom/movable/gone, direction) @@ -391,10 +392,8 @@ ///Updates the smoke state to something else, setting particles if relevant /obj/machinery/coffeemaker/proc/toggle_steam() - QDEL_NULL(particles) - if(brewing) - particles = new /particles/smoke/steam/mild() - particles.position = list(-6, 0, 0) + var/obj/effect/abstract/shared_particle_holder/smoke_particles = add_shared_particles(/particles/smoke/steam/mild, "smoke_coffeemaker") + smoke_particles.particles.position = list(-6, 0, 0) /obj/machinery/coffeemaker/proc/operate_for(time, silent = FALSE) brewing = TRUE @@ -705,10 +704,8 @@ update_appearance(UPDATE_OVERLAYS) /obj/machinery/coffeemaker/impressa/toggle_steam() - QDEL_NULL(particles) - if(brewing) - particles = new /particles/smoke/steam/mild() - particles.position = list(-2, 1, 0) + var/obj/effect/abstract/shared_particle_holder/smoke_particles = add_shared_particles(/particles/smoke/steam/mild, "smoke_impressa") + smoke_particles.particles.position = list(-2, 1, 0) /obj/machinery/coffeemaker/impressa/brew() power_change() diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm index cd50f29ffe4..4703b9fae1d 100644 --- a/code/modules/food_and_drinks/machinery/gibber.dm +++ b/code/modules/food_and_drinks/machinery/gibber.dm @@ -182,7 +182,7 @@ var/typeofmeat = /obj/item/food/meat/slab/human var/typeofskin - var/obj/item/food/meat/slab/allmeat[meat_produced] + var/list/results = list() var/obj/item/stack/sheet/animalhide/skin var/list/datum/disease/diseases = mob_occupant.get_static_viruses() @@ -201,22 +201,29 @@ if(isalien(C)) typeofskin = /obj/item/stack/sheet/animalhide/xeno - var/occupant_volume - if(occupant?.reagents) - occupant_volume = occupant.reagents.total_volume - for (var/i=1 to meat_produced) + for (var/i in 1 to meat_produced) var/obj/item/food/meat/slab/newmeat = new typeofmeat newmeat.name = "[sourcename] [newmeat.name]" newmeat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, occupant) = 4 * SHEET_MATERIAL_AMOUNT)) - if(istype(newmeat)) - newmeat.subjectname = sourcename - newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, sourcenutriment / meat_produced) // Thehehe. Fat guys go first - if(occupant_volume) - occupant.reagents.trans_to(newmeat, occupant_volume / meat_produced, remove_blacklisted = TRUE) - if(sourcejob) - newmeat.subjectjob = sourcejob + if(!istype(newmeat)) + continue + newmeat.subjectname = sourcename + if(sourcejob) + newmeat.subjectjob = sourcejob + + results += newmeat + + SEND_SIGNAL(occupant, COMSIG_LIVING_GIBBER_ACT, user, src, results) + + var/reagents_in_produced = 0 + for(var/obj/item/result as anything in results) + if(result.reagents) + reagents_in_produced++ + + for(var/obj/item/result as anything in results) + occupant.reagents.trans_to(result, occupant.reagents.total_volume / reagents_in_produced, remove_blacklisted = TRUE) + result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, sourcenutriment / reagents_in_produced) // Thehehe. Fat guys go first - allmeat[i] = newmeat if(typeofskin) skin = new typeofskin @@ -227,9 +234,9 @@ mob_occupant.ghostize() set_occupant(null) qdel(mob_occupant) - addtimer(CALLBACK(src, PROC_REF(make_meat), skin, allmeat, meat_produced, gibtype, diseases), gibtime) + addtimer(CALLBACK(src, PROC_REF(make_meat), skin, results, meat_produced, gibtype, diseases), gibtime) -/obj/machinery/gibber/proc/make_meat(obj/item/stack/sheet/animalhide/skin, list/obj/item/food/meat/slab/allmeat, meat_produced, gibtype, list/datum/disease/diseases) +/obj/machinery/gibber/proc/make_meat(obj/item/stack/sheet/animalhide/skin, list/results, meat_produced, gibtype, list/datum/disease/diseases) playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE) operating = FALSE if (!dirty && prob(50)) @@ -239,9 +246,9 @@ if(skin) skin.forceMove(loc) skin.throw_at(pick(nearby_turfs),meat_produced,3) - for (var/i=1 to meat_produced) - var/obj/item/meatslab = allmeat[i] + var/iteration = 1 + for (var/obj/item/meatslab in results) if(LAZYLEN(diseases)) var/list/datum/disease/diseases_to_add = list() for(var/datum/disease/disease as anything in diseases) @@ -254,11 +261,15 @@ meatslab.AddComponent(/datum/component/infective, diseases_to_add) meatslab.forceMove(loc) - meatslab.throw_at(pick(nearby_turfs),i,3) - for (var/turfs=1 to meat_produced) - var/turf/gibturf = pick(nearby_turfs) - if (!gibturf.density && (src in view(gibturf))) - new gibtype(gibturf, i, diseases) + meatslab.throw_at(pick(nearby_turfs), iteration, 3) + + iteration++ + + for (var/i in 1 to meat_produced**2) //2 slabs: 4 giblets, 3 slabs: 9, etc. + var/turf/gibturf = pick(nearby_turfs) + if (!gibturf.density && (src in view(gibturf))) + new gibtype(gibturf, round(1 + i / meat_produced), diseases) + pixel_x = base_pixel_x //return to its spot after shaking operating = FALSE diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm index ae6e3945c94..53725dad385 100644 --- a/code/modules/food_and_drinks/machinery/microwave.dm +++ b/code/modules/food_and_drinks/machinery/microwave.dm @@ -113,7 +113,7 @@ /obj/machinery/microwave/Destroy() QDEL_LIST(ingredients) QDEL_NULL(soundloop) - QDEL_NULL(particles) + remove_shared_particles(/particles/smoke) if(!isnull(cell)) QDEL_NULL(cell) return ..() @@ -707,17 +707,14 @@ if(HAS_TRAIT(smeller, TRAIT_ANOSMIA)) cant_smell += smeller visible_message(span_danger("You smell a burnt smell coming from [src]!"), ignored_mobs = cant_smell) - particles = new /particles/smoke() - addtimer(CALLBACK(src, PROC_REF(remove_smoke)), 10 SECONDS) + add_shared_particles(/particles/smoke) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, remove_shared_particles), /particles/smoke), 10 SECONDS) Shake(duration = 1 SECONDS) cycles-- use_energy(active_power_usage) addtimer(CALLBACK(src, PROC_REF(cook_loop), type, cycles, wait, cooker), wait) -/obj/machinery/microwave/proc/remove_smoke() - QDEL_NULL(particles) - /obj/machinery/microwave/power_change() . = ..() if(cell_powered) diff --git a/code/modules/food_and_drinks/machinery/oven.dm b/code/modules/food_and_drinks/machinery/oven.dm index c997f349241..e9af9338000 100644 --- a/code/modules/food_and_drinks/machinery/oven.dm +++ b/code/modules/food_and_drinks/machinery/oven.dm @@ -31,6 +31,8 @@ var/datum/looping_sound/oven/oven_loop ///Current state of smoke coming from the oven var/smoke_state = OVEN_SMOKE_STATE_NONE + ///Currently used particle type, if any + var/particle_type /obj/machinery/oven/Initialize(mapload) . = ..() @@ -40,7 +42,8 @@ /obj/machinery/oven/Destroy() QDEL_NULL(oven_loop) - QDEL_NULL(particles) + if (particle_type) + remove_shared_particles(particle_type) return ..() /// Used to determine if the oven appears active and cooking, or offline. @@ -210,16 +213,22 @@ /obj/machinery/oven/proc/set_smoke_state(new_state) if(new_state == smoke_state) return + smoke_state = new_state + if (particle_type) + remove_shared_particles(particle_type) + particle_type = null - QDEL_NULL(particles) switch(smoke_state) if(OVEN_SMOKE_STATE_BAD) - particles = new /particles/smoke() + particle_type = /particles/smoke if(OVEN_SMOKE_STATE_NEUTRAL) - particles = new /particles/smoke/steam() + particle_type = /particles/smoke/steam if(OVEN_SMOKE_STATE_GOOD) - particles = new /particles/smoke/steam/mild() + particle_type = /particles/smoke/steam/mild + + if (particle_type) + add_shared_particles(particle_type) /obj/machinery/oven/crowbar_act(mob/living/user, obj/item/tool) return default_deconstruction_crowbar(tool, ignore_panel = TRUE) diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm index 0481d1c4910..afd566b7e47 100644 --- a/code/modules/food_and_drinks/machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/machinery/smartfridge.dm @@ -526,7 +526,7 @@ /obj/machinery/smartfridge/drying/proc/toggle_drying(forceoff, mob/user) if(drying || forceoff) drying = FALSE - current_user = FALSE + current_user = null update_use_power(IDLE_POWER_USE) else drying = TRUE diff --git a/code/modules/food_and_drinks/machinery/stove_component.dm b/code/modules/food_and_drinks/machinery/stove_component.dm index 76f52345c8c..8db59dc2739 100644 --- a/code/modules/food_and_drinks/machinery/stove_component.dm +++ b/code/modules/food_and_drinks/machinery/stove_component.dm @@ -11,8 +11,6 @@ VAR_FINAL/on = FALSE /// A reference to the current soup pot overtop VAR_FINAL/obj/item/container - /// A particle holder for the smoke that comes out of the soup while a container is cooking. - VAR_FINAL/obj/effect/abstract/particle_holder/soup_smoke /// Typepath of particles to use for the particle holder. VAR_FINAL/particle_type = /particles/smoke/steam/mild /// Ref to our looping sound played when cooking @@ -60,11 +58,12 @@ real_parent.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1 /datum/component/stove/UnregisterFromParent() + var/obj/machinery/real_parent = parent if(!QDELING(parent)) - var/obj/machinery/real_parent = parent container.forceMove(real_parent.drop_location()) - QDEL_NULL(soup_smoke) + if (particle_type) + real_parent.remove_shared_particles("[particle_type]_stove_[container_x]") UnregisterSignal(parent, list( COMSIG_ATOM_ATTACK_HAND_SECONDARY, @@ -248,6 +247,7 @@ SIGNAL_HANDLER var/existing_temp = container?.reagents.chem_temp || 0 + var/old_type = particle_type if(existing_temp >= SOUP_BURN_TEMP) particle_type = /particles/smoke/steam/bad else if(existing_temp >= WATER_BOILING_POINT) @@ -255,20 +255,23 @@ else particle_type = null - update_smoke() + update_smoke(old_type) + +/datum/component/stove/proc/update_smoke(old_type = null) + var/obj/obj_parent = parent + + if (old_type) + obj_parent.remove_shared_particles("[old_type]_stove_[container_x]") + + if(!on || !container?.reagents.total_volume) + soup_sound?.stop() + if (!isnull(particle_type)) + obj_parent.remove_shared_particles("[particle_type]_stove_[container_x]") + return -/datum/component/stove/proc/update_smoke() - if(on && container?.reagents.total_volume > 0) - soup_sound.start() - // Don't override existing particles, wasteful - if(isnull(soup_smoke) || soup_smoke.particles.type != particle_type) - QDEL_NULL(soup_smoke) - if(isnull(particle_type)) - return - // this gets badly murdered by sidemap - soup_smoke = new(parent, particle_type) - soup_smoke.set_particle_position(container_x, round(ICON_SIZE_Y * 0.66), 0) + soup_sound.start() + if(isnull(particle_type)) return + var/obj/effect/abstract/shared_particle_holder/soup_smoke = obj_parent.add_shared_particles(particle_type, "[particle_type]_stove_[container_x]") + soup_smoke.particles.position = list(container_x, round(ICON_SIZE_Y * 0.66), 0) - QDEL_NULL(soup_smoke) - soup_sound?.stop() diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm index 417ac543612..82607e4049c 100644 --- a/code/modules/food_and_drinks/pizzabox.dm +++ b/code/modules/food_and_drinks/pizzabox.dm @@ -237,6 +237,7 @@ if(!user.can_perform_action(src)) return balloon_alert(user, "writing box tag...") + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) boxtag_set = TRUE update_appearance() return diff --git a/code/modules/forensics/_forensics.dm b/code/modules/forensics/_forensics.dm index 5c43b9da099..450521e6c62 100644 --- a/code/modules/forensics/_forensics.dm +++ b/code/modules/forensics/_forensics.dm @@ -109,10 +109,10 @@ /// Adds a single fingerprint /datum/forensics/proc/add_fingerprint(mob/living/suspect, ignoregloves = FALSE) if(!isliving(suspect)) - if(!iscameramob(suspect)) + if(!iseyemob(suspect)) return if(isaicamera(suspect)) - var/mob/camera/ai_eye/ai_camera = suspect + var/mob/eye/camera/ai/ai_camera = suspect if(!ai_camera.ai) return suspect = ai_camera.ai @@ -190,10 +190,10 @@ /// Adds a single hiddenprint /datum/forensics/proc/add_hiddenprint(mob/suspect) if(!isliving(suspect)) - if(!iscameramob(suspect)) + if(!iseyemob(suspect)) return if(isaicamera(suspect)) - var/mob/camera/ai_eye/ai_camera = suspect + var/mob/eye/camera/ai/ai_camera = suspect if(!ai_camera.ai) return suspect = ai_camera.ai diff --git a/code/modules/hallucination/fake_death.dm b/code/modules/hallucination/fake_death.dm index c80c54959ee..126e9dd3a2b 100644 --- a/code/modules/hallucination/fake_death.dm +++ b/code/modules/hallucination/fake_death.dm @@ -101,41 +101,55 @@ return ..() /datum/hallucination/death/dust/start() - - if(!ishuman(hallucinator)) - return FALSE - - var/mob/living/carbon/human/hallucinating_human = hallucinator - var/dust_icon_state = hallucinating_human.dna?.species?.dust_anim - if(!dust_icon_state) - return FALSE - . = ..() if(!.) return - created_images = list() - var/turf/below_hallucinating = get_turf(hallucinator) - - // Apply a blank / empty image to make them look invisible to themselves + LAZYINITLIST(created_images) + // Makes hallucinator invisible, we create a clone image to animate on var/image/make_them_invisible = image(loc = hallucinator) make_them_invisible.override = TRUE created_images += make_them_invisible - - // Grab the typepath of the dust animation visual so we can steal its icon (for consistency and futureproofing) - var/obj/effect/temp_visual/dust_animation/dust_source = /obj/effect/temp_visual/dust_animation - var/image/fake_dust_animation = image(initial(dust_source.icon), below_hallucinating, dust_icon_state, layer = ABOVE_MOB_LAYER) - created_images += fake_dust_animation - - // Grab the typepath of remains so we can steal its icon and state (futureproofing) - var/obj/effect/decal/remains/human/remains_source = /obj/effect/decal/remains/human - var/image/fake_remains_image = image(initial(remains_source.icon), below_hallucinating, initial(remains_source.icon_state)) - created_images += fake_remains_image - - // Grab the typepath of an observer so we can steal its icon and state (futureproofing) - var/mob/dead/observer/observer_source = /mob/dead/observer - var/image/fake_ghost = image(initial(observer_source.icon), below_hallucinating, initial(observer_source.icon_state)) + // Makes remains, only visible if on a turf + if(isturf(hallucinator.loc)) + created_images += image(/obj/effect/decal/remains/human, hallucinator.loc) + // Makes a ghost + var/image/fake_ghost = image(/mob/dead/observer, get_turf(hallucinator)) DO_FLOATING_ANIM(fake_ghost) created_images += fake_ghost hallucinator.client?.images |= created_images + + // Does the actual animation here + if(isturf(hallucinator.loc)) + new /obj/effect/temp_visual/dust_hallucination(hallucinator.loc, hallucinator) + +/obj/effect/temp_visual/dust_hallucination + // duration doesn't really matter - it just needs to be longer than the dust animation + // for all non-hallucinating mobs, we're invisible + // for the hallucinating mob, we animate into invisibility + duration = 10 SECONDS + randomdir = FALSE + +/obj/effect/temp_visual/dust_hallucination/Initialize(mapload, mob/hallucinator) + . = ..() + if(isnull(hallucinator)) + return INITIALIZE_HINT_QDEL + + dir = hallucinator.dir + appearance = hallucinator.appearance + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + // make it invisible to everyone else + var/image/invisible = image(loc = src) + invisible.override = TRUE + add_alt_appearance( + /* type = *//datum/atom_hud/alternate_appearance/basic/one_person/reversed, + /* key = */"[REF(src)]", + /* image = */invisible, + /* options = */null, + /* non-seer = */hallucinator, + ) + + // do the dust animation, only the hallucinator can see it now + dust_animation() diff --git a/code/modules/hallucination/stray_bullet.dm b/code/modules/hallucination/stray_bullet.dm index 13ace293335..11f88c8c964 100644 --- a/code/modules/hallucination/stray_bullet.dm +++ b/code/modules/hallucination/stray_bullet.dm @@ -101,7 +101,6 @@ spawn_hit(target, TRUE) qdel(src) - return TRUE /// Called when a mob is hit by the fake projectile /obj/projectile/hallucination/proc/on_mob_hit(mob/living/hit_mob) diff --git a/code/modules/hydroponics/beekeeping/bee_smoker.dm b/code/modules/hydroponics/beekeeping/bee_smoker.dm index 3daa75f89e6..d04c72adfd6 100644 --- a/code/modules/hydroponics/beekeeping/bee_smoker.dm +++ b/code/modules/hydroponics/beekeeping/bee_smoker.dm @@ -25,6 +25,10 @@ . = ..() beesmoke_loop = new(src) +/obj/item/bee_smoker/Destroy(force) + remove_shared_particles(/particles/smoke/bee_smoke) + return ..() + /obj/item/bee_smoker/attack_self(mob/user) . = ..() if(.) @@ -100,13 +104,13 @@ if(!activated) beesmoke_loop.stop() - QDEL_NULL(particles) + remove_shared_particles(/particles/smoke/bee_smoke) STOP_PROCESSING(SSobj, src) return beesmoke_loop.start() START_PROCESSING(SSobj, src) - particles = new /particles/smoke/bee_smoke + add_shared_particles(/particles/smoke/bee_smoke) /particles/smoke/bee_smoke lifespan = 0.4 SECONDS diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 6edbfd382f9..b5527782011 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -139,10 +139,7 @@ reagents.add_reagent(/datum/reagent/consumable/ethanol/fruit_wine, reagent.volume, data, added_purity = reagent_purity) reagents.del_reagent(reagent.type) -/obj/item/food/grown/grind(datum/reagents/target_holder, mob/user) - if(on_grind() == -1) - return FALSE - +/obj/item/food/grown/grind_atom(datum/reagents/target_holder, mob/user) var/grind_results_num = LAZYLEN(grind_results) if(grind_results_num) var/average_purity = reagents.get_average_purity() @@ -152,9 +149,7 @@ for(var/reagent in grind_results) reagents.add_reagent(reagent, single_reagent_amount, added_purity = average_purity) - if(reagents && target_holder) - reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) - return TRUE + return reagents?.trans_to(target_holder, reagents.total_volume, transferred_by = user) #undef BITE_SIZE_POTENCY_MULTIPLIER #undef BITE_SIZE_VOLUME_MULTIPLIER diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 53ffae4aca0..e6eb41c42e6 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -202,6 +202,7 @@ /obj/machinery/hydroponics/Destroy() if(myseed) QDEL_NULL(myseed) + remove_shared_particles(/particles/pollen) return ..() /obj/machinery/hydroponics/Exited(atom/movable/gone) @@ -536,7 +537,7 @@ age = 0 update_appearance() if(isnull(myseed)) - particles = null + remove_shared_particles(/particles/pollen) /* * Setter proc to set a tray to a new self_sustaining state and update all values associated with it. @@ -798,12 +799,11 @@ T.myseed.set_instability(round((T.myseed.instability+(1/10)*(myseed.instability-T.myseed.instability)))) T.myseed.set_yield(round((T.myseed.yield+(1/2)*(myseed.yield-T.myseed.yield)))) any_adjacent = TRUE - if(isnull(particles)) - particles = new /particles/pollen() + add_shared_particles(/particles/pollen) if(myseed.instability >= 20 && prob(70) && length(T.myseed.reagents_add)) myseed.perform_reagent_pollination(T.myseed) if(!any_adjacent) - particles = null + remove_shared_particles(/particles/pollen) /** * Bee pollinate proc. diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 4589a3c4312..e0e88f7c402 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -50,7 +50,7 @@ var/list/genes = list() /// A list of reagents to add to product. var/list/reagents_add - // Format: "reagent_id" = potency multiplier + // Format: /datum/reagent/type = potency multiplier // Stronger reagents must always come first to avoid being displaced by weaker ones. // Total amount of any reagent in plant is calculated by formula: max(round(potency * multiplier), 1) ///If the chance below passes, then this many weeds sprout during growth diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm index 9d1fcf1d358..7a26e2bd959 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm @@ -300,6 +300,7 @@ righthand_file = 'icons/mob/inhands/items/touchspell_righthand.dmi' slot_flags = null item_flags = ABSTRACT | DROPDEL + resistance_flags = FIRE_PROOF|ACID_PROOF w_class = WEIGHT_CLASS_HUGE hitsound = 'sound/items/weapons/sear.ogg' damtype = BURN @@ -415,6 +416,7 @@ w_class = WEIGHT_CLASS_HUGE slot_flags = null item_flags = ABSTRACT + resistance_flags = FIRE_PROOF|ACID_PROOF sharpness = SHARP_EDGED attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices") attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice") @@ -538,6 +540,7 @@ righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' slot_flags = null item_flags = ABSTRACT + resistance_flags = FIRE_PROOF|ACID_PROOF w_class = WEIGHT_CLASS_HUGE sharpness = SHARP_EDGED wound_bonus = -20 @@ -588,7 +591,7 @@ /obj/item/nullrod/bostaff name = "monk's staff" desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts, it is now used to harass the clown." - force = 14 + force = 10 block_chance = 40 block_sound = 'sound/items/weapons/genhit.ogg' slot_flags = ITEM_SLOT_BACK @@ -616,6 +619,10 @@ icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" return ..() +/obj/item/nullrod/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight + return ..() // Arrhythmic Knife - Lets your walk without rhythm by varying your walk speed. Can't be put away. diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm index d2f6f61251d..2debc004e4d 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm @@ -9,11 +9,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and name = "sinister shard" desc = "This shard seems to be directly linked to some sinister entity. It might be your god! It also gives you a really horrible rash when you hold onto it for too long." items_to_create = list(/obj/item/vorpalscythe) - -/obj/item/organ/cyberimp/arm/shard/scythe/mob_insert(mob/living/carbon/receiver, special, movement_flags) - . = ..() - if(receiver.mind) - ADD_TRAIT(receiver.mind, TRAIT_MORBID, ORGAN_TRAIT) + organ_traits = list(TRAIT_MORBID) /obj/item/organ/cyberimp/arm/shard/scythe/Retract() var/obj/item/vorpalscythe/scythe = active_item diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 9b2dc91137c..a84d4e0e737 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -83,7 +83,7 @@ GLOBAL_LIST_EMPTY(security_officer_distribution) var/ears = null var/accessory = null - var/list/dep_trim = null + var/datum/id_trim/dep_trim = null var/destination = null switch(department) diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm index 99c28b76ff2..4c31092199d 100644 --- a/code/modules/library/bibles.dm +++ b/code/modules/library/bibles.dm @@ -367,8 +367,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list( uses -= 1 to_chat(user, span_userdanger("You try to open the book AND IT BITES YOU!")) playsound(src.loc, 'sound/effects/snap.ogg', 50, TRUE) - var/active_hand_zone = (!(user.active_hand_index % RIGHT_HANDS) ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM) - user.apply_damage(5, BRUTE, active_hand_zone, attacking_item = src) + user.apply_damage(5, BRUTE, user.get_active_hand(), attacking_item = src) to_chat(user, span_notice("Your name appears on the inside cover, in blood.")) owner_name = user.real_name diff --git a/code/modules/library/book.dm b/code/modules/library/book.dm index 7f5f010563a..0a190b94662 100644 --- a/code/modules/library/book.dm +++ b/code/modules/library/book.dm @@ -131,6 +131,7 @@ to_chat(user, span_warning("That title is invalid.")) return name = newtitle + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) book_data.set_title(html_decode(newtitle)) //Don't want to double encode here if("Contents") var/content = tgui_input_text(user, "Write your book's contents (HTML NOT allowed)", "Book Contents", max_length = MAX_PAPER_LENGTH, multiline = TRUE) @@ -140,6 +141,7 @@ to_chat(user, span_warning("The content is invalid.")) return book_data.set_content(html_decode(content)) + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) if("Author") var/author = tgui_input_text(user, "Write the author's name", "Author Name", max_length = MAX_NAME_LEN) if(!user.can_perform_action(src) || !user.can_write(attacking_item)) @@ -148,6 +150,7 @@ to_chat(user, span_warning("The name is invalid.")) return book_data.set_author(html_decode(author)) //Setting this encodes, don't want to double up + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) else if(istype(attacking_item, /obj/item/barcodescanner)) var/obj/item/barcodescanner/scanner = attacking_item diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index d7102fe9600..c3adc298b63 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -441,10 +441,12 @@ GLOBAL_VAR_INIT(library_table_modified, 0) var/id = params["book_id"] inventory -= id inventory_update() + update_static_data_for_all_viewers() return TRUE if("switch_inventory_page") inventory_page = sanitize_page_input(params["page"], inventory_page, inventory_page_count) inventory_update() + update_static_data_for_all_viewers() return TRUE if("checkout") var/list/available = list() diff --git a/code/modules/loadout/categories/heads.dm b/code/modules/loadout/categories/heads.dm index ad23f0b2dfb..69ef68cfff1 100644 --- a/code/modules/loadout/categories/heads.dm +++ b/code/modules/loadout/categories/heads.dm @@ -66,7 +66,7 @@ /datum/loadout_item/head/white_cap name = "Cap (White)" - item_path = /obj/item/clothing/head/soft + item_path = /obj/item/clothing/head/soft/mime /datum/loadout_item/head/yellow_cap name = "Cap (Yellow)" @@ -147,7 +147,7 @@ /datum/loadout_item/head/geranium name = "Geranium" item_path = /obj/item/food/grown/poppy/geranium - + /datum/loadout_item/head/harebell name = "Harebell" item_path = /obj/item/food/grown/harebell diff --git a/code/modules/mafia/abilities/investigative/reveal.dm b/code/modules/mafia/abilities/investigative/reveal.dm index 5e38d3c9faf..24161017156 100644 --- a/code/modules/mafia/abilities/investigative/reveal.dm +++ b/code/modules/mafia/abilities/investigative/reveal.dm @@ -4,11 +4,11 @@ * During the night, revealing someone will announce their role when day comes. * This is one time use, we'll delete ourselves once done. */ -/datum/mafia_ability/reaveal_role +/datum/mafia_ability/reveal_role name = "Reveal" ability_action = "psychologically evaluate" -/datum/mafia_ability/reaveal_role/perform_action_target(datum/mafia_controller/game, datum/mafia_role/day_target) +/datum/mafia_ability/reveal_role/perform_action_target(datum/mafia_controller/game, datum/mafia_role/day_target) . = ..() if(!.) return FALSE @@ -17,7 +17,7 @@ target_role.reveal_role(game, verbose = TRUE) return TRUE -/datum/mafia_ability/vest/clean_action_refs(datum/mafia_controller/game) +/datum/mafia_ability/reveal_role/clean_action_refs(datum/mafia_controller/game) if(using_ability) host_role.role_unique_actions -= src qdel(src) diff --git a/code/modules/mafia/roles/town/town_investigative.dm b/code/modules/mafia/roles/town/town_investigative.dm index a09cff3acd2..76b81eab3bd 100644 --- a/code/modules/mafia/roles/town/town_investigative.dm +++ b/code/modules/mafia/roles/town/town_investigative.dm @@ -20,7 +20,7 @@ hud_icon = "hudpsychologist" revealed_icon = "psychologist" - role_unique_actions = list(/datum/mafia_ability/reaveal_role) + role_unique_actions = list(/datum/mafia_ability/reveal_role) /datum/mafia_role/chaplain name = "Chaplain" diff --git a/code/modules/manufactorio/_manufacturing.dm b/code/modules/manufactorio/_manufacturing.dm index 5c44d1e8615..02cc47999d5 100644 --- a/code/modules/manufactorio/_manufacturing.dm +++ b/code/modules/manufactorio/_manufacturing.dm @@ -106,7 +106,7 @@ if(!manufactury.anchored) return MANUFACTURING_FAIL return manufactury.receive_resource(sending, src, isturf(what_or_dir) ? get_dir(src, what_or_dir) : what_or_dir) - if(next_turf.is_blocked_turf(exclude_mobs = TRUE, source_atom = sending)) + if(next_turf.is_blocked_turf(exclude_mobs = TRUE, source_atom = sending) && !ischasm(next_turf)) return MANUFACTURING_FAIL if(length(next_turf.contents) >= MANUFACTURING_TURF_LAG_LIMIT) return MANUFACTURING_FAIL_FULL diff --git a/code/modules/manufactorio/machines/crafter.dm b/code/modules/manufactorio/machines/crafter.dm index 4b3f2dba1c4..d164976b81a 100644 --- a/code/modules/manufactorio/machines/crafter.dm +++ b/code/modules/manufactorio/machines/crafter.dm @@ -22,6 +22,7 @@ craftsman = AddComponent(/datum/component/personal_crafting/machine) if(ispath(recipe)) recipe = locate(recipe) in (cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes) + START_PROCESSING(SSmanufacturing, src) /obj/machinery/power/manufacturing/crafter/examine(mob/user) . = ..() @@ -45,8 +46,7 @@ var/turf/machine_turf = get_turf(src) if(length(machine_turf.contents) >= MANUFACTURING_TURF_LAG_LIMIT) return MANUFACTURING_FAIL_FULL - receiving.Move(machine_turf, receive_dir) - START_PROCESSING(SSmanufacturing, src) + receiving.forceMove(machine_turf) return MANUFACTURING_SUCCESS /obj/machinery/power/manufacturing/crafter/multitool_act(mob/living/user, obj/item/tool) diff --git a/code/modules/manufactorio/machines/lathe.dm b/code/modules/manufactorio/machines/lathe.dm index 431d7af1c11..351e5c25030 100644 --- a/code/modules/manufactorio/machines/lathe.dm +++ b/code/modules/manufactorio/machines/lathe.dm @@ -125,7 +125,7 @@ return var/craft_time = (design.construction_time * design.lathe_time_factor) ** 0.8 - flick_overlay_view(mutable_appearance(icon, "crafter_printing"), craft_time) + flick_overlay_view(mutable_appearance(icon, "lathe_printing"), craft_time) print_sound.start() add_load(power_cost) busy = addtimer(CALLBACK(src, PROC_REF(do_make_item), design, materials_needed), craft_time, TIMER_UNIQUE | TIMER_STOPPABLE | TIMER_DELETE_ME) diff --git a/code/modules/manufactorio/machines/sorter.dm b/code/modules/manufactorio/machines/sorter.dm index 247a96ea606..b1e45e708eb 100644 --- a/code/modules/manufactorio/machines/sorter.dm +++ b/code/modules/manufactorio/machines/sorter.dm @@ -125,21 +125,32 @@ return ismob(moving) ? moving.Move(get_step(src,dir), dir) : send_resource(moving, dir) /obj/machinery/power/manufacturing/sorter/process() - if(delay_timerid || !length(loc?.contents - 1)) + if(!anchored || delay_timerid || !length(loc?.contents - 1)) return launch_everything() +/// Is target something we should even attempt to start launching? +/obj/machinery/power/manufacturing/sorter/proc/can_be_launched(atom/movable/target) + . = TRUE + if(!istype(target) || target == src || target.anchored) //target is not movable, us or anchored + return FALSE + var/mob/living/probably_living = target + if(isdead(target) || (istype(probably_living) && probably_living.incorporeal_move)) //target is incorporeal + return FALSE + /obj/machinery/power/manufacturing/sorter/proc/on_entered(datum/source, atom/movable/mover) SIGNAL_HANDLER - if(mover == src || !istype(mover) || mover.anchored || delay_timerid) + if(!anchored || !can_be_launched(mover) || delay_timerid) return delay_timerid = addtimer(CALLBACK(src, PROC_REF(launch_everything)), 0.2 SECONDS) /obj/machinery/power/manufacturing/sorter/proc/launch_everything() delay_timerid = null + if(!anchored) + return var/turf/where_we_at = get_turf(src) for(var/atom/movable/mover as anything in where_we_at.contents) - if(mover.anchored) + if(!can_be_launched(mover)) continue for(var/datum/sortrouter_filter/sorting as anything in sort_filters) if(sorting.meets_conditions(mover) == sorting.inverted) diff --git a/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm b/code/modules/mapfluff/ruins/icemoonruin_code/cursed_spring.dm similarity index 52% rename from code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm rename to code/modules/mapfluff/ruins/icemoonruin_code/cursed_spring.dm index 6952dd0af58..c9ee307e423 100644 --- a/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm +++ b/code/modules/mapfluff/ruins/icemoonruin_code/cursed_spring.dm @@ -10,33 +10,30 @@ * */ -/turf/open/water/cursed_spring - name = "cursed spring" - baseturfs = /turf/open/water/cursed_spring +/turf/open/water/hot_spring/cursed + baseturfs = /turf/open/water/hot_spring/cursed planetary_atmos = TRUE initial_gas_mix = ICEMOON_DEFAULT_ATMOS fishing_datum = /datum/fish_source/cursed_spring -/turf/open/water/cursed_spring/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - if(!isliving(arrived)) - return - var/mob/living/to_transform = arrived - var/datum/mind/transforming_mind = to_transform.mind - if(!to_transform.client || to_transform.incorporeal_move || !transforming_mind) - return - if(HAS_TRAIT(transforming_mind, TRAIT_HOT_SPRING_CURSED)) // no double dipping - return +/turf/open/water/hot_spring/cursed/dip_in(atom/movable/movable) + if(!isliving(movable)) + return ..() + var/mob/living/living = movable + if(!living.client || living.incorporeal_move || !living.mind) + return ..() + if(HAS_MIND_TRAIT(living, TRAIT_HOT_SPRING_CURSED)) // no double dipping + return ..() - ADD_TRAIT(transforming_mind, TRAIT_HOT_SPRING_CURSED, TRAIT_GENERIC) - var/mob/living/transformed_mob = to_transform.wabbajack(pick(WABBAJACK_HUMAN, WABBAJACK_ANIMAL), change_flags = RACE_SWAP) + ADD_TRAIT(living.mind, TRAIT_HOT_SPRING_CURSED, TRAIT_GENERIC) + var/mob/living/transformed_mob = living.wabbajack(pick(WABBAJACK_HUMAN, WABBAJACK_ANIMAL), change_flags = RACE_SWAP) if(!transformed_mob) // Wabbajack failed, maybe the mob had godmode or something. - if(!QDELETED(to_transform)) - to_chat(to_transform, span_notice("The water seems to have no effect on you.")) + if(!QDELETED(living)) + to_chat(living, span_notice("The water seems to have no effect on you.")) // because it failed, let's allow them to try again in a lil' bit - addtimer(TRAIT_CALLBACK_REMOVE(transforming_mind, TRAIT_HOT_SPRING_CURSED, TRAIT_GENERIC), 10 SECONDS) - return + addtimer(TRAIT_CALLBACK_REMOVE(living.mind, TRAIT_HOT_SPRING_CURSED, TRAIT_GENERIC), 10 SECONDS) + return ..() var/turf/return_turf = find_safe_turf(extended_safety_checks = TRUE, dense_atoms = FALSE) transformed_mob.forceMove(return_turf) diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm b/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm index 7ef451ddc30..ce57035a766 100644 --- a/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm +++ b/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm @@ -59,13 +59,13 @@ if(offeredmob.mind?.has_antag_datum(/datum/antagonist/ashwalker) && (offeredmob.ckey || offeredmob.get_ghost(FALSE, TRUE))) //special interactions for dead lava lizards with ghosts attached visible_message(span_warning("Serrated tendrils carefully pull [offeredmob] to [src], absorbing the body and creating it anew.")) - var/datum/mind/deadmind + var/mob/deadmob if(offeredmob.ckey) - deadmind = offeredmob + deadmob = offeredmob else - deadmind = offeredmob.get_ghost(FALSE, TRUE) - to_chat(deadmind, "Your body has been returned to the nest. You are being remade anew, and will awaken shortly.
Your memories will remain intact in your new body, as your soul is being salvaged") - SEND_SOUND(deadmind, sound('sound/effects/magic/enter_blood.ogg',volume=100)) + deadmob = offeredmob.get_ghost(FALSE, TRUE) + to_chat(deadmob, "Your body has been returned to the nest. You are being remade anew, and will awaken shortly.
Your memories will remain intact in your new body, as your soul is being salvaged") + SEND_SOUND(deadmob, sound('sound/effects/magic/enter_blood.ogg',volume=100)) addtimer(CALLBACK(src, PROC_REF(remake_walker), offeredmob), 20 SECONDS) offeredmob.forceMove(src) return diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm index 39458ac9429..ac4f257dc2a 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm @@ -245,7 +245,7 @@ //is this being used as part of the haunted trading post ruin? if true, will self destruct when boss dies var/donk_ai_slave = FALSE // machine that the trap inhabits - var/obj/structure/host_machine + var/obj/machinery/host_machine // turf that the trap is on var/turf/my_turf //how long until trap zaps everything, after it detects something diff --git a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm index 994162345da..7c003b0dc7a 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm @@ -86,7 +86,7 @@ ..() return TRUE -/obj/machinery/mod_installer/close_machine(mob/living/carbon/user, density_to_set = TRUE) +/obj/machinery/mod_installer/close_machine(atom/movable/target, density_to_set = TRUE) if(!state_open) return FALSE ..() @@ -108,7 +108,7 @@ /obj/machinery/mod_installer/interact(mob/user) if(state_open) - close_machine(null, user) + close_machine() return else if(busy) balloon_alert(user, "it's locked!") diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index bca2ace6260..10d506f8c48 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -839,7 +839,7 @@ GLOBAL_LIST_EMPTY(map_model_default) if(member_string[length(member_string)] == "}") variables_start = findtext(member_string, "{") - var/path_text = trimtext(copytext(member_string, 1, variables_start)) + var/path_text = trim(copytext(member_string, 1, variables_start)) var/atom_def = text2path(path_text) //path definition, e.g /obj/foo/bar if(!ispath(atom_def, /atom)) // Skip the item if the path does not exist. Fix your crap, mappers! @@ -1011,7 +1011,7 @@ GLOBAL_LIST_EMPTY(map_model_default) // check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7)) var/equal_position = findtext(text,"=",old_position, position) - var/trim_left = trimtext(copytext(text,old_position,(equal_position ? equal_position : position))) + var/trim_left = trim(copytext(text,old_position,(equal_position ? equal_position : position))) var/left_constant = parse_constant(trim_left) if(position) old_position = position + length(text[position]) @@ -1021,7 +1021,7 @@ GLOBAL_LIST_EMPTY(map_model_default) if(equal_position && !isnum(left_constant)) // Associative var, so do the association. // Note that numbers cannot be keys - the RHS is dropped if so. - var/trim_right = trimtext(copytext(text, equal_position + length(text[equal_position]), position)) + var/trim_right = trim(copytext(text, equal_position + length(text[equal_position]), position)) var/right_constant = parse_constant(trim_right) .[left_constant] = right_constant else // simple var diff --git a/code/modules/mining/boulder_processing/boulder.dm b/code/modules/mining/boulder_processing/boulder.dm index e345f870f78..9f2dba12ab2 100644 --- a/code/modules/mining/boulder_processing/boulder.dm +++ b/code/modules/mining/boulder_processing/boulder.dm @@ -158,8 +158,10 @@ /** * This function is called while breaking boulders manually, and drops ore based on the boulder's mineral content. * Quantity of ore spawned here is 1 less than if the boulder was processed by a machine, but clamped at 10 maximum, 1 minimum. + * + * target_destination: Optional - Sets the location directly instead of dropping it */ -/obj/item/boulder/proc/convert_to_ore() +/obj/item/boulder/proc/convert_to_ore(atom/target_destination) for(var/datum/material/picked in custom_materials) var/obj/item/stack/ore/cracked_ore // Take the associated value and convert it into ore stacks... var/quantity = clamp(round((custom_materials[picked] - SHEET_MATERIAL_AMOUNT)/SHEET_MATERIAL_AMOUNT), 1, 10) //but less resources than if they processed it by hand. @@ -168,7 +170,10 @@ if(isnull(cracked_ore_type)) stack_trace("boulder found containing material type [picked.type] with no set ore_type") continue - cracked_ore = new cracked_ore_type (drop_location(), quantity) + var/atom/ore_destination = drop_location() + if(target_destination) + ore_destination = target_destination + cracked_ore = new cracked_ore_type (ore_destination, quantity) SSblackbox.record_feedback("tally", "ore_mined", quantity, cracked_ore.type) ///Moves boulder contents to the drop location, and then deletes the boulder. diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index c2dffd37ee5..216275a2390 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -109,6 +109,7 @@ icon_state = "goliath_cloak" desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." body_parts_covered = CHEST|GROIN|LEGS|ARMS + supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON cold_protection = CHEST|GROIN|LEGS|ARMS min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT heat_protection = CHEST|GROIN|LEGS|ARMS diff --git a/code/modules/mining/equipment/grapple_gun.dm b/code/modules/mining/equipment/grapple_gun.dm index 0247d0164b0..fa7f0b0b735 100644 --- a/code/modules/mining/equipment/grapple_gun.dm +++ b/code/modules/mining/equipment/grapple_gun.dm @@ -10,7 +10,7 @@ inhand_icon_state = "gun" item_flags = NOBLUDGEON ///overlay when the hook is retracted - var/static/mutable_appearance/hook_overlay = new(icon = 'icons/obj/mining.dmi', icon_state = "grapple_gun_hooked") + var/static/mutable_appearance/hook_overlay = mutable_appearance(icon = 'icons/obj/mining.dmi', icon_state = "grapple_gun_hooked") ///is the hook retracted var/hooked = TRUE ///addtimer id for launching the user diff --git a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm index d58ecf4da42..d6767fe913b 100644 --- a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm +++ b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm @@ -71,7 +71,7 @@ id = "brimdust_coating" stacks = 0 max_stacks = 3 - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK consumed_on_threshold = FALSE alert_type = /atom/movable/screen/alert/status_effect/brimdust_coating status_type = STATUS_EFFECT_REFRESH // Allows us to add one stack at a time by just applying the effect diff --git a/code/modules/mining/equipment/monster_organs/monster_organ.dm b/code/modules/mining/equipment/monster_organs/monster_organ.dm index b6bd54a361e..ccd3a08c19c 100644 --- a/code/modules/mining/equipment/monster_organs/monster_organ.dm +++ b/code/modules/mining/equipment/monster_organs/monster_organ.dm @@ -83,7 +83,7 @@ deltimer(decay_timer) return ..() -/obj/item/organ/monster_core/mob_insert(mob/living/carbon/target_carbon, special = FALSE, movement_flags) +/obj/item/organ/monster_core/on_mob_insert(mob/living/carbon/target_carbon, special = FALSE, movement_flags) . = ..() if (inert) @@ -96,7 +96,7 @@ target_carbon.visible_message(span_notice("[src] stabilizes as it's inserted.")) return TRUE -/obj/item/organ/monster_core/mob_remove(mob/living/carbon/target_carbon, special, movement_flags) +/obj/item/organ/monster_core/on_mob_remove(mob/living/carbon/target_carbon, special, movement_flags) if (!inert && !special) owner.visible_message(span_notice("[src] rapidly decays as it's removed.")) go_inert() diff --git a/code/modules/mining/equipment/monster_organs/rush_gland.dm b/code/modules/mining/equipment/monster_organs/rush_gland.dm index cf901269e90..8012286975a 100644 --- a/code/modules/mining/equipment/monster_organs/rush_gland.dm +++ b/code/modules/mining/equipment/monster_organs/rush_gland.dm @@ -25,7 +25,7 @@ . = ..() RegisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED, PROC_REF(trigger_organ_action_on_sig)) -/obj/item/organ/monster_core/rush_gland/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/monster_core/rush_gland/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED) diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index b22603f52d9..ce0c2d923a4 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -15,9 +15,14 @@ icon_state = "capsule" icon = 'icons/obj/mining.dmi' w_class = WEIGHT_CLASS_TINY + ///The id we use to fetch the template datum var/template_id = "shelter_alpha" + ///The template datum we use to load the shelter var/datum/map_template/shelter/template + ///If true, this capsule is active and will deploy the area if conditions are met. var/used = FALSE + ///Will this capsule yeet mobs back once the area is deployed? + var/yeet_back = TRUE /obj/item/survivalcapsule/proc/get_template() if(template) @@ -37,7 +42,7 @@ . += "This capsule has the [template.name] stored." . += template.description -/obj/item/survivalcapsule/interact(mob/user) +/obj/item/survivalcapsule/interact(mob/living/user) . = ..() if(.) return . @@ -50,6 +55,9 @@ loc.visible_message(span_warning("[src] begins to shake. Stand back!")) used = TRUE addtimer(CALLBACK(src, PROC_REF(expand), user), 5 SECONDS) + if(iscarbon(user)) + var/mob/living/carbon/carbon = user + carbon.throw_mode_on(THROW_MODE_TOGGLE) return TRUE /// Expands the capsule into a full shelter, placing the template at the item's location (NOT triggerer's location) @@ -58,24 +66,32 @@ return var/turf/deploy_location = get_turf(src) - var/status = template.check_deploy(deploy_location) - switch(status) - if(SHELTER_DEPLOY_BAD_AREA) - loc.visible_message(span_warning("[src] will not function in this area.")) - if(SHELTER_DEPLOY_BAD_TURFS, SHELTER_DEPLOY_ANCHORED_OBJECTS, SHELTER_DEPLOY_OUTSIDE_MAP) - loc.visible_message(span_warning("[src] doesn't have room to deploy! You need to clear a [template.width]x[template.height] area!")) - + var/status = template.check_deploy(deploy_location, src, get_ignore_flags()) if(status != SHELTER_DEPLOY_ALLOWED) + fail_feedback(status) used = FALSE return - yote_nearby(deploy_location) + if(yeet_back) + yote_nearby(deploy_location) template.load(deploy_location, centered = TRUE) trigger_admin_alert(triggerer, deploy_location) playsound(src, 'sound/effects/phasein.ogg', 100, TRUE) new /obj/effect/particle_effect/fluid/smoke(get_turf(src)) qdel(src) +/// Returns a bitfield used to ignore some checks in template.check_deploy() +/obj/item/survivalcapsule/proc/get_ignore_flags() + return NONE + +///Returns a message including the reason why it couldn't be deployed +/obj/item/survivalcapsule/proc/fail_feedback(status) + switch(status) + if(SHELTER_DEPLOY_BAD_AREA) + loc.visible_message(span_warning("[src] will not function in this area.")) + if(SHELTER_DEPLOY_BAD_TURFS, SHELTER_DEPLOY_ANCHORED_OBJECTS, SHELTER_DEPLOY_OUTSIDE_MAP, SHELTER_DEPLOY_BANNED_OBJECTS) + loc.visible_message(span_warning("[src] doesn't have room to deploy! You need to clear a [template.width]x[template.height] area!")) + /// Throws any mobs near the deployed location away from the item / shelter /// Does some math to make closer mobs get thrown further /obj/item/survivalcapsule/proc/yote_nearby(turf/deploy_location) diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm index 6ffcfa7bc67..fa9b63a4658 100644 --- a/code/modules/mining/equipment/wormhole_jaunter.dm +++ b/code/modules/mining/equipment/wormhole_jaunter.dm @@ -103,7 +103,7 @@ light_on = FALSE wibbles = FALSE -/obj/effect/portal/jaunt_tunnel/teleport(atom/movable/M) +/obj/effect/portal/jaunt_tunnel/teleport(atom/movable/M, force = FALSE) . = ..() if(.) // KERPLUNK diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index ee700e3d977..e7966764b6a 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -150,10 +150,10 @@ points += SHEET_POINT_VALUE * input.amount return ..() -/obj/machinery/mineral/stacking_machine/laborstacker/attackby(obj/item/weapon, mob/user, params) +/obj/machinery/mineral/stacking_machine/laborstacker/base_item_interaction(mob/living/user, obj/item/weapon, list/modifiers) if(istype(weapon, /obj/item/stack/sheet)) process_sheet(weapon) - return + return ITEM_INTERACT_SUCCESS return ..() /**********************Point Lookup Console**************************/ diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm index fe338b35ccb..b50c966d230 100644 --- a/code/modules/mining/lavaland/tendril_loot.dm +++ b/code/modules/mining/lavaland/tendril_loot.dm @@ -760,8 +760,10 @@ berserk_value *= PROJECTILE_HIT_MULTIPLIER berserk_charge = clamp(round(berserk_charge + berserk_value), 0, MAX_BERSERK_CHARGE) if(berserk_charge >= MAX_BERSERK_CHARGE) + var/datum/action/item_action/berserk_mode/ragemode = locate() in actions to_chat(owner, span_notice("Berserk mode is fully charged.")) balloon_alert(owner, "berserk charged") + ragemode?.build_all_button_icons(UPDATE_BUTTON_STATUS) /obj/item/clothing/head/hooded/berserker/IsReflect() if(berserk_active) @@ -769,6 +771,7 @@ /// Starts berserk, reducing incoming brute by 50%, doubled attacking speed, NOGUNS trait, adding a color and giving them the berserk movespeed modifier /obj/item/clothing/head/hooded/berserker/proc/berserk_mode(mob/living/carbon/human/user) + var/datum/action/item_action/berserk_mode/ragemode = locate() in actions to_chat(user, span_warning("You enter berserk mode.")) playsound(user, 'sound/effects/magic/staff_healing.ogg', 50) user.add_movespeed_modifier(/datum/movespeed_modifier/berserk) @@ -779,6 +782,7 @@ ADD_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT) berserk_active = TRUE START_PROCESSING(SSobj, src) + ragemode?.build_all_button_icons(UPDATE_BUTTON_STATUS) /// Ends berserk, reverting the changes from the proc [berserk_mode] /obj/item/clothing/head/hooded/berserker/proc/end_berserk(mob/living/carbon/human/user) @@ -787,6 +791,8 @@ berserk_active = FALSE if(QDELETED(user)) return + var/datum/action/item_action/berserk_mode/ragemode = locate() in actions + ragemode?.build_all_button_icons(UPDATE_BUTTON_STATUS) to_chat(user, span_warning("You exit berserk mode.")) playsound(user, 'sound/effects/magic/summonitems_generic.ogg', 50) user.remove_movespeed_modifier(/datum/movespeed_modifier/berserk) @@ -811,7 +817,11 @@ /obj/item/drake_remains/Initialize(mapload) . = ..() - particles = new /particles/bonfire() + add_shared_particles(/particles/bonfire) + +/obj/item/drake_remains/Destroy(force) + remove_shared_particles(/particles/bonfire) + return ..() /obj/item/clothing/glasses/godeye name = "eye of god" diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 672879e8d38..a0f6a25e9d2 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -229,21 +229,27 @@ for(var/datum/material/material as anything in mat_container.materials) var/amount = mat_container.materials[material] var/sheet_amount = amount / SHEET_MATERIAL_AMOUNT + var/obj/sheet_type = material.sheet_type data["materials"] += list(list( "name" = material.name, "id" = REF(material), "amount" = sheet_amount, "category" = "material", "value" = ore_values[material.type], + "icon" = sheet_type::icon, + "icon_state" = sheet_type::icon_state, )) for(var/research in stored_research.researched_designs) var/datum/design/alloy = SSresearch.techweb_design_by_id(research) + var/obj/alloy_type = alloy.build_path data["materials"] += list(list( "name" = alloy.name, "id" = alloy.id, "category" = "alloy", "amount" = can_smelt_alloy(alloy), + "icon" = alloy_type::icon, + "icon_state" = alloy_type::icon_state, )) data["disconnected"] = null @@ -274,29 +280,6 @@ ) return data -/obj/machinery/mineral/ore_redemption/ui_static_data(mob/user) - var/list/data = list() - - var/datum/component/material_container/mat_container = materials.mat_container - if (mat_container) - for(var/datum/material/material as anything in mat_container.materials) - var/obj/material_display = initial(material.sheet_type) - data["material_icons"] += list(list( - "id" = REF(material), - "product_icon" = icon2base64(getFlatIcon(image(icon = initial(material_display.icon), icon_state = initial(material_display.icon_state)), no_anim=TRUE)), - )) - - for(var/research in stored_research.researched_designs) - var/datum/design/alloy = SSresearch.techweb_design_by_id(research) - var/obj/alloy_display = initial(alloy.build_path) - data["material_icons"] += list(list( - "id" = alloy.id, - "product_icon" = icon2base64(getFlatIcon(image(icon = initial(alloy_display.icon), icon_state = initial(alloy_display.icon_state)), no_anim=TRUE)), - )) - - return data - - /obj/machinery/mineral/ore_redemption/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) diff --git a/code/modules/mining/satchel_ore_box.dm b/code/modules/mining/satchel_ore_box.dm index 94be35108d0..2745bdb5d09 100644 --- a/code/modules/mining/satchel_ore_box.dm +++ b/code/modules/mining/satchel_ore_box.dm @@ -66,6 +66,13 @@ else return ..() +/obj/structure/ore_box/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if(istype(arrived, /obj/item/boulder) && ismecha(loc)) //Boulders being put into a mech's orebox get processed + var/obj/item/boulder/to_process = arrived + to_process.convert_to_ore(src) + qdel(to_process) + /obj/structure/ore_box/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) diff --git a/code/modules/mining/shelters.dm b/code/modules/mining/shelters.dm index c3fdc820bef..5a08b292022 100644 --- a/code/modules/mining/shelters.dm +++ b/code/modules/mining/shelters.dm @@ -1,36 +1,39 @@ +///Map templates used for bluespace survival capsules. /datum/map_template/shelter + has_ceiling = TRUE + ceiling_turf = /turf/open/floor/engine/hull + ceiling_baseturfs = list(/turf/open/floor/plating) + ///The id of the shelter template in the relative list from the mapping subsystem var/shelter_id + ///The description of the shelter, shown when examining survival capsule. var/description + ///If turf in the affected turfs is in this list, the survival capsule cannot be deployed. var/list/blacklisted_turfs - var/list/whitelisted_turfs + ///Areas where the capsule cannot be deployed. var/list/banned_areas - var/list/banned_objects - has_ceiling = TRUE - ceiling_turf = /turf/open/floor/engine/hull - ceiling_baseturfs = list(/turf/open/floor/plating) + ///If any object in this list is found in the affected turfs, the capsule cannot deploy. + var/list/banned_objects = list() /datum/map_template/shelter/New() . = ..() blacklisted_turfs = typecacheof(/turf/closed) - whitelisted_turfs = list() - banned_areas = typecacheof(/area/shuttle) - banned_objects = list() + banned_areas = typecacheof(list(/area/shuttle, /area/station/holodeck)) -/datum/map_template/shelter/proc/check_deploy(turf/deploy_location) +/datum/map_template/shelter/proc/check_deploy(turf/deploy_location, obj/item/survivalcapsule/capsule, ignore_flags = NONE) var/affected = get_affected_turfs(deploy_location, centered=TRUE) - for(var/turf/T in affected) - var/area/A = get_area(T) - if(is_type_in_typecache(A, banned_areas)) + for(var/turf/turf in affected) + var/area/area = get_area(turf) + if(is_type_in_typecache(area, banned_areas)) return SHELTER_DEPLOY_BAD_AREA - var/banned = is_type_in_typecache(T, blacklisted_turfs) - var/permitted = is_type_in_typecache(T, whitelisted_turfs) - if(banned && !permitted) + if(is_type_in_typecache(turf, blacklisted_turfs)) return SHELTER_DEPLOY_BAD_TURFS - for(var/obj/O in T) - if((O.density && O.anchored) || is_type_in_typecache(O, banned_objects)) + for(var/obj/object in turf) + if(!(ignore_flags & CAPSULE_IGNORE_ANCHORED_OBJECTS) && object.density && object.anchored) return SHELTER_DEPLOY_ANCHORED_OBJECTS + if(!(ignore_flags & CAPSULE_IGNORE_BANNED_OBJECTS) && is_type_in_typecache(object, banned_objects)) + return SHELTER_DEPLOY_BANNED_OBJECTS // Check if the shelter sticks out of map borders var/shelter_origin_x = deploy_location.x - round(width/2) @@ -53,7 +56,7 @@ /datum/map_template/shelter/alpha/New() . = ..() - whitelisted_turfs = typecacheof(/turf/closed/mineral) + blacklisted_turfs -= typesof(/turf/closed/mineral) banned_objects = typecacheof(/obj/structure/stone_tile) /datum/map_template/shelter/beta @@ -68,7 +71,7 @@ /datum/map_template/shelter/beta/New() . = ..() - whitelisted_turfs = typecacheof(/turf/closed/mineral) + blacklisted_turfs -= typesof(/turf/closed/mineral) banned_objects = typecacheof(/obj/structure/stone_tile) /datum/map_template/shelter/charlie @@ -83,7 +86,7 @@ /datum/map_template/shelter/charlie/New() . = ..() - whitelisted_turfs = typecacheof(/turf/closed/mineral) + blacklisted_turfs -= typesof(/turf/closed/mineral) banned_objects = typecacheof(/obj/structure/stone_tile) /datum/map_template/shelter/toilet @@ -96,5 +99,76 @@ /datum/map_template/shelter/toilet/New() . = ..() - whitelisted_turfs = typecacheof(/turf/closed/mineral) + blacklisted_turfs -= typesof(/turf/closed/mineral) banned_objects = typecacheof(/obj/structure/stone_tile) + +///Not exactly mining shelters, but they make use of survival capsules code. +/datum/map_template/shelter/fishing + name = "Freshwater Spring" + shelter_id = "fishing_default" + description = "A spring from which you can fish several freshwater fish, including goldfish, catfish and pikes." + mappath = "_maps/templates/fishing_freshwater.dmm" + has_ceiling = FALSE + ///The icon shown in the radial menu + var/radial_icon = "river" + /** + * If FALSE, the capsule needs to be emagged for this template to be selectable. + * its usage will also be logged, and admins warned if used indoors. + */ + var/safe = TRUE + +/datum/map_template/shelter/fishing/New() + . = ..() + blacklisted_turfs -= typesof(/turf/closed/mineral) + blacklisted_turfs += typecacheof(/turf/open/openspace) + // Stop the capsule from being used around pipes and cables (if not emagged) cuz it'd look bad and a bit disruptive. + banned_objects = typecacheof(list( + /obj/structure/disposalpipe, + /obj/machinery/atmospherics/pipe, + /obj/structure/cable, + /obj/structure/transit_tube, + )) + +/datum/map_template/shelter/fishing/beach + name = "Saltwater Spring" + shelter_id = "fishing_beach" + mappath = "_maps/templates/fishing_saltwater.dmm" + description = "A spring from which you can fish several saltwater fish, including clownfish, pufferfish and stingrays." + radial_icon = "seaboat" + +/datum/map_template/shelter/fishing/tizira + name = "Tiziran Spring" + shelter_id = "fishing_tizira" + mappath = "_maps/templates/fishing_tizira.dmm" + description = "A spring from which you can fish several fish native to the lizardfolk's native planet, Tizira." + radial_icon = "planet" + +/datum/map_template/shelter/fishing/hot_spring + name = "Hot Spring" + shelter_id = "fishing_hot_spring" + mappath = "_maps/templates/fishing_hot_spring.dmm" + description = "A hot spring. Its purposes as a fishing spot is limited, but at least you get to have a relaxing bath." + radial_icon = "onsen" + +/datum/map_template/shelter/fishing/ice + name = "Ice Fishing Spot" + shelter_id = "fishing_ice" + mappath = "_maps/templates/fishing_ice.dmm" + description = "A small, already dug ice hole surrounded by snow. You can catch salmon and arctic char here." + radial_icon = "ice" + +/datum/map_template/shelter/fishing/lava + name = "Lava Fishing Spot" + shelter_id = "fishing_lava" + mappath = "_maps/templates/fishing_lava.dmm" + description = "A small 2x2 puddle of not-safe-for-live lava. You can catch lava loops here, and maybe a chest." + radial_icon = "lava" + safe = FALSE + +/datum/map_template/shelter/fishing/plasma + name = "Plasma Fishing Spot" + shelter_id = "fishing_plasma" + mappath = "_maps/templates/fishing_plasma.dmm" + description = "A small 2x2 puddle of not-safe-for-live plasma. You can catch lava loops and arctic chrabs here." + radial_icon = "plasma" + safe = FALSE diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 74be453b2b1..cc03fb05a30 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -323,7 +323,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghostize(FALSE) // FALSE parameter is so we can never re-enter our body. U ded. return TRUE -/mob/camera/verb/ghost() +/mob/eye/verb/ghost() set category = "OOC" set name = "Ghost" set desc = "Relinquish your life and enter the land of the dead." @@ -704,7 +704,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp //this is called when a ghost is drag clicked to something. /mob/dead/observer/mouse_drop_dragged(atom/over, mob/user) - if (isobserver(user) && user.client.holder && (isliving(over) || iscameramob(over))) + if (isobserver(user) && user.client.holder && (isliving(over) || iseyemob(over))) user.client.holder.cmd_ghost_drag(src, over) /mob/dead/observer/Topic(href, href_list) diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index bdb0b7ce37e..d2823eae2b5 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -285,7 +285,7 @@ GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new) * Helper POI validation function passed as a callback to various SSpoints_of_interest procs. * * Provides extended validation above and beyond standard, limiting mob POIs without minds or ckeys - * unless they're mobs, camera mobs or megafauna. Also allows exceptions for mobs that are deadchat controlled. + * unless they're mobs, eye mobs or megafauna. Also allows exceptions for mobs that are deadchat controlled. * * If they satisfy that requirement, falls back to default validation for the POI. */ @@ -294,7 +294,7 @@ GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new) if(!potential_mob_poi.mind && !potential_mob_poi.ckey) if(!mob_allowed_typecache) mob_allowed_typecache = typecacheof(list( - /mob/camera, + /mob/eye, /mob/living/basic/regal_rat, /mob/living/simple_animal/bot, /mob/living/simple_animal/hostile/megafauna, diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 28b5575108d..924498332ec 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -46,7 +46,7 @@ /datum/emote/help key = "help" - mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai, /mob/camera/imaginary_friend) + mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai, /mob/eye/imaginary_friend) /datum/emote/help/run_emote(mob/user, params, type_override, intentional) . = ..() @@ -78,8 +78,8 @@ key = "flip" key_third_person = "flips" hands_use_check = TRUE - mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer, /mob/camera/imaginary_friend) - mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai, /mob/camera/imaginary_friend) + mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer, /mob/eye/imaginary_friend) + mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai, /mob/eye/imaginary_friend) /datum/emote/flip/run_emote(mob/user, params , type_override, intentional) . = ..() @@ -111,8 +111,8 @@ key = "spin" key_third_person = "spins" hands_use_check = TRUE - mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer, /mob/camera/imaginary_friend) - mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/camera/imaginary_friend) + mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer, /mob/eye/imaginary_friend) + mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/eye/imaginary_friend) /datum/emote/spin/run_emote(mob/user, params, type_override, intentional) . = ..() diff --git a/code/modules/mob/eye/camera/camera.dm b/code/modules/mob/eye/camera/camera.dm new file mode 100644 index 00000000000..9f3ddc7b2ee --- /dev/null +++ b/code/modules/mob/eye/camera/camera.dm @@ -0,0 +1,80 @@ +/** + * Eye mob used to look around a [camera network][/datum/cameranet]. \ + * As it moves, it makes requests to the network to update what the user can and cannot see. + */ +/mob/eye/camera + name = "Inactive Camera Eye" + icon = 'icons/mob/eyemob.dmi' + icon_state = "generic_camera" + + invisibility = INVISIBILITY_OBSERVER + interaction_range = INFINITY + /// If TRUE, the eye will cover turfs hidden to the cameranet with static. + var/use_visibility = TRUE + /// List of [camera chunks][/datum/camerachunk] visible to this camera. + /// Please don't interface with this directly. Use the [cameranet][/datum/cameranet]. + var/final/list/datum/camerachunk/visibleCameraChunks = list() + /// NxN Range of a single camera chunk. + var/static_visibility_range = 16 + +/mob/eye/camera/Initialize(mapload) + . = ..() + GLOB.camera_eyes += src + +/mob/eye/camera/Destroy() + for(var/datum/camerachunk/chunk in visibleCameraChunks) + chunk.remove(src) + GLOB.camera_eyes -= src + return ..() + +/** + * Getter proc for getting the current user's client. + * + * The base version of this proc returns null. + * Subtypes are expected to overload this proc and make it return something meaningful. + */ +/mob/eye/camera/proc/GetViewerClient() + RETURN_TYPE(/client) + SHOULD_BE_PURE(TRUE) + + return null + +/** + * Use this when setting the camera eye's location directly. \ + * It will also attempt to update visible chunks. + */ +/mob/eye/camera/proc/setLoc(destination, force_update = FALSE) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(TRUE) + + destination = get_turf(destination) + if(!force_update && (destination == get_turf(src))) + return + + if(destination) + abstract_move(destination) + else + moveToNullspace() + + if(use_visibility) + update_visibility() + update_parallax_contents() + +/// Sends a visibility query to the cameranet. +/// Can be used as a signal handler. +/mob/eye/camera/proc/update_visibility() + SIGNAL_HANDLER + PROTECTED_PROC(TRUE) + SHOULD_CALL_PARENT(TRUE) + + if(use_visibility) + GLOB.cameranet.visibility(src) + +/mob/eye/camera/zMove(dir, turf/target, z_move_flags = NONE, recursions_left = 1, list/falling_movs) + . = ..() + if(.) + setLoc(loc, force_update = TRUE) + +/mob/eye/camera/Move() + SHOULD_NOT_OVERRIDE(TRUE) + return diff --git a/code/modules/mob/eye/camera/remote.dm b/code/modules/mob/eye/camera/remote.dm new file mode 100644 index 00000000000..97fe41886ce --- /dev/null +++ b/code/modules/mob/eye/camera/remote.dm @@ -0,0 +1,130 @@ +/** + * A camera controlled by a machine-operating user, like advanced cameras. + * Handles assigning/unassigning it's users, as well as applying sight effects. + */ +/mob/eye/camera/remote + /// Weakref to the current user of this eye. Must be a [living mob][/mob/living]. + var/datum/weakref/user_ref + /// Weakref to the creator of this eye. Must be a [machine][/obj/machinery]. + var/datum/weakref/origin_ref + + /// TRUE if this camera should show itself to the user. + var/visible_to_user = FALSE + /// If visible_to_user is TRUE, it will show this in the center of the screen. + VAR_PROTECTED/image/user_image + + /* The below code could be shared by AI eyes... */ + + /// If TRUE, the eye will have acceleration when moving. + var/acceleration = TRUE + /// Used internally for calculating wait time. (world.timeofday + wait_time) + VAR_FINAL/last_moved = 0 + /// The amount of time that must pass before var/sprint is reset. + VAR_PROTECTED/wait_time = 5 DECISECONDS + /// The speed of the camera. Scales from initial(sprint) to var/max_sprint + VAR_PROTECTED/sprint = 10 + /// Amount of speed that is added to var/sprint. + VAR_PROTECTED/momentum = 0.5 + /// The maximum sprint that this eye can reach. + VAR_PROTECTED/max_sprint = 50 + + +/mob/eye/camera/remote/Initialize(mapload, obj/machinery/creator) + if(!creator) + return INITIALIZE_HINT_QDEL + + . = ..() + + origin_ref = WEAKREF(creator) + if(visible_to_user) + set_user_icon(icon, icon_state) + +/mob/eye/camera/remote/Destroy() + var/mob/living/user = user_ref?.resolve() + var/obj/machinery/origin = origin_ref?.resolve() + if(origin && user) + origin.remove_eye_control(user,src) + + assign_user(null) + origin_ref = null + return ..() + +/mob/eye/camera/remote/proc/assign_user(mob/living/new_user) + var/mob/living/old_user = user_ref?.resolve() + if(old_user) + old_user.remote_control = null + old_user.reset_perspective(null) + name = initial(src.name) + + var/client/old_user_client = GetViewerClient() + if(user_image && old_user_client) + old_user_client.images -= user_image + + user_ref = WEAKREF(new_user) //The user_ref can still be null! + + if(new_user) + new_user.remote_control = src + new_user.reset_perspective(src) + name = "Camera Eye ([new_user.name])" + + var/client/new_user_client = GetViewerClient() + if(user_image && new_user_client) + new_user_client.images += user_image + +/** + * Sets the camera's user image to this icon and state. + * If chosen_icon is null, the user image will be removed. + */ +/mob/eye/camera/remote/proc/set_user_icon(icon/chosen_icon, icon_state) + SHOULD_CALL_PARENT(TRUE) + + var/client/user_client = GetViewerClient() + + if(!isnull(chosen_icon)) + set_user_icon(null) //remove whatever the last icon was + if(!isicon(chosen_icon) || !(!isnull(icon_state) && istext(icon_state))) + CRASH("Tried to set [src]'s user_image with bad parameters") + + user_image = image(chosen_icon, src, icon_state, FLY_LAYER) + if(user_client) + user_client.images += user_image + else + if(user_client) + user_client.images -= user_image + QDEL_NULL(user_image) + +/mob/eye/camera/remote/update_remote_sight(mob/living/user) + user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras + user.set_sight(SEE_TURFS) + return TRUE + +/mob/eye/camera/remote/GetViewerClient() + var/mob/living/user = user_ref?.resolve() + + if(user) + return user.client + return null + +/mob/eye/camera/remote/setLoc(turf/destination, force_update = FALSE) + . = ..() + + var/client/user_client = GetViewerClient() + if(user_image && user_client) + SET_PLANE(user_image, ABOVE_GAME_PLANE, destination) //incase we move a z-level + +/mob/eye/camera/remote/relaymove(mob/living/user, direction) + var/initial = initial(src.sprint) + + if(last_moved < world.timeofday) // It's been too long since we last moved, reset sprint + sprint = initial + + for(var/i = 0; i < max(sprint, initial); i += 20) + var/turf/step = get_turf(get_step(src, direction)) + if(step) + setLoc(step) + + last_moved = world.timeofday + wait_time + if(acceleration) + sprint = min(sprint + momentum, max_sprint) + else + sprint = initial diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/eye/eye.dm similarity index 65% rename from code/modules/mob/camera/camera.dm rename to code/modules/mob/eye/eye.dm index eb0d787f64b..54cb5e2b40a 100644 --- a/code/modules/mob/camera/camera.dm +++ b/code/modules/mob/eye/eye.dm @@ -1,6 +1,6 @@ -// Camera mob, used by AI camera and blob. -/mob/camera - name = "camera mob" +/// Eye mob, used by cameras and overminds such as blobs. +/mob/eye + name = "eye mob" density = FALSE move_force = INFINITY move_resist = INFINITY @@ -8,47 +8,47 @@ invisibility = INVISIBILITY_ABSTRACT // No one can see us sight = SEE_SELF status_flags = NONE - /// Toggles if the camera can move on shuttles + /// Toggles if the eye can move on shuttles var/move_on_shuttle = FALSE - /// Toggles if the camera can use emotes + /// Toggles if the eye can use emotes var/has_emotes = FALSE -/mob/camera/Initialize(mapload) +/mob/eye/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_GODMODE, INNATE_TRAIT) SSpoints_of_interest.make_point_of_interest(src) if(!move_on_shuttle) ADD_TRAIT(src, TRAIT_BLOCK_SHUTTLE_MOVEMENT, INNATE_TRAIT) -/mob/camera/experience_pressure_difference() +/mob/eye/experience_pressure_difference() return -/mob/camera/canUseStorage() +/mob/eye/canUseStorage() return FALSE -/mob/camera/up() +/mob/eye/up() set name = "Move Upwards" set category = "IC" if(zMove(UP, z_move_flags = ZMOVE_FEEDBACK)) to_chat(src, span_notice("You move upwards.")) -/mob/camera/down() +/mob/eye/down() set name = "Move Down" set category = "IC" if(zMove(DOWN, z_move_flags = ZMOVE_FEEDBACK)) to_chat(src, span_notice("You move down.")) -/mob/camera/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) +/mob/eye/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) z_move_flags |= ZMOVE_IGNORE_OBSTACLES //cameras do not respect these FLOORS you speak so much of return ..() -/mob/camera/emote(act, m_type=1, message = null, intentional = FALSE, force_silence = FALSE) +/mob/eye/emote(act, m_type=1, message = null, intentional = FALSE, force_silence = FALSE) if(has_emotes) return ..() return FALSE -/mob/camera/update_sight() +/mob/eye/update_sight() lighting_color_cutoffs = list(lighting_cutoff_red, lighting_cutoff_green, lighting_cutoff_blue) return ..() diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index ee173795c13..14e38e65144 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -16,7 +16,7 @@ //So we're treating each "pair" of limbs as a team, so "both" refers to them /mob/proc/get_inactive_hand_index() var/other_hand = 0 - if(!(active_hand_index % 2)) + if(IS_RIGHT_INDEX(active_hand_index)) other_hand = active_hand_index-1 //finding the matching "left" limb else other_hand = active_hand_index+1 //finding the matching "right" limb @@ -33,7 +33,7 @@ //Odd = left. Even = right /mob/proc/held_index_to_dir(i) - if(!(i % 2)) + if(IS_RIGHT_INDEX(i)) return "r" return "l" @@ -132,7 +132,7 @@ if(i > 2) hand += "upper " var/num = 0 - if(!(i % 2)) + if(IS_RIGHT_INDEX(i)) num = i-2 hand += "right hand" else diff --git a/code/modules/mob/living/basic/blob_minions/blob_mob.dm b/code/modules/mob/living/basic/blob_minions/blob_mob.dm index 6c30bdfe16d..1ebd53bb592 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_mob.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_mob.dm @@ -25,7 +25,7 @@ AddComponent(/datum/component/blob_minion, on_strain_changed = CALLBACK(src, PROC_REF(on_strain_updated))) /// Called when our blob overmind changes their variant, update some of our mob properties -/mob/living/basic/blob_minion/proc/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain) +/mob/living/basic/blob_minion/proc/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain) return /// Associates this mob with a specific blob factory node diff --git a/code/modules/mob/living/basic/blob_minions/blob_spore.dm b/code/modules/mob/living/basic/blob_minions/blob_spore.dm index 9c7b8001e06..6a61e8a56a2 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_spore.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_spore.dm @@ -88,7 +88,7 @@ z_turf = get_turf(factory) /// If the blob changes to distributed neurons then you can control the spores -/mob/living/basic/blob_minion/spore/minion/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain) +/mob/living/basic/blob_minion/spore/minion/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain) if (isnull(overmind)) REMOVE_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT) else diff --git a/code/modules/mob/living/basic/blob_minions/blobbernaut.dm b/code/modules/mob/living/basic/blob_minions/blobbernaut.dm index 13146c3b5c5..a548a30be6a 100644 --- a/code/modules/mob/living/basic/blob_minions/blobbernaut.dm +++ b/code/modules/mob/living/basic/blob_minions/blobbernaut.dm @@ -92,7 +92,7 @@ to_chat(src, span_infoplain("The [blobstrain.name] reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")) /// Set our attack damage based on blob's properties -/mob/living/basic/blob_minion/blobbernaut/minion/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain) +/mob/living/basic/blob_minion/blobbernaut/minion/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain) if (isnull(overmind)) melee_damage_lower = initial(melee_damage_lower) melee_damage_upper = initial(melee_damage_upper) diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm index be7ce8bc5dd..19534cebafd 100644 --- a/code/modules/mob/living/basic/bots/_bots.dm +++ b/code/modules/mob/living/basic/bots/_bots.dm @@ -16,10 +16,6 @@ GLOBAL_LIST_INIT(command_strings, list( basic_mob_flags = DEL_ON_DEATH density = FALSE - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "medibot0" - base_icon_state = "medibot" - damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, STAMINA = 0, OXY = 0) habitable_atmos = null hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) @@ -103,6 +99,8 @@ GLOBAL_LIST_INIT(command_strings, list( TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, ) + ///name of the UI we will attempt to open + var/bot_ui = "SimpleBot" /// If true we will offer this COOLDOWN_DECLARE(offer_ghosts_cooldown) @@ -371,7 +369,7 @@ GLOBAL_LIST_INIT(command_strings, list( /mob/living/basic/bot/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "SimpleBot", name) + ui = new(user, src, bot_ui, name) ui.open() /mob/living/basic/bot/click_alt(mob/user) diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index d777614d743..dfef7f1e378 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -17,16 +17,18 @@ /datum/ai_planning_subtree/manage_unreachable_list, ) max_target_distance = AI_BOT_PATH_LENGTH + can_idle = FALSE + ///minimum distance we need to be from our target in path calculations + var/minimum_distance = 0 ///keys to be reset when the bot is reseted var/list/reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, ) - can_idle = FALSE /datum/targeting_strategy/basic/bot/can_attack(mob/living/living_mob, atom/the_target, vision_range) - var/datum/ai_controller/my_controller = living_mob.ai_controller + var/datum/ai_controller/basic_controller/bot/my_controller = living_mob.ai_controller if(isnull(my_controller)) return FALSE if(!ishuman(the_target) || LAZYACCESS(my_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], the_target)) @@ -36,7 +38,7 @@ return FALSE if(get_turf(living_mob) == get_turf(living_target)) return ..() - var/list/path = get_path_to(living_mob, living_target, max_distance = 10, access = my_controller.get_access()) + var/list/path = get_path_to(living_mob, living_target, mintargetdist = my_controller.minimum_distance, max_distance = 10, access = my_controller.get_access()) if(!length(path) || QDELETED(living_mob)) my_controller?.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, living_target, TRUE) return FALSE @@ -101,7 +103,7 @@ return TRUE if(get_turf(pawn) == get_turf(target)) return TRUE - var/list/path = get_path_to(pawn, target, max_distance = distance, access = get_access()) + var/list/path = get_path_to(pawn, target, simulated_only = !HAS_TRAIT(pawn, TRAIT_SPACEWALK), mintargetdist = minimum_distance, max_distance = distance, access = get_access()) if(!length(path)) return FALSE return TRUE @@ -265,14 +267,15 @@ action_cooldown = 2 SECONDS behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION -/datum/ai_behavior/bot_search/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, looking_for, radius = 5, pathing_distance = 10, bypass_add_blacklist = FALSE) +/datum/ai_behavior/bot_search/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, looking_for, radius = 5, pathing_distance = 10, bypass_add_blacklist = FALSE, turf_search = FALSE) if(!istype(controller)) stack_trace("attempted to give [controller.pawn] the bot search behavior!") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/mob/living/living_pawn = controller.pawn var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - for(var/atom/potential_target as anything in oview(radius, controller.pawn)) + var/list/objects_to_search = turf_search ? spiral_range_turfs(radius, controller.pawn) : oview(radius, controller.pawn) //use range turfs instead of oview when we can for performance + for(var/atom/potential_target as anything in objects_to_search) if(QDELETED(living_pawn)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED if(!is_type_in_typecache(potential_target, looking_for)) @@ -301,3 +304,37 @@ announcement.announce(pick(list_to_pick_from)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +///behavior to interact with atoms +/datum/ai_behavior/bot_interact + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH + ///should we remove the target afterwards? + var/clear_target = TRUE + +/datum/ai_behavior/bot_interact/setup(datum/ai_controller/controller, target_key) + . = ..() + var/turf/target = controller.blackboard[target_key] + if(isnull(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/bot_interact/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + var/mob/living/basic/living_pawn = controller.pawn + var/atom/target = controller.blackboard[target_key] + + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + living_pawn.UnarmedAttack(target, proximity_flag = TRUE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/ai_behavior/bot_interact/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(clear_target) + controller.clear_blackboard_key(target_key) + if(!succeeded && !isnull(target)) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + +/datum/ai_behavior/bot_interact/keep_target + clear_target = FALSE diff --git a/code/modules/mob/living/basic/bots/bot_hud.dm b/code/modules/mob/living/basic/bots/bot_hud.dm index 6cb3f6bcd01..50d40dad01d 100644 --- a/code/modules/mob/living/basic/bots/bot_hud.dm +++ b/code/modules/mob/living/basic/bots/bot_hud.dm @@ -27,7 +27,7 @@ switch(mode) if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons holder.icon_state = "hudcalled" - if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing + if(BOT_CLEANING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing holder.icon_state = "hudworking" if(BOT_PATROL, BOT_START_PATROL) //Patrol mode holder.icon_state = "hudpatrol" diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm new file mode 100644 index 00000000000..89dad9b4f35 --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm @@ -0,0 +1,389 @@ + +/mob/living/basic/bot/repairbot + name = "\improper Repairbot" + desc = "I can fix it!" + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "repairbot_base" + base_icon_state = "repairbot_base" + pass_flags = parent_type::pass_flags | PASSTABLE + density = FALSE + layer = BELOW_MOB_LAYER + anchored = FALSE + health = 100 + can_be_held = TRUE + maxHealth = 100 + path_image_color = "#80dae7" + bot_ui = "RepairBot" + req_one_access = list(ACCESS_ROBOTICS, ACCESS_ENGINEERING) + radio_key = /obj/item/encryptionkey/headset_service + radio_channel = RADIO_CHANNEL_ENGINEERING + bot_type = REPAIR_BOT + additional_access = /datum/id_trim/job/station_engineer + ai_controller = /datum/ai_controller/basic_controller/bot/repairbot + ///our iron stack + var/obj/item/stack/sheet/iron/our_iron + ///our glass stack + var/obj/item/stack/sheet/glass/our_glass + ///our floor stack + var/obj/item/stack/tile/our_tiles + ///our welder + var/obj/item/weldingtool/repairbot/our_welder + ///our crowbar + var/obj/item/crowbar/our_crowbar + ///our screwdriver + var/obj/item/screwdriver/our_screwdriver + ///our iron rods + var/obj/item/stack/rods/our_rods + ///our rcd object we use to deconstruct when emagged + var/obj/item/construction/rcd/repairbot/deconstruction_device + ///possible interactions + var/static/list/possible_stack_interactions = list( + /obj/item/stack/sheet/iron = typecacheof(list(/obj/structure/girder)), + /obj/item/stack/tile = typecacheof(list(/turf/open/space, /turf/open/floor/plating)), + /obj/item/stack/sheet/glass = typecacheof(list(/obj/structure/grille)), + ) + var/static/list/possible_tool_interactions = list( + /obj/item/weldingtool/repairbot = typecacheof(list(/obj/machinery, /obj/structure/window)), + /obj/item/crowbar = typecacheof(list(/turf/open/floor)), + ) + ///our neutral voicelines + var/static/list/neutral_voicelines = list( + REPAIRBOT_VOICED_BRICK = 'sound/voice/repairbot/brick.ogg', + REPAIRBOT_VOICED_ENTROPY = 'sound/voice/repairbot/entropy.ogg', + REPAIRBOT_VOICED_FIX_IT = 'sound/voice/repairbot/fixit.ogg', + REPAIRBOT_VOICED_FIX_TOUCH = 'sound/voice/repairbot/fixtouch.ogg', + REPAIRBOT_VOICED_HOLE = 'sound/voice/repairbot/patchingholes.ogg', + REPAIRBOT_VOICED_PAY = 'sound/voice/repairbot/pay.ogg', + ) + ///our emagged voicelines + var/static/list/emagged_voicelines = list( + REPAIRBOT_VOICED_ENTROPY = 'sound/voice/repairbot/entropy.ogg', + REPAIRBOT_VOICED_STRINGS = 'sound/voice/repairbot/strings.ogg', + REPAIRBOT_VOICED_PASSION = 'sound/voice/repairbot/passionproject.ogg', + ) + ///types we can retrieve from our ui + var/static/list/retrievable_types = list( + /obj/item/stack/sheet/iron, + /obj/item/stack/sheet/glass, + /obj/item/stack/tile, + ) + + ///our flags + var/repairbot_flags = REPAIRBOT_FIX_BREACHES | REPAIRBOT_FIX_GIRDERS | REPAIRBOT_REPLACE_WINDOWS | REPAIRBOT_REPLACE_TILES | REPAIRBOT_BUILD_GIRDERS + ///our color + var/toolbox_color = "#445eb3" + ///toolbox type we drop on death + var/toolbox = /obj/item/storage/toolbox + +/mob/living/basic/bot/repairbot/Initialize(mapload) + . = ..() + ai_controller.set_blackboard_key(BB_REPAIRBOT_EMAGGED_SPEECH, emagged_voicelines) + ai_controller.set_blackboard_key(BB_REPAIRBOT_NORMAL_SPEECH, neutral_voicelines) + var/static/list/abilities = list( + /datum/action/cooldown/mob_cooldown/bot/build_girder = BB_GIRDER_BUILD_ABILITY, + ) + grant_actions_by_list(abilities) + add_traits(list(TRAIT_SPACEWALK, TRAIT_NEGATES_GRAVITY, TRAIT_MOB_MERGE_STACKS, TRAIT_FIREDOOR_OPENER), INNATE_TRAIT) + our_welder = new(src) + our_welder.switched_on(src) + our_crowbar = new(src) + our_screwdriver = new(src) + our_rods = new(src, our_rods::max_amount) + set_color(toolbox_color) + START_PROCESSING(SSobj, src) + +/mob/living/basic/bot/repairbot/proc/set_color(new_color) + add_atom_colour(new_color, FIXED_COLOUR_PRIORITY) + toolbox_color = new_color + +/mob/living/basic/bot/repairbot/attackby(obj/item/potential_stack, mob/living/carbon/human/user, list/modifiers) + if(!istype(potential_stack, /obj/item/stack)) + return ..() + attempt_merge(potential_stack, user) + +/mob/living/basic/bot/repairbot/proc/attempt_merge(obj/item/stack/potential_stack, mob/living/user) + var/static/list/our_contents = list(/obj/item/stack/sheet/iron, /obj/item/stack/sheet/glass, /obj/item/stack/tile, /obj/item/stack/rods) + for(var/obj/item/stack/content as anything in our_contents) + if(!istype(potential_stack, content)) + continue + var/obj/item/stack/our_sheet = locate(content) in src + if(isnull(our_sheet)) + potential_stack.forceMove(src) + return + if(our_sheet.amount >= our_sheet.max_amount) + user?.balloon_alert(user, "full!") + return + if(!our_sheet.can_merge(potential_stack)) + return + var/atom/movable/to_move = potential_stack.split_stack(user, min(our_sheet.max_amount - our_sheet.amount, potential_stack.amount)) + to_move.forceMove(src) + return + +/mob/living/basic/bot/repairbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if(istype(arrived, /obj/item/stack/sheet/iron) && isnull(our_iron)) //show iron tiles and glass in our hands + our_iron = arrived + update_appearance() + if(istype(arrived, /obj/item/stack/sheet/glass) && isnull(our_glass)) + our_glass = arrived + update_appearance() + if(istype(arrived, /obj/item/stack/tile) && isnull(our_tiles)) + our_tiles = arrived + if(istype(arrived, /obj/item/stack/rods) && isnull(our_rods)) + our_rods = arrived + +/mob/living/basic/bot/repairbot/UnarmedAttack(atom/target, proximity_flag, list/modifiers) + . = ..() + + if(!. || !proximity_flag) + return + + if(bot_access_flags & BOT_COVER_EMAGGED) + emagged_interactions(target, modifiers) + return + + if(istype(target, /obj/item/stack)) + attempt_merge(target, src) + return + + //priority interactions + if(istype(target, /turf/open/space)) + var/turf/open/space/space_target = target + if(!space_target.has_valid_support() && !(locate(/obj/structure/lattice) in space_target)) + our_rods?.melee_attack_chain(src, space_target) + + if(istype(target, /obj/structure/grille)) + var/obj/structure/grille/grille_target = target + if(grille_target.broken) + our_rods?.melee_attack_chain(src, grille_target) + + if(istype(target, /turf/open)) + var/turf/open/open_target = target + if(open_target.broken || open_target.burnt) + our_welder?.melee_attack_chain(src, open_target) + + if(istype(target, /obj/structure/window)) + var/obj/structure/window/target_window = target + if(!target_window.anchored) + our_screwdriver?.melee_attack_chain(src, target_window) + + //stack interactions + for(var/type in possible_stack_interactions) + var/obj/item/target_stack = locate(type) in src + if(isnull(target_stack)) + continue + if(!is_type_in_typecache(target, possible_stack_interactions[type])) + continue + target_stack.melee_attack_chain(src, target) + return + + //tool interactions + var/list/our_tools = list(our_welder, our_crowbar) + for(var/obj/item/tool in our_tools) + if(is_type_in_typecache(target, possible_tool_interactions[tool.type]) && !combat_mode) + tool.melee_attack_chain(src, target) + return + +/mob/living/basic/bot/repairbot/proc/emagged_interactions(atom/target, modifiers) + if(!istype(target, /mob/living/silicon/robot)) + deconstruction_device.interact_with_atom_secondary(target, src, modifiers) + return + if(HAS_TRAIT(target, TRAIT_MOB_TIPPED)) + return + var/old_combat_mode = combat_mode + set_combat_mode(TRUE) + target.attack_hand_secondary(src, modifiers) //tip the guy! + set_combat_mode(old_combat_mode) + +/mob/living/basic/bot/repairbot/start_pulling(atom/movable/movable_pulled, state, force, supress_message) + . = ..() + if(pulling) + setGrabState(GRAB_AGGRESSIVE) //automatically aggro grab everything! + +/mob/living/basic/bot/repairbot/Destroy() + . = ..() + QDEL_NULL(our_iron) + QDEL_NULL(our_glass) + QDEL_NULL(our_tiles) + QDEL_NULL(our_welder) + QDEL_NULL(our_screwdriver) + QDEL_NULL(our_crowbar) + QDEL_NULL(our_rods) + QDEL_NULL(deconstruction_device) + +/mob/living/basic/bot/repairbot/Exited(atom/movable/gone, direction) + if(gone == our_crowbar) + our_crowbar = null + if(gone == our_screwdriver) + our_screwdriver = null + if(gone == our_welder) + our_welder = null + if(gone == our_tiles) + our_tiles = null + if(gone == our_iron) + our_iron = null + if(gone == our_glass) + our_glass = null + if(gone == our_rods) + our_rods = null + update_appearance() + return ..() + +/mob/living/basic/bot/repairbot/process(seconds_per_tick) //generate 1 iron rod every 2 seconds + if(isnull(our_rods) || our_rods.amount < our_rods.max_amount) + new /obj/item/stack/rods(src) + +/mob/living/basic/bot/repairbot/turn_on() + . = ..() + if(!.) + return + START_PROCESSING(SSobj, src) + +/mob/living/basic/bot/repairbot/turn_off() + . = ..() + STOP_PROCESSING(SSobj, src) + +/mob/living/basic/bot/repairbot/update_overlays() + . = ..() + . += mutable_appearance(icon, "repairbot[bot_mode_flags & BOT_MODE_ON]", appearance_flags = RESET_COLOR) + if(our_glass) + var/mutable_appearance/glass = mutable_appearance(icon, "repairbot_glass_overlay", BELOW_MOB_LAYER - 0.02, appearance_flags = RESET_COLOR) + glass.pixel_x = -6 + glass.pixel_y = -5 + . += glass + if(our_iron) + var/mutable_appearance/iron = mutable_appearance(icon, "repairbot_iron_overlay", BELOW_MOB_LAYER - 0.02, appearance_flags = RESET_COLOR) + iron.pixel_y = -5 + iron.pixel_x = 7 + . += iron + +/mob/living/basic/bot/repairbot/update_icon_state() + . = ..() + icon_state = base_icon_state + +/mob/living/basic/bot/repairbot/generate_speak_list() + return neutral_voicelines + emagged_voicelines + +/mob/living/basic/bot/repairbot/Bumped(atom/movable/bumped_object) + . = ..() + if(istype(bumped_object, /obj/machinery/door/firedoor) && bumped_object.density) + our_crowbar.melee_attack_chain(src, bumped_object) + +/mob/living/basic/bot/repairbot/ui_data(mob/user) + var/list/data = ..() + data["repairbot_materials"] = list() + if((bot_access_flags & BOT_COVER_LOCKED) && !issilicon(user) && !isAdminGhostAI(user)) + return data + data["custom_controls"]["fix_breaches"] = repairbot_flags & REPAIRBOT_FIX_BREACHES + data["custom_controls"]["replace_windows"] = repairbot_flags & REPAIRBOT_REPLACE_WINDOWS + data["custom_controls"]["replace_tiles"] = repairbot_flags & REPAIRBOT_REPLACE_TILES + data["custom_controls"]["fix_girders"] = repairbot_flags & REPAIRBOT_FIX_GIRDERS + data["custom_controls"]["build_girders"] = repairbot_flags & REPAIRBOT_BUILD_GIRDERS + + for(var/data_path in retrievable_types) + var/atom/to_retrieve = locate(data_path) in src + if(isnull(to_retrieve)) + continue + + data["repairbot_materials"] += list(list( + "material_ref" = REF(to_retrieve), + "material_icon" = to_retrieve::icon, + "material_icon_state" = to_retrieve::icon_state, + )) + + return data + +/mob/living/basic/bot/repairbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(. || !isliving(ui.user) || (bot_access_flags & BOT_COVER_LOCKED) && !(HAS_SILICON_ACCESS(ui.user))) + return + switch(action) + if("fix_breaches") + repairbot_flags ^= REPAIRBOT_FIX_BREACHES + if("replace_windows") + repairbot_flags ^= REPAIRBOT_REPLACE_WINDOWS + if("replace_tiles") + repairbot_flags ^= REPAIRBOT_REPLACE_TILES + if("fix_girders") + repairbot_flags ^= REPAIRBOT_FIX_GIRDERS + if("build_girders") + repairbot_flags ^= REPAIRBOT_BUILD_GIRDERS + if("remove_item") + var/item_params = params["item_reference"] + if(isnull(item_params)) + return TRUE + var/obj/item/retrieved = locate(item_params) in contents + if(isnull(retrieved) || !is_type_in_list(retrieved, retrievable_types)) + return TRUE + var/mob/living/user = ui.user + user.put_in_hands(retrieved) + return TRUE + + +/mob/living/basic/bot/repairbot/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + if(!(bot_access_flags & BOT_COVER_EMAGGED) || !isnull(deconstruction_device)) + return + deconstruction_device = new(src) + +/obj/item/weldingtool/repairbot + max_fuel = INFINITY + starting_fuel = TRUE + change_icons = FALSE + +/obj/item/construction/rcd/repairbot + matter = INFINITY + has_ammobar = FALSE + +/mob/living/basic/bot/repairbot/mob_pickup(mob/living/user) + var/obj/item/carried_repairbot/carried = new(get_turf(src)) + carried.set_bot(src) + carried.add_atom_colour(toolbox_color, FIXED_COLOUR_PRIORITY) + user.visible_message(span_warning("[user] scoops up [src]!")) + user.put_in_hands(carried) + +/obj/item/carried_repairbot + desc = "A most robust bot!" + attack_verb_continuous = list("robusts") + attack_verb_simple = list("robust") + hitsound = 'sound/items/weapons/smash.ogg' + drop_sound = 'sound/items/handling/toolbox/toolbox_drop.ogg' + pickup_sound = 'sound/items/handling/toolbox/toolbox_pickup.ogg' + ///the bot we own + var/atom/movable/our_bot + +/obj/item/carried_repairbot/proc/set_bot(mob/living/basic/bot/repairbot/repairbot) + var/obj/item/bot_toolbox = repairbot.toolbox + icon = bot_toolbox::icon + icon_state = bot_toolbox::icon_state + lefthand_file = bot_toolbox::lefthand_file + righthand_file = bot_toolbox::righthand_file + inhand_icon_state = bot_toolbox::inhand_icon_state + force = bot_toolbox::force + repairbot.forceMove(src) + +/obj/item/carried_repairbot/dropped() + . = ..() + if(isturf(loc)) + release_bot() + +/obj/item/carried_repairbot/proc/release_bot(bypass_delete = FALSE) + if(!isnull(our_bot)) + our_bot.forceMove(drop_location()) + our_bot.balloon_alert_to_viewers("plops down") + if(!bypass_delete) + qdel(src) + +/obj/item/carried_repairbot/Destroy() + . = ..() + release_bot(bypass_delete = TRUE) + +/obj/item/carried_repairbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if(isliving(arrived)) + our_bot = arrived + +/obj/item/carried_repairbot/Exited(atom/movable/gone, direction) + if(gone == our_bot) + our_bot = null + return ..() diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm new file mode 100644 index 00000000000..fa8d4970634 --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm @@ -0,0 +1,43 @@ +#define BUILDING_WALL_ABILITY "building wall ability" + +/datum/action/cooldown/mob_cooldown/bot/build_girder + name = "Build Girder" + desc = "Use iron rods to build a girder!" + cooldown_time = 3 SECONDS + click_to_activate = TRUE + +/datum/action/cooldown/mob_cooldown/bot/build_girder/IsAvailable(feedback) + . = ..() + if(!.) + return FALSE + var/obj/item/stack/rods/our_rods = locate() in owner + if(isnull(our_rods) || our_rods.amount < 2) + return FALSE + return TRUE + +/datum/action/cooldown/mob_cooldown/bot/build_girder/Activate(atom/target) + if(DOING_INTERACTION(owner, BUILDING_WALL_ABILITY)) + return TRUE + if(!isopenturf(target) || isgroundlessturf(target)) + owner.balloon_alert(owner, "cant build here!") + return TRUE + var/obj/item/stack/rods/our_rods = locate() in owner + var/turf/turf_target = target + if(turf_target.is_blocked_turf()) + owner.balloon_alert(owner, "blocked!") + return TRUE + var/obj/effect/constructing_effect/effect = new(turf_target, 3 SECONDS) + + if(!do_after(owner, 3 SECONDS, target = turf_target, interaction_key = BUILDING_WALL_ABILITY) || isnull(turf_target) || turf_target.is_blocked_turf()) + qdel(effect) + return TRUE + + playsound(turf_target, 'sound/machines/click.ogg', 50, TRUE) + new /obj/structure/girder(turf_target) + var/atom/stack_to_delete = our_rods.split_stack(owner, 2) + qdel(stack_to_delete) + StartCooldown() + qdel(effect) + return TRUE + +#undef BUILDING_WALL_ABILITY diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm new file mode 100644 index 00000000000..1793f0b65c2 --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm @@ -0,0 +1,320 @@ +#define REPAIRBOT_SPEECH_TIMER 30 SECONDS + +/datum/ai_controller/basic_controller/bot/repairbot + planning_subtrees = list( + /datum/ai_planning_subtree/repairbot_speech, + /datum/ai_planning_subtree/manage_unreachable_list, + /datum/ai_planning_subtree/mug_robot, + /datum/ai_planning_subtree/refill_materials, + /datum/ai_planning_subtree/repairbot_deconstruction, + /datum/ai_planning_subtree/respond_to_summon, + /datum/ai_planning_subtree/replace_floors/breaches, + /datum/ai_planning_subtree/wall_girder, + /datum/ai_planning_subtree/build_girder, + /datum/ai_planning_subtree/replace_window, + /datum/ai_planning_subtree/replace_floors, + /datum/ai_planning_subtree/fix_window, + /datum/ai_planning_subtree/salute_authority, + /datum/ai_planning_subtree/find_patrol_beacon, + ) + reset_keys = list( + BB_TILELESS_FLOOR, + BB_GIRDER_TARGET, + BB_GIRDER_TO_WALL_TARGET, + BB_BEACON_TARGET, + BB_PREVIOUS_BEACON_TARGET, + BB_WELDER_TARGET, + BB_WINDOW_FRAMETARGET, + ) + ai_traits = PAUSE_DURING_DO_AFTER + minimum_distance = 1 + +///subtree to refill our stacks +/datum/ai_planning_subtree/refill_materials + +/datum/ai_planning_subtree/refill_materials/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/static/list/refillable_items = typecacheof(list( + /obj/item/stack/sheet/iron, + /obj/item/stack/sheet/glass, + /obj/item/stack/tile, + )) + if(!controller.blackboard_key_exists(BB_REFILLABLE_TARGET)) + controller.queue_behavior(/datum/ai_behavior/bot_search/refillable_target, BB_REFILLABLE_TARGET, refillable_items) + return + controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_REFILLABLE_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/bot_search/refillable_target + action_cooldown = 10 SECONDS + +/datum/ai_behavior/bot_search/refillable_target/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + var/static/list/desired_types = list( + /obj/item/stack/sheet/iron, + /obj/item/stack/sheet/glass, + /obj/item/stack/tile, + ) + for(var/object_type in desired_types) + if(!istype(my_target, object_type)) + continue + var/obj/item/stack/sheet_type = locate(object_type) in controller.pawn + if(isnull(sheet_type)) + return TRUE //we dont have any of it! + if(sheet_type.amount < sheet_type.max_amount && sheet_type.can_merge(my_target)) + return TRUE + return FALSE + +/datum/ai_planning_subtree/mug_robot + +/datum/ai_planning_subtree/mug_robot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/living_bot = controller.pawn + if(!(living_bot.bot_access_flags & BOT_COVER_EMAGGED)) + return + var/static/list/robot_targets = typecacheof( + /mob/living/silicon/robot, + ) + if(!controller.blackboard_key_exists(BB_ROBOT_TARGET)) + controller.queue_behavior(/datum/ai_behavior/bot_search/valid_robot, BB_ROBOT_TARGET, robot_targets) + return + if(!living_bot.pulling) + controller.queue_behavior(/datum/ai_behavior/drag_target, BB_ROBOT_TARGET) + else + controller.queue_behavior(/datum/ai_behavior/bot_interact/tip_robot, BB_ROBOT_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/bot_search/valid_robot + +/datum/ai_behavior/bot_search/valid_robot/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + return (!HAS_TRAIT(my_target, TRAIT_MOB_TIPPED)) && can_see(controller.pawn, my_target) + +/datum/ai_behavior/bot_interact/tip_robot + +/datum/ai_behavior/bot_interact/tip_robot/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + if(succeeded) + var/mob/living/living_pawn = controller.pawn + living_pawn.stop_pulling() + +///subtree to deconstruct things when we're emagged +/datum/ai_planning_subtree/repairbot_deconstruction + +/datum/ai_planning_subtree/repairbot_deconstruction/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/living_bot = controller.pawn + if(!(living_bot.bot_access_flags & BOT_COVER_EMAGGED)) + return + var/static/list/things_to_deconstruct = typecacheof(list( + /obj/structure/window, + /turf/open/floor, + /turf/closed/wall, + )) + if(!controller.blackboard_key_exists(BB_DECONSTRUCT_TARGET)) + controller.queue_behavior(/datum/ai_behavior/bot_search/deconstructable, BB_DECONSTRUCT_TARGET, things_to_deconstruct) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_DECONSTRUCT_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/bot_search/deconstructable + +/datum/ai_behavior/bot_search/deconstructable/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + return (!(my_target.resistance_flags & INDESTRUCTIBLE) && !isgroundlessturf(my_target)) + +///subtree to control bot speech +/datum/ai_planning_subtree/repairbot_speech + +/datum/ai_planning_subtree/repairbot_speech/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + if(controller.blackboard[BB_REPAIRBOT_SPEECH_COOLDOWN] > world.time) + return + var/static/list/keys_to_look = list( + BB_WELDER_TARGET, + BB_WINDOW_FRAMETARGET, + BB_TILELESS_FLOOR, + BB_BREACHED_FLOOR, + BB_GIRDER_TO_WALL_TARGET, + BB_GIRDER_TARGET, + BB_DECONSTRUCT_TARGET, + ) + for(var/key in keys_to_look) + if(controller.blackboard_key_exists(key)) + controller.queue_behavior(/datum/ai_behavior/repairbot_speech, key) + return + +/datum/ai_behavior/repairbot_speech + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/repairbot_speech/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + var/list/speech_to_pick_from = (target_key == BB_DECONSTRUCT_TARGET) ? controller.blackboard[BB_REPAIRBOT_EMAGGED_SPEECH] : controller.blackboard[BB_REPAIRBOT_NORMAL_SPEECH] + if(!length(speech_to_pick_from)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + announcement.announce(pick(speech_to_pick_from)) + controller.set_blackboard_key(BB_REPAIRBOT_SPEECH_COOLDOWN, world.time + REPAIRBOT_SPEECH_TIMER) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +///subtree to replace iron platings +/datum/ai_planning_subtree/replace_floors + ///flag we check before executing + var/required_flag = REPAIRBOT_REPLACE_TILES + ///key of our floor target + var/floor_key = BB_TILELESS_FLOOR + ///type of tile we need to replace floors + var/needed_tile_type = /obj/item/stack/tile + ///type of floors we can replace + var/list/type_of_turf = list(/turf/open/floor/plating) + ///our searching behavior + var/search_behavior = /datum/ai_behavior/bot_search/valid_plateless_turf + +/datum/ai_planning_subtree/replace_floors/New() + . = ..() + type_of_turf = typecacheof(type_of_turf) + +/datum/ai_planning_subtree/replace_floors/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & required_flag)) + return + if(!locate(needed_tile_type) in bot_pawn) + return + if(controller.blackboard_key_exists(floor_key)) + controller.queue_behavior(/datum/ai_behavior/bot_interact, floor_key) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.queue_behavior(search_behavior, floor_key, type_of_turf, 5, 10, FALSE, TRUE) + +/datum/ai_behavior/bot_search/valid_plateless_turf + +/datum/ai_behavior/bot_search/valid_plateless_turf/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/open/my_target) + var/static/list/blacklist_objects = typecacheof(list( + /obj/structure/window, + /obj/structure/grille, + )) + for(var/atom/possible_blacklisted as anything in my_target) + if(is_type_in_typecache(possible_blacklisted, blacklist_objects)) + return FALSE + if(istype(my_target, /turf/open/floor/plating) && !can_see(controller.pawn, my_target, 5)) + return FALSE + return !istype(get_area(my_target), /area/space) + +///subtree to fix hull breaches +/datum/ai_planning_subtree/replace_floors/breaches + floor_key = BB_BREACHED_FLOOR + needed_tile_type = /obj/item/stack/tile/iron + type_of_turf = list(/turf/open/space) + required_flag = REPAIRBOT_FIX_BREACHES + search_behavior = /datum/ai_behavior/bot_search/valid_plateless_turf/breached + +///exists as to not conflict with the base turf searching behavior cause of how the queue system works... +/datum/ai_behavior/bot_search/valid_plateless_turf/breached + +///subtree to build girders +/datum/ai_planning_subtree/build_girder + +/datum/ai_planning_subtree/build_girder/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & REPAIRBOT_BUILD_GIRDERS)) + return + var/obj/item/stack/rods/my_rods = locate() in bot_pawn + if(isnull(my_rods) || my_rods.amount < 2) + return + var/datum/action/cooldown/ability = controller.blackboard[BB_GIRDER_BUILD_ABILITY] + if(!ability?.IsAvailable()) + return + if(controller.blackboard_key_exists(BB_GIRDER_TARGET)) + controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/build_girder, BB_GIRDER_BUILD_ABILITY, BB_GIRDER_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + var/static/list/searchable_turfs = typecacheof(list(/turf/open)) + controller.queue_behavior(/datum/ai_behavior/bot_search/valid_wall_target, BB_GIRDER_TARGET, searchable_turfs, 5, 10, FALSE, TRUE) + +/datum/ai_behavior/bot_search/valid_wall_target + action_cooldown = 5 SECONDS + +/datum/ai_behavior/bot_search/valid_wall_target/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/my_target) + if(istype(get_area(my_target), /area/space) || isgroundlessturf(my_target) || my_target.is_blocked_turf()) + return FALSE + var/static/list/blacklist_objects = list( + /obj/machinery/door, + /obj/structure/grille, + ) + + for(var/atom/contents in my_target) + if(is_type_in_typecache(contents, blacklist_objects)) + return FALSE + + var/turf/adjacent_turfs = get_adjacent_open_turfs(my_target) + for(var/turf/possible_spaced_turf as anything in adjacent_turfs) + if(isspaceturf(possible_spaced_turf) && istype(get_area(possible_spaced_turf), /area/space)) + return TRUE + return FALSE + +/datum/ai_behavior/targeted_mob_ability/build_girder + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/targeted_mob_ability/build_girder/setup(datum/ai_controller/controller, ability_key, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/targeted_mob_ability/build_girder/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + controller.clear_blackboard_key(target_key) + if(!succeeded && !isnull(target)) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + + +///subtree to place glass on windows +/datum/ai_planning_subtree/replace_window + +/datum/ai_planning_subtree/replace_window/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/basic/bot/repairbot/living_pawn = controller.pawn + if(!(living_pawn.repairbot_flags & REPAIRBOT_REPLACE_WINDOWS)) + return + if(!locate(/obj/item/stack/sheet/glass) in living_pawn) + return + if(controller.blackboard_key_exists(BB_WINDOW_FRAMETARGET)) + controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_WINDOW_FRAMETARGET) + return SUBTREE_RETURN_FINISH_PLANNING + var/static/list/searchable_grilles = typecacheof(list(/obj/structure/grille)) + controller.queue_behavior(/datum/ai_behavior/bot_search/valid_grille_target, BB_WINDOW_FRAMETARGET, searchable_grilles) + +/datum/ai_behavior/bot_search/valid_grille_target/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/structure/my_target) + if(locate(/obj/structure/window) in get_turf(my_target)) + return FALSE + return (!istype(get_area(my_target), /area/space)) + + +///subtree to place iron on girders +/datum/ai_planning_subtree/wall_girder + +/datum/ai_planning_subtree/wall_girder/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/basic/bot/repairbot/living_pawn = controller.pawn + if(!(living_pawn.repairbot_flags & REPAIRBOT_FIX_GIRDERS)) + return + var/obj/item/stack/sheet/iron/my_iron = locate() in living_pawn + if(isnull(my_iron) || my_iron.amount < 2) + return + if(controller.blackboard_key_exists(BB_GIRDER_TO_WALL_TARGET)) + controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_GIRDER_TO_WALL_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + var/static/list/searchable_girder = typecacheof(list(/obj/structure/girder)) + controller.queue_behavior(/datum/ai_behavior/bot_search/valid_girder, BB_GIRDER_TO_WALL_TARGET, searchable_girder) + +/datum/ai_behavior/bot_search/valid_girder/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) + return isfloorturf(my_target.loc) + +///subtree to repair machines with welders +/datum/ai_planning_subtree/fix_window + +/datum/ai_planning_subtree/fix_window/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(controller.blackboard_key_exists(BB_WELDER_TARGET)) + controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_WELDER_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + var/static/list/searchable_objects = typecacheof(list(/obj/structure/window)) + controller.queue_behavior(/datum/ai_behavior/bot_search/valid_window_fix, BB_WELDER_TARGET, searchable_objects) + +/datum/ai_behavior/bot_search/valid_window_fix + +/datum/ai_behavior/bot_search/valid_window_fix/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) + return (my_target.get_integrity() < my_target.max_integrity || !my_target.anchored) + +#undef REPAIRBOT_SPEECH_TIMER diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm index fe5dbb4d2d5..7412551d598 100644 --- a/code/modules/mob/living/basic/drone/_drone.dm +++ b/code/modules/mob/living/basic/drone/_drone.dm @@ -348,6 +348,9 @@ to_chat(src, span_warning("Using [machine] could break your laws.")) return COMPONENT_CANT_INTERACT_WIRES +/mob/living/basic/drone/proc/init_shy_in_room_component(list/drone_bad_areas) + if(CONFIG_GET(flag/drone_area_interaction_restrict)) + LoadComponent(/datum/component/shy_in_room, drone_bad_areas, "Touching anything in %ROOM could break your laws.") /mob/living/basic/drone/proc/set_shy(new_shy) shy = new_shy @@ -366,8 +369,8 @@ REMOVE_TRAIT(src, TRAIT_CAN_STRIP, DRONE_SHY_TRAIT) // To shy to touch someone elses hat ADD_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT) LoadComponent(/datum/component/shy, mob_whitelist=not_shy_of, shy_range=3, message="Your laws prevent this action near %TARGET.", keyless_shy=FALSE, clientless_shy=TRUE, dead_shy=FALSE, dead_shy_immediate=TRUE, machine_whitelist=shy_machine_whitelist) - LoadComponent(/datum/component/shy_in_room, drone_bad_areas, "Touching anything in %ROOM could break your laws.") - LoadComponent(/datum/component/technoshy, 1 MINUTES, "%TARGET was touched by a being recently, using it could break your laws.") + init_shy_in_room_component(drone_bad_areas) + LoadComponent(/datum/component/technoshy, 20 SECONDS, "%TARGET was touched by a being recently, using it could break your laws.") LoadComponent(/datum/component/itempicky, drone_good_items, "Using %TARGET could break your laws.") RegisterSignal(src, COMSIG_TRY_USE_MACHINE, PROC_REF(blacklist_on_try_use_machine)) RegisterSignal(src, COMSIG_TRY_WIRES_INTERACT, PROC_REF(blacklist_on_try_wires_interact)) diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm index 77fa9ce8ca0..1081c9b7b63 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm @@ -8,6 +8,7 @@ callback.Invoke() /datum/ai_behavior/find_hunt_target/pollinate + action_cooldown = 10 SECONDS /datum/ai_behavior/find_hunt_target/pollinate/valid_dinner(mob/living/source, obj/machinery/hydroponics/dinner, radius) if(!dinner.can_bee_pollinate()) @@ -16,6 +17,7 @@ /datum/ai_behavior/enter_exit_hive behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH + action_cooldown = 10 SECONDS /datum/ai_behavior/enter_exit_hive/setup(datum/ai_controller/controller, target_key, attack_key) . = ..() diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm index 3d56dd990dc..76abb281b99 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm @@ -71,7 +71,7 @@ return var/mob/living/bee_pawn = controller.pawn - var/action_prob = (bee_pawn in current_home.contents) ? exit_chance : flyback_chance + var/action_prob = (bee_pawn.loc == current_home) ? exit_chance : flyback_chance if(!SPT_PROB(action_prob, seconds_per_tick)) return @@ -91,3 +91,9 @@ hunt_targets = list(/obj/machinery/hydroponics) hunt_range = 10 hunt_chance = 85 + +/datum/ai_planning_subtree/find_and_hunt_target/pollinate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/atom/atom_pawn = controller.pawn + if(!isturf(atom_pawn.loc)) + return + return ..() diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm index 363243a2833..7e572c2d92f 100644 --- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm @@ -177,6 +177,19 @@ paralyze_chance = 0 initial_size = 0.9 +/mob/living/basic/gorilla/hostile + name = "Feral Gorilla" + maxHealth = 180 + health = 180 + desc = "A gorilla created via \"advanced genetic science\". While not quite as strong as their wildborne brethren, this simian still packs a punch." + melee_damage_lower = 15 + melee_damage_upper = 18 + obj_damage = 25 + speed = 0.1 + paralyze_chance = 0 + initial_size = 0.9 + faction = list(FACTION_HOSTILE) + /mob/living/basic/gorilla/genetics/Initialize(mapload) . = ..() qdel(GetComponent(/datum/component/amputating_limbs)) diff --git a/code/modules/mob/living/basic/heretic/flesh_worm.dm b/code/modules/mob/living/basic/heretic/flesh_worm.dm index cddd34ba441..13372c72688 100644 --- a/code/modules/mob/living/basic/heretic/flesh_worm.dm +++ b/code/modules/mob/living/basic/heretic/flesh_worm.dm @@ -56,7 +56,7 @@ /mob/living/basic/heretic_summon/armsy/has_gravity(turf/gravity_turf) return TRUE -/mob/living/basic/heretic_summon/armsy/can_be_pulled() +/mob/living/basic/heretic_summon/armsy/can_be_pulled(user, force) return FALSE // The component does this but not on the head. We don't want the head to be pulled either. /mob/living/basic/heretic_summon/armsy/proc/build_tail(worm_length) diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm index 440cfc2861b..ee93a9c1236 100644 --- a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm +++ b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm @@ -37,6 +37,7 @@ hunt_range = 7 /datum/ai_behavior/find_and_set/treatable_hydro + action_cooldown = 5 SECONDS /datum/ai_behavior/find_and_set/treatable_hydro/search_tactic(datum/ai_controller/controller, locate_path, search_range) var/list/possible_trays = list() @@ -97,6 +98,9 @@ return FALSE set_movement_target(controller, target) +/datum/ai_behavior/find_and_set/beamable_hydroplants + action_cooldown = 15 SECONDS + /datum/ai_behavior/find_and_set/beamable_hydroplants/search_tactic(datum/ai_controller/controller, locate_path, search_range) var/list/possible_trays = list() @@ -136,7 +140,8 @@ return can_see(source, water_source, radius) /datum/ai_behavior/hunt_target/interact_with_target/water_source - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH + always_reset_target = TRUE hunt_cooldown = 5 SECONDS /datum/ai_controller/basic_controller/seedling/meanie diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm index 303776384d1..4e9e0459587 100644 --- a/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm +++ b/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm @@ -17,7 +17,7 @@ var/mob/living/living_target = target if(FACTION_JUNGLE in living_target.faction) - return + return BULLET_ACT_BLOCK return ..() diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm index 7e3022f9571..a826fb4a436 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm @@ -2,11 +2,13 @@ always_reset_target = TRUE /datum/ai_behavior/find_hunt_target/injured_raptor + action_cooldown = 7.5 SECONDS /datum/ai_behavior/find_hunt_target/injured_raptor/valid_dinner(mob/living/source, mob/living/target, radius) return (source != target && target.health < target.maxHealth) /datum/ai_behavior/find_hunt_target/raptor_victim + action_cooldown = 30 SECONDS /datum/ai_behavior/find_hunt_target/raptor_victim/valid_dinner(mob/living/source, mob/living/target, radius) if(target.ai_controller?.blackboard[BB_RAPTOR_TROUBLE_MAKER]) @@ -30,6 +32,7 @@ return ..() /datum/ai_behavior/find_hunt_target/raptor_trough + action_cooldown = 7.5 SECONDS /datum/ai_behavior/find_hunt_target/raptor_trough/valid_dinner(mob/living/source, atom/movable/trough, radius) return !!(locate(/obj/item/stack/ore) in trough.contents) diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher.dm index c7648aa38b2..5c4183b48ee 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher.dm @@ -13,6 +13,9 @@ speed = 3 maxHealth = 160 health = 160 + melee_damage_lower = 15 + melee_damage_upper = 15 + attack_sound = 'sound/items/weapons/bladeslice.ogg' attack_verb_continuous = "buffets" attack_verb_simple = "buffet" crusher_loot = /obj/item/crusher_trophy/watcher_wing diff --git a/code/modules/mob/living/basic/pets/cat/kitten_ai.dm b/code/modules/mob/living/basic/pets/cat/kitten_ai.dm index 4136aa0aa01..cdb7ac542e1 100644 --- a/code/modules/mob/living/basic/pets/cat/kitten_ai.dm +++ b/code/modules/mob/living/basic/pets/cat/kitten_ai.dm @@ -61,8 +61,7 @@ for(var/mob/living/carbon/human/human_target in oview(search_range, controller.pawn)) if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) continue - if(!length(typecache_filter_list(human_target.held_items, locate_items))) - continue - return human_target - + for (var/obj/item/held_item in human_target.held_items) + if (is_type_in_typecache(held_item, locate_items)) + return human_target return null diff --git a/code/modules/mob/living/basic/pets/orbie/orbie.dm b/code/modules/mob/living/basic/pets/orbie/orbie.dm index 85d82e92515..b4099a8c634 100644 --- a/code/modules/mob/living/basic/pets/orbie/orbie.dm +++ b/code/modules/mob/living/basic/pets/orbie/orbie.dm @@ -77,7 +77,7 @@ happy_state = !happy_state update_appearance() -/mob/living/basic/orbie/can_be_pulled(user, grab_state, force) +/mob/living/basic/orbie/can_be_pulled(user, force) return FALSE /mob/living/basic/orbie/proc/on_level_up(datum/source, new_level) diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm index c5ff2fb90e7..550484a75c5 100644 --- a/code/modules/mob/living/basic/ruin_defender/flesh.dm +++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm @@ -1,4 +1,10 @@ +/// Chance per second to print a warning text +#define LIVING_FLESH_WARN_CHANCE 3 +/// Chance per second to perform an unwanted interaction +#define LIVING_FLESH_INTERFERENCE_CHANCE 1.5 +/// Chance to caress instead of grab something nearby without combat mode #define LIVING_FLESH_TOUCH_CHANCE 30 +/// Chance to punch instead of grab something nearby in combat mode #define LIVING_FLESH_COMBAT_TOUCH_CHANCE 70 /datum/ai_controller/basic_controller/living_limb_flesh @@ -59,47 +65,47 @@ if(isnull(current_bodypart) || isnull(current_bodypart.owner)) return var/mob/living/carbon/human/victim = current_bodypart.owner - if(SPT_PROB(3, SSMOBS_DT)) - to_chat(victim, span_warning("The thing posing as your limb makes you feel funny...")) //warn em - //firstly as a sideeffect we drain nutrition from our host + if(SPT_PROB(LIVING_FLESH_WARN_CHANCE, SSMOBS_DT)) + to_chat(victim, span_warning("The skin on your [current_bodypart.plaintext_zone] crawls.")) + victim.adjust_nutrition(-1.5) - if(!SPT_PROB(1.5, SSMOBS_DT)) + if(!SPT_PROB(LIVING_FLESH_INTERFERENCE_CHANCE, SSMOBS_DT)) return - if(istype(current_bodypart, /obj/item/bodypart/arm)) - var/list/candidates = list() - for(var/atom/movable/movable in orange(victim, 1)) - if(movable == victim) - continue - if(!victim.CanReach(movable) || victim.invisibility) - continue - candidates += movable - if(!length(candidates)) - return - var/atom/movable/candidate = pick(candidates) - if(isnull(candidate)) - return - - victim.visible_message(span_warning("[victim]'s [current_bodypart.name] instinctively starts feeling [candidate]!")) - if (!victim.anchored && !prob(victim.combat_mode ? LIVING_FLESH_COMBAT_TOUCH_CHANCE : LIVING_FLESH_TOUCH_CHANCE)) - INVOKE_ASYNC(victim, TYPE_PROC_REF(/atom/movable, start_pulling), candidate, supress_message = TRUE) + if(istype(current_bodypart, /obj/item/bodypart/leg)) + if(HAS_TRAIT(victim, TRAIT_IMMOBILIZED)) return + step(victim, pick(GLOB.cardinals)) + to_chat(victim, span_warning("Your [current_bodypart.plaintext_zone] moves on its own!")) + return - var/active_hand = victim.active_hand_index - var/new_index = (current_bodypart.body_zone == BODY_ZONE_L_ARM) ? LEFT_HANDS : RIGHT_HANDS - if (active_hand != new_index) - victim.swap_hand(new_index, TRUE) - victim.resolve_unarmed_attack(candidate) - if (active_hand != victim.active_hand_index) // Different check in case we failed to swap hands previously due to holding a bulky item - victim.swap_hand(active_hand, TRUE) + var/list/candidates = list() + for(var/atom/movable/movable in orange(victim, 1)) + if(movable == victim) + continue + if(!victim.CanReach(movable) || movable.invisibility > victim.see_invisible) + continue + candidates += movable + if(!length(candidates)) + return + var/atom/movable/candidate = pick(candidates) + if(isnull(candidate)) return - if(HAS_TRAIT(victim, TRAIT_IMMOBILIZED)) + if (!prob(victim.combat_mode ? LIVING_FLESH_COMBAT_TOUCH_CHANCE : LIVING_FLESH_TOUCH_CHANCE) && candidate.can_be_pulled(user = victim, force = victim.pull_force)) + victim.visible_message(span_warning("[victim]'s [current_bodypart.plaintext_zone] suddenly fastens around [candidate]!")) + INVOKE_ASYNC(victim, TYPE_PROC_REF(/atom/movable, start_pulling), candidate, supress_message = TRUE) return - step(victim, pick(GLOB.cardinals)) - to_chat(victim, span_warning("Your [current_bodypart] moves on its own!")) + victim.visible_message(span_warning("[victim]'s [current_bodypart.plaintext_zone] suddenly spasms towards [candidate]!")) + var/active_hand = victim.active_hand_index + var/new_index = (current_bodypart.body_zone == BODY_ZONE_L_ARM) ? LEFT_HANDS : RIGHT_HANDS + if (active_hand != new_index) + victim.swap_hand(new_index, TRUE) + victim.resolve_unarmed_attack(candidate) + if (active_hand != victim.active_hand_index) // Different check in case we failed to swap hands previously due to holding a bulky item + victim.swap_hand(active_hand, TRUE) /mob/living/basic/living_limb_flesh/melee_attack(mob/living/carbon/human/target, list/modifiers, ignore_cooldown) . = ..() @@ -157,7 +163,7 @@ if(!detach_self()) return var/turf/our_location = get_turf(src) - our_location.visible_message(span_warning("[part_owner][part_owner.p_s()] [current_bodypart] begins to convulse wildly!")) + our_location.visible_message(span_warning("[part_owner][part_owner.p_s()] [current_bodypart.plaintext_zone] begins to convulse wildly!")) /mob/living/basic/living_limb_flesh/proc/owner_died(datum/source, gibbed) SIGNAL_HANDLER @@ -200,3 +206,5 @@ #undef LIVING_FLESH_TOUCH_CHANCE #undef LIVING_FLESH_COMBAT_TOUCH_CHANCE +#undef LIVING_FLESH_WARN_CHANCE +#undef LIVING_FLESH_INTERFERENCE_CHANCE diff --git a/code/modules/mob/living/basic/slime/ai/behaviours.dm b/code/modules/mob/living/basic/slime/ai/behaviours.dm index fe8102eee11..934404d88dd 100644 --- a/code/modules/mob/living/basic/slime/ai/behaviours.dm +++ b/code/modules/mob/living/basic/slime/ai/behaviours.dm @@ -25,6 +25,7 @@ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED /datum/ai_behavior/find_hunt_target/find_slime_food + action_cooldown = 7.5 SECONDS // Check if the slime can drain the target /datum/ai_behavior/find_hunt_target/find_slime_food/valid_dinner(mob/living/basic/slime/hunter, mob/living/dinner, radius, datum/ai_controller/controller, seconds_per_tick) diff --git a/code/modules/mob/living/basic/slime/ai/controller.dm b/code/modules/mob/living/basic/slime/ai/controller.dm index 41466b29734..1d5f00e6c43 100644 --- a/code/modules/mob/living/basic/slime/ai/controller.dm +++ b/code/modules/mob/living/basic/slime/ai/controller.dm @@ -4,7 +4,6 @@ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_SLIME_RABID = FALSE, BB_SLIME_HUNGER_DISABLED = FALSE, - BB_CURRENT_HUNTING_TARGET = null, // people whose energy we want to drain ) ai_movement = /datum/ai_movement/basic_avoidance @@ -13,13 +12,12 @@ /datum/ai_planning_subtree/change_slime_face, /datum/ai_planning_subtree/use_mob_ability/evolve, /datum/ai_planning_subtree/use_mob_ability/reproduce, - /datum/ai_planning_subtree/target_retaliate, /datum/ai_planning_subtree/pet_planning, + /datum/ai_planning_subtree/target_retaliate, /datum/ai_planning_subtree/find_and_hunt_target/find_slime_food, /datum/ai_planning_subtree/basic_melee_attack_subtree/slime, /datum/ai_planning_subtree/random_speech/slime, ) - can_idle = FALSE /datum/ai_controller/basic_controller/slime/CancelActions() ..() diff --git a/code/modules/mob/living/basic/slime/ai/pet_command.dm b/code/modules/mob/living/basic/slime/ai/pet_command.dm index 33484e360fb..211d7aa552c 100644 --- a/code/modules/mob/living/basic/slime/ai/pet_command.dm +++ b/code/modules/mob/living/basic/slime/ai/pet_command.dm @@ -10,7 +10,7 @@ var/mob/living/basic/slime/slime_pawn = controller.pawn if(isslime(slime_pawn) && slime_pawn.can_feed_on(controller.blackboard[BB_CURRENT_PET_TARGET], check_friendship = TRUE)) - controller.queue_behavior(hunting_behavior, BB_CURRENT_PET_TARGET, BB_HUNTING_COOLDOWN) + controller.queue_behavior(hunting_behavior, BB_CURRENT_PET_TARGET) return SUBTREE_RETURN_FINISH_PLANNING return ..() diff --git a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm index fdc92d06f66..2af3a42c26d 100644 --- a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm +++ b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm @@ -39,7 +39,7 @@ var/datum/action/cooldown/spell/jaunt/bloodcrawl/crawl = new(heart_owner) crawl.Grant(heart_owner) -/obj/item/organ/heart/demon/on_mob_remove(mob/living/carbon/heart_owner, special = FALSE) +/obj/item/organ/heart/demon/on_mob_remove(mob/living/carbon/heart_owner, special = FALSE, movement_flags) . = ..() var/datum/action/cooldown/spell/jaunt/bloodcrawl/crawl = locate() in heart_owner.actions qdel(crawl) diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm index c162ecf2c21..366cb1c5065 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm @@ -111,7 +111,7 @@ return FALSE var/datum/beam/draining_beam = Beam(target, icon_state = "drain_life") - if(!do_after(src, 4.6 SECONDS, target, timed_action_flags = (IGNORE_HELD_ITEM | IGNORE_INCAPACITATED))) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining. + if(!do_after(src, 4.6 SECONDS, target, timed_action_flags = (IGNORE_HELD_ITEM | IGNORE_INCAPACITATED))) //As one cannot prove the existence of ghosts, ghosts cannot prove the existence of the target they were draining. to_chat(src, span_revenwarning("[target ? "[target]'s soul has" : "[target_They_have]"] been drawn out of your grasp. The link has been broken.")) if(target) target.visible_message( diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 53f7b714d43..044796305a5 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -50,16 +50,7 @@ // Brain size logic transform = transform.Scale(brain_size) -/obj/item/organ/brain/examine() - . = ..() - if (smooth_brain) - . += span_notice("All the pesky wrinkles are gone. Now it just needs a good drying...") - if(brain_size < 1) - . += span_notice("It is a bit on the smaller side...") - if(brain_size > 1) - . += span_notice("It is bigger than average...") - -/obj/item/organ/brain/mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags) +/obj/item/organ/brain/on_mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags) . = ..() if(!.) return @@ -115,7 +106,7 @@ if(!special && !(brain_owner.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS)) brain_owner.update_body_parts() -/obj/item/organ/brain/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/brain/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) // Delete skillchips first as parent proc sets owner to null, and skillchips need to know the brain's owner. if(!QDELETED(organ_owner) && length(skillchips)) if(!special) @@ -132,7 +123,7 @@ BT.on_lose(TRUE) BT.owner = null - if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !(movement_flags & NO_ID_TRANSFER)) + if((!QDELETED(src) || !QDELETED(owner)) && !(movement_flags & NO_ID_TRANSFER)) transfer_identity(organ_owner) if(!special) if(!(organ_owner.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS)) @@ -140,7 +131,7 @@ organ_owner.clear_mood_event("brain_damage") /obj/item/organ/brain/update_icon_state() - icon_state = "[initial(icon_state)][smooth_brain ? "-smooth_brain" : ""]" + icon_state = "[initial(icon_state)][smooth_brain ? "-smooth" : ""]" return ..() /obj/item/organ/brain/proc/transfer_identity(mob/living/L) @@ -241,6 +232,8 @@ if(length(skillchips)) . += span_info("It has a skillchip embedded in it.") . += brain_damage_examine() + if (smooth_brain) + . += span_notice("All the pesky wrinkles are gone. Now it just needs a good drying...") if(brain_size < 1) . += span_notice("It is a bit on the smaller side...") if(brain_size > 1) @@ -460,12 +453,20 @@ can_smoothen_out = FALSE organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP) -/obj/item/organ/brain/lustrous/on_mob_remove(mob/living/carbon/organ_owner, special) +// This fixes an edge case from species/regenerate_organs that would transfer the brain trauma before organ/on_mob_remove can remove it +// Prevents wizards from using the magic mirror to gain bluespace_prophet trauma and then switching to another race +/obj/item/organ/brain/lustrous/before_organ_replacement(obj/item/organ/replacement) + if(owner) + owner.cure_trauma_type(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE) + owner.RemoveElement(/datum/element/tenacious) + . = ..() + +/obj/item/organ/brain/lustrous/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() organ_owner.cure_trauma_type(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE) organ_owner.RemoveElement(/datum/element/tenacious) -/obj/item/organ/brain/lustrous/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/brain/lustrous/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() organ_owner.gain_trauma(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE) organ_owner.AddElement(/datum/element/tenacious) @@ -629,7 +630,7 @@ /obj/item/organ/brain/proc/get_attacking_limb(mob/living/carbon/human/target) var/obj/item/bodypart/arm/active_hand = owner.get_active_hand() if(target.body_position == LYING_DOWN && owner.usable_legs) - var/obj/item/bodypart/found_bodypart = owner.get_bodypart((active_hand.held_index % 2) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG) + var/obj/item/bodypart/found_bodypart = owner.get_bodypart(IS_LEFT_INDEX(active_hand.held_index) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG) return found_bodypart || active_hand return active_hand diff --git a/code/modules/mob/living/carbon/alien/adult/adult.dm b/code/modules/mob/living/carbon/alien/adult/adult.dm index d2c41e61a12..45e9dea454d 100644 --- a/code/modules/mob/living/carbon/alien/adult/adult.dm +++ b/code/modules/mob/living/carbon/alien/adult/adult.dm @@ -56,14 +56,16 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list( ..() //For alien evolution/promotion/queen finder procs. Checks for an active alien of that type -/proc/get_alien_type(alienpath) - for(var/mob/living/carbon/alien/adult/A in GLOB.alive_mob_list) - if(!istype(A, alienpath)) +/proc/get_alien_type(alien_path, mob/ignored) + for(var/mob/living/carbon/alien/alien in GLOB.carbon_list) + if(alien == ignored) continue - if(!A.key || A.stat == DEAD) //Only living aliens with a ckey are valid. + if(!istype(alien, alien_path)) continue - return A - return FALSE + if(!alien.key || alien.stat == DEAD) //Only living aliens with a ckey are valid. + continue + return alien + return null /mob/living/carbon/alien/adult/check_breath(datum/gas_mixture/breath) if(breath?.total_moles() > 0 && !HAS_TRAIT(src, TRAIT_SNEAK)) diff --git a/code/modules/mob/living/carbon/alien/adult/queen.dm b/code/modules/mob/living/carbon/alien/adult/queen.dm index 8cbd5571977..cfe6f45320a 100644 --- a/code/modules/mob/living/carbon/alien/adult/queen.dm +++ b/code/modules/mob/living/carbon/alien/adult/queen.dm @@ -44,18 +44,6 @@ alien_speed = 2 /mob/living/carbon/alien/adult/royal/queen/Initialize(mapload) - //there should only be one queen - for(var/mob/living/carbon/alien/adult/royal/queen/Q in GLOB.carbon_list) - if(Q == src) - continue - if(Q.stat == DEAD) - continue - if(Q.client) - name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. - break - - real_name = src.name - var/static/list/innate_actions = list( /datum/action/cooldown/alien/promote, /datum/action/cooldown/spell/aoe/repulse/xeno, @@ -72,6 +60,11 @@ organs += new /obj/item/organ/alien/eggsac return ..() +/mob/living/carbon/alien/adult/royal/queen/set_name() + if(get_alien_type(/mob/living/carbon/alien/adult/royal/queen, ignored = src)) + name = "alien princess" + return ..() + //Queen verbs /datum/action/cooldown/alien/make_structure/lay_egg name = "Lay Egg" diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 120633acdfe..12471109867 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -22,7 +22,7 @@ gib_type = /obj/effect/decal/cleanable/xenoblood/xgibs unique_name = TRUE - var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|queen)( \\(\\d+\\))?") + var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|princess|queen)( \\(\\d+\\))?") var/static/list/xeno_allowed_items = typecacheof(list( /obj/item/clothing/mask/facehugger, /obj/item/toy/basketball, // playing ball against a xeno is rigged since they cannot be disarmed, their game is out of this world diff --git a/code/modules/mob/living/carbon/alien/death.dm b/code/modules/mob/living/carbon/alien/death.dm index f5a0b7ace1b..8671a66c98a 100644 --- a/code/modules/mob/living/carbon/alien/death.dm +++ b/code/modules/mob/living/carbon/alien/death.dm @@ -7,8 +7,10 @@ /mob/living/carbon/alien/gib_animation() new /obj/effect/temp_visual/gib_animation(loc, "gibbed-a") -/mob/living/carbon/alien/spawn_dust() - new /obj/effect/decal/remains/xeno(loc) +/mob/living/carbon/alien/spawn_dust(just_ash) + if(just_ash) + return ..() -/mob/living/carbon/alien/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-a") + var/obj/effect/decal/remains/xeno/bones = new(loc) + bones.pixel_z = -6 + bones.pixel_w = rand(-1, 1) diff --git a/code/modules/mob/living/carbon/alien/larva/death.dm b/code/modules/mob/living/carbon/alien/larva/death.dm index 4b7f9f93218..3c4500518de 100644 --- a/code/modules/mob/living/carbon/alien/larva/death.dm +++ b/code/modules/mob/living/carbon/alien/larva/death.dm @@ -15,8 +15,10 @@ /mob/living/carbon/alien/larva/gib_animation() new /obj/effect/temp_visual/gib_animation(loc, "gibbed-l") -/mob/living/carbon/alien/larva/spawn_dust() - new /obj/effect/decal/remains/xeno(loc) +/mob/living/carbon/alien/larva/spawn_dust(just_ash) + if(just_ash) + return ..() -/mob/living/carbon/alien/larva/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-l") + var/obj/effect/decal/remains/xeno/bones = new(loc) + bones.pixel_z = -6 + bones.pixel_w = rand(-1, 1) diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 7db1bb103cc..46bd7bb04f9 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -103,7 +103,7 @@ . = ..() organ_owner.faction |= ROLE_ALIEN -/obj/item/organ/alien/hivenode/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE) +/obj/item/organ/alien/hivenode/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags) if(organ_owner) organ_owner.faction -= ROLE_ALIEN return ..() @@ -221,11 +221,11 @@ stomach_contents -= source UnregisterSignal(source, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_DEATH, COMSIG_QDELETING)) -/obj/item/organ/stomach/alien/mob_insert(mob/living/carbon/stomach_owner, special, movement_flags) +/obj/item/organ/stomach/alien/on_mob_insert(mob/living/carbon/stomach_owner, special, movement_flags) RegisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE, PROC_REF(something_moved)) return ..() -/obj/item/organ/stomach/alien/mob_remove(mob/living/carbon/stomach_owner, special, movement_flags) +/obj/item/organ/stomach/alien/on_mob_remove(mob/living/carbon/stomach_owner, special, movement_flags) UnregisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE) return ..() diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index b70139cee05..be65a9a1d3d 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -78,7 +78,7 @@ for(var/datum/surgery/operations as anything in owner.surgeries) if(operations.location != BODY_ZONE_CHEST) continue - if(!istype(operations.get_surgery_step(), /datum/surgery_step/manipulate_organs/internal)) + if(!ispath(operations.steps[operations.status], /datum/surgery_step/manipulate_organs/internal)) continue attempt_grow(gib_on_success = FALSE) return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index e43cddd81bf..a13939f4f70 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -572,7 +572,11 @@ set_health(round(maxHealth - getOxyLoss() - getToxLoss() - total_burn - total_brute, DAMAGE_PRECISION)) update_stat() update_stamina() - if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD*2) && stat == DEAD ) + + /// The amount of burn damage needed to be done for this mob to be husked + var/husk_threshold = get_bodypart(BODY_ZONE_CHEST).max_damage * -1 + + if(((maxHealth - total_burn) < husk_threshold) && stat == DEAD ) become_husk(BURN) med_hud_set_health() if(stat == SOFT_CRIT) @@ -745,7 +749,7 @@ //Fire and Brute damage overlay (BSSR) var/hurtdamage = getBruteLoss() + getFireLoss() + damageoverlaytemp - if(hurtdamage) + if(hurtdamage && !HAS_TRAIT(src, TRAIT_ANALGESIA)) var/severity = 0 switch(hurtdamage) if(5 to 15) diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index aebba0ae2a9..2eb701c6146 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -272,7 +272,7 @@ break var/icon_file = I.lefthand_file - if(get_held_index_of_item(I) % 2 == 0) + if(IS_RIGHT_INDEX(get_held_index_of_item(I))) icon_file = I.righthand_file hands += I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index f7d988db910..16458bf11c7 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -142,8 +142,6 @@ GLOBAL_LIST_EMPTY(features_by_species) ///What gas does this species breathe? Used by suffocation screen alerts, most of actual gas breathing is handled by mutantlungs. See [life.dm][code/modules/mob/living/carbon/human/life.dm] var/breathid = GAS_O2 - ///What anim to use for dusting - var/dust_anim = "dust-h" ///What anim to use for gibbing var/gib_anim = "gibbed-h" @@ -181,9 +179,9 @@ GLOBAL_LIST_EMPTY(features_by_species) var/list/outfit_override_registry = list() // unique gibspawner for species - var/obj/effect/gibspawner/gibspawner = null + var/obj/effect/gibspawner/gibspawner_type // unique remains for species - var/obj/effect/decal/remains/decalremains = null + var/obj/effect/decal/remains/remains_type // Can we write numbers in name var/allow_numbers_in_name = FALSE diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 9c8ecc93a93..83cc270adee 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -2,28 +2,30 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) /mob/living/carbon/human/gib_animation() new /obj/effect/temp_visual/gib_animation(loc, dna.species.gib_anim) -/mob/living/carbon/human/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, dna.species.dust_anim) - /mob/living/carbon/human/spawn_gibs(drop_bitflags=NONE) if(flags_1 & HOLOGRAM_1) return if(drop_bitflags & DROP_BODYPARTS) - if(dna.species.gibspawner) - new dna.species.gibspawner(drop_location(), src, get_static_viruses()) + if(!isnull(dna.species.gibspawner_type)) + new dna.species.gibspawner_type(drop_location(), src, get_static_viruses()) else new /obj/effect/gibspawner/human(drop_location(), src, get_static_viruses()) else new /obj/effect/gibspawner/human/bodypartless(drop_location(), src, get_static_viruses()) -/mob/living/carbon/human/spawn_dust(just_ash = FALSE) - if(dna.species.decalremains) - var/obj/effect/decal/remains/specialdecalremains = dna.species.decalremains - new specialdecalremains(loc) - else if(just_ash) - new /obj/effect/decal/cleanable/ash(loc) - else - new /obj/effect/decal/remains/human(loc) +/mob/living/carbon/human/spawn_dust(just_ash) + if(just_ash) + return ..() + + var/bone_type = /obj/effect/decal/remains/human + if(isplasmaman(src)) + bone_type = /obj/effect/decal/remains/plasma + else if(!isnull(dna.species.remains_type)) + bone_type = dna.species.remains_type + + var/obj/effect/decal/remains/human/bones = new bone_type(loc) + bones.pixel_z = -6 + bones.pixel_w = rand(-1, 1) /mob/living/carbon/human/death(gibbed) if(stat == DEAD) diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index cac9ff90326..7582b7f896e 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -121,6 +121,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) target.dna.features["pod_hair"] = get_consistent_feature_entry(SSaccessories.pod_hair_list) target.dna.features["arachnid_appendages"] = get_consistent_feature_entry(SSaccessories.arachnid_appendages_list) target.dna.features["ipc_chassis"] = get_consistent_feature_entry(SSaccessories.ipc_chassis_list) + target.dna.features["caps"] = get_consistent_feature_entry(SSaccessories.caps_list) target.dna.initialize_dna(create_mutation_blocks = FALSE, randomize_features = FALSE) // UF and UI are nondeterministic, even though the features are the same some blocks will randomize slightly // In practice this doesn't matter, but this is for the sake of 100%(ish) consistency diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 98ba9e86392..3a138f4113d 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1029,6 +1029,52 @@ if(mind.assigned_role.title in SSjob.name_occupations) .[mind.assigned_role.title] = minutes +/mob/living/carbon/human/proc/add_eye_color_left(color, color_priority, update_body = TRUE) + LAZYSET(eye_color_left_overrides, "[color_priority]", color) + if (update_body) + update_body() + +/mob/living/carbon/human/proc/add_eye_color_right(color, color_priority, update_body = TRUE) + LAZYSET(eye_color_right_overrides, "[color_priority]", color) + if (update_body) + update_body() + +/mob/living/carbon/human/proc/add_eye_color(color, color_priority, update_body = TRUE) + add_eye_color_left(color, color_priority, update_body = FALSE) + add_eye_color_right(color, color_priority, update_body = update_body) + +/mob/living/carbon/human/proc/remove_eye_color(color_priority, update_body = TRUE) + LAZYREMOVE(eye_color_left_overrides, "[color_priority]") + LAZYREMOVE(eye_color_right_overrides, "[color_priority]") + if (update_body) + update_body() + +/mob/living/carbon/human/proc/get_right_eye_color() + if (!LAZYLEN(eye_color_right_overrides)) + return eye_color_right + + var/eye_color = eye_color_right + var/priority + for (var/override_priority in eye_color_right_overrides) + var/new_priority = text2num(override_priority) + if (new_priority > priority) + priority = new_priority + eye_color = eye_color_right_overrides[override_priority] + return eye_color + +/mob/living/carbon/human/proc/get_left_eye_color() + if (!LAZYLEN(eye_color_left_overrides)) + return eye_color_left + + var/eye_color = eye_color_left + var/priority + for (var/override_priority in eye_color_left_overrides) + var/new_priority = text2num(override_priority) + if (new_priority > priority) + priority = new_priority + eye_color = eye_color_left_overrides[override_priority] + return eye_color + /mob/living/carbon/human/monkeybrain ai_controller = /datum/ai_controller/monkey @@ -1152,7 +1198,7 @@ race = /datum/species/snail /mob/living/carbon/human/species/vampire - race = /datum/species/vampire + race = /datum/species/human/vampire /mob/living/carbon/human/species/zombie race = /datum/species/zombie diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 13700dd5aad..8df7c758e2b 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -378,9 +378,12 @@ else if(wear_suit.siemens_coefficient <= 0) siemens_coeff -= 0.95 siemens_coeff = max(siemens_coeff, 0) - else if(!(flags & SHOCK_NOGLOVES)) //This gets the siemens_coeff for all non tesla shocks - if(gloves) - siemens_coeff *= gloves.siemens_coefficient + if(flags & SHOCK_NOGLOVES) //This gets the siemens_coeff for all non tesla shocks + if(wear_suit) + siemens_coeff *= wear_suit.siemens_coefficient + else if(gloves) + siemens_coeff *= gloves.siemens_coefficient + siemens_coeff *= physiology.siemens_coeff siemens_coeff *= dna.species.siemens_coeff . = ..() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index b14d2e194be..db1a45e4c06 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -35,11 +35,14 @@ var/facial_hair_color = COLOR_BLACK var/facial_hairstyle = "Shaved" - //Eye colour + // Base "natural" eye color var/eye_color_left = COLOR_BLACK var/eye_color_right = COLOR_BLACK /// Var used to keep track of a human mob having a heterochromatic right eye. To ensure prefs don't overwrite shit var/eye_color_heterochromatic = FALSE + // Eye color overrides assoc lists - priority key to hex color + var/list/eye_color_left_overrides + var/list/eye_color_right_overrides var/skin_tone = "caucasian1" //Skin tone diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index d7f1acd955e..daebdba069b 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -130,7 +130,7 @@ /mob/living/carbon/human/proc/check_chunky_fingers() if(HAS_TRAIT_NOT_FROM(src, TRAIT_CHUNKYFINGERS, RIGHT_ARM_TRAIT) && HAS_TRAIT_NOT_FROM(src, TRAIT_CHUNKYFINGERS, LEFT_ARM_TRAIT)) return TRUE - return (active_hand_index % 2) ? HAS_TRAIT_FROM(src, TRAIT_CHUNKYFINGERS, LEFT_ARM_TRAIT) : HAS_TRAIT_FROM(src, TRAIT_CHUNKYFINGERS, RIGHT_ARM_TRAIT) + return IS_LEFT_INDEX(active_hand_index) ? HAS_TRAIT_FROM(src, TRAIT_CHUNKYFINGERS, LEFT_ARM_TRAIT) : HAS_TRAIT_FROM(src, TRAIT_CHUNKYFINGERS, RIGHT_ARM_TRAIT) /mob/living/carbon/human/get_policy_keywords() . = ..() diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index 2ad994432c0..73ce6808339 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -550,7 +550,7 @@ There are several things that need to be remembered: t_state = worn_item.icon_state var/mutable_appearance/hand_overlay - var/icon_file = held_index % 2 == 0 ? worn_item.righthand_file : worn_item.lefthand_file + var/icon_file = IS_RIGHT_INDEX(held_index) ? worn_item.righthand_file : worn_item.lefthand_file hand_overlay = worn_item.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) var/obj/item/bodypart/arm/held_in_hand = hand_bodyparts[held_index] held_in_hand?.held_hand_offset?.apply_offset(hand_overlay) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 02f627459cb..219060012fc 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -424,7 +424,7 @@ hand_bodyparts.len = amt for(var/i in old_limbs+1 to amt) var/path = /obj/item/bodypart/arm/left - if(!(i % 2)) + if(IS_RIGHT_INDEX(i)) path = /obj/item/bodypart/arm/right var/obj/item/bodypart/BP = new path () diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm index 05c54871037..fa3091d09a1 100644 --- a/code/modules/mob/living/carbon/human/species_types/ipc.dm +++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -47,7 +47,7 @@ BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/ipc, BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/ipc, ) - gibspawner = /obj/effect/gibspawner/robot/android + gibspawner_type = /obj/effect/gibspawner/robot/android allow_numbers_in_name = TRUE /datum/species/ipc/on_species_gain(mob/living/carbon/human/ipc, datum/species/old_species, pref_load) 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 bf22c032b05..9379fdd6167 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -212,10 +212,6 @@ Lizard subspecies: SILVER SCALED examine_limb_id = SPECIES_LIZARD ///stored mutcolor for when we turn back off of a silverscale. var/old_mutcolor - ///stored eye color for when we turn back off of a silverscale. - var/old_eye_color_left - ///See above - var/old_eye_color_right /datum/species/lizard/silverscale/get_physical_attributes() return "Silver Scales are to lizardpeople what angels are to humans. \ @@ -224,17 +220,13 @@ Lizard subspecies: SILVER SCALED /datum/species/lizard/silverscale/on_species_gain(mob/living/carbon/human/new_silverscale, datum/species/old_species, pref_load) old_mutcolor = new_silverscale.dna.features["mcolor"] - old_eye_color_left = new_silverscale.eye_color_left - old_eye_color_right = new_silverscale.eye_color_right new_silverscale.dna.features["mcolor"] = "#eeeeee" - new_silverscale.eye_color_left = "#0000a0" - new_silverscale.eye_color_right = "#0000a0" + new_silverscale.add_eye_color("#0000a0", EYE_COLOR_SPECIES_PRIORITY) . = ..() new_silverscale.add_filter("silver_glint", 2, list("type" = "outline", "color" = "#ffffff63", "size" = 2)) /datum/species/lizard/silverscale/on_species_loss(mob/living/carbon/human/was_silverscale, datum/species/new_species, pref_load) was_silverscale.dna.features["mcolor"] = old_mutcolor - was_silverscale.eye_color_left = old_eye_color_left - was_silverscale.eye_color_right = old_eye_color_right + was_silverscale.remove_eye_color(EYE_COLOR_SPECIES_PRIORITY) was_silverscale.remove_filter("silver_glint") return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm index a1f98250e74..057dffbf222 100644 --- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm +++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm @@ -35,7 +35,6 @@ BODY_ZONE_CHEST = /obj/item/bodypart/chest/monkey, ) fire_overlay = "monkey" - dust_anim = "dust-m" gib_anim = "gibbed-m" payday_modifier = 1.5 diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index d0b052a8888..44b5d959038 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -4,7 +4,7 @@ ///maximum a vampire will drain, they will drain less if they hit their cap #define VAMP_DRAIN_AMOUNT 50 -/datum/species/vampire +/datum/species/human/vampire name = "Vampire" id = SPECIES_VAMPIRE examine_limb_id = SPECIES_HUMAN @@ -18,33 +18,30 @@ ) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID changesource_flags = MIRROR_BADMIN | MIRROR_PRIDE | WABBAJACK | ERT_SPAWN - exotic_bloodtype = "U" + exotic_bloodtype = "V" blood_deficiency_drain_rate = BLOOD_DEFICIENCY_MODIFIER // vampires already passively lose blood, so this just makes them lose it slightly more quickly when they have blood deficiency. mutantheart = /obj/item/organ/heart/vampire mutanttongue = /obj/item/organ/tongue/vampire - mutantstomach = null - mutantlungs = null - skinned_type = /obj/item/stack/sheet/animalhide/human ///some starter text sent to the vampire initially, because vampires have shit to do to stay alive var/info_text = "You are a Vampire. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability." -/datum/species/vampire/check_roundstart_eligible() +/datum/species/human/vampire/check_roundstart_eligible() if(check_holidays(HALLOWEEN)) return TRUE return ..() -/datum/species/vampire/on_species_gain(mob/living/carbon/human/new_vampire, datum/species/old_species) +/datum/species/human/vampire/on_species_gain(mob/living/carbon/human/new_vampire, datum/species/old_species) . = ..() to_chat(new_vampire, "[info_text]") new_vampire.skin_tone = "albino" new_vampire.update_body(0) RegisterSignal(new_vampire, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(damage_weakness)) -/datum/species/vampire/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) +/datum/species/human/vampire/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) . = ..() UnregisterSignal(C, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS) -/datum/species/vampire/spec_life(mob/living/carbon/human/vampire, seconds_per_tick, times_fired) +/datum/species/human/vampire/spec_life(mob/living/carbon/human/vampire, seconds_per_tick, times_fired) . = ..() if(istype(vampire.loc, /obj/structure/closet/crate/coffin)) var/need_mob_update = FALSE @@ -66,27 +63,27 @@ vampire.adjust_fire_stacks(3 * seconds_per_tick) vampire.ignite_mob() -/datum/species/vampire/proc/damage_weakness(datum/source, list/damage_mods, damage_amount, damagetype, def_zone, sharpness, attack_direction, obj/item/attacking_item) +/datum/species/human/vampire/proc/damage_weakness(datum/source, list/damage_mods, damage_amount, damagetype, def_zone, sharpness, attack_direction, obj/item/attacking_item) SIGNAL_HANDLER if(istype(attacking_item, /obj/item/nullrod/whip)) damage_mods += 2 -/datum/species/vampire/get_physical_attributes() +/datum/species/human/vampire/get_physical_attributes() return "Vampires are afflicted with the Thirst, needing to sate it by draining the blood out of another living creature. However, they do not need to breathe or eat normally. \ They will instantly turn into dust if they run out of blood or enter a holy area. However, coffins stabilize and heal them, and they can transform into bats!" -/datum/species/vampire/get_species_description() +/datum/species/human/vampire/get_species_description() return "A classy Vampire! They descend upon Space Station Thirteen Every year to spook the crew! \"Bleeg!!\"" -/datum/species/vampire/get_species_lore() +/datum/species/human/vampire/get_species_lore() return list( "Vampires are unholy beings blessed and cursed with The Thirst. \ The Thirst requires them to feast on blood to stay alive, and in return it gives them many bonuses. \ Because of this, Vampires have split into two clans, one that embraces their powers as a blessing and one that rejects it.", ) -/datum/species/vampire/create_pref_unique_perks() +/datum/species/human/vampire/create_pref_unique_perks() var/list/to_add = list() to_add += list( @@ -115,7 +112,7 @@ return to_add // Vampire blood is special, so it needs to be handled with its own entry. -/datum/species/vampire/create_pref_blood_perks() +/datum/species/human/vampire/create_pref_blood_perks() var/list/to_add = list() to_add += list(list( @@ -132,7 +129,7 @@ return to_add // There isn't a "Minor Undead" biotype, so we have to explain it in an override (see: dullahans) -/datum/species/vampire/create_pref_biotypes_perks() +/datum/species/human/vampire/create_pref_biotypes_perks() var/list/to_add = list() to_add += list(list( diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 93d6f9ce2e1..e80ba0c95be 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -124,9 +124,9 @@ //Tries to play the carbon a breathing sound when using internals, also invokes check_breath /mob/living/carbon/proc/try_breathing_sound(breath) var/should_be_on = canon_client?.prefs?.read_preference(/datum/preference/toggle/sound_breathing) - if(should_be_on && !breathing_loop.timer_id) + if(should_be_on && !breathing_loop.timer_id && canon_client?.mob.can_hear()) breathing_loop.start() - else if(!should_be_on && breathing_loop.timer_id) + else if((!should_be_on && breathing_loop.timer_id) || !canon_client?.mob.can_hear()) breathing_loop.stop() /mob/living/carbon/proc/has_smoke_protection() diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index d4005cf51eb..6616736a610 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -69,6 +69,9 @@ /mob/living/proc/spread_bodyparts(drop_bitflags=NONE) return +/// Length of the animation in dust_animation.dmi +#define DUST_ANIMATION_TIME 1.3 SECONDS + /** * This is the proc for turning a mob into ash. * Dusting robots does not eject the MMI, so it's a bit more powerful than gib() @@ -91,15 +94,55 @@ buckled.unbuckle_mob(src, force = TRUE) dust_animation() - spawn_dust(just_ash) + addtimer(CALLBACK(src, PROC_REF(spawn_dust), just_ash), DUST_ANIMATION_TIME - 0.3 SECONDS) ghostize() - QDEL_IN(src,5) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes. + QDEL_IN(src, DUST_ANIMATION_TIME) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes. -/mob/living/proc/dust_animation() - return +/// Animates turning into dust. +/// Does not delete src afterwards, BUT it will become invisible (and grey), so ensure you handle that yourself +/atom/movable/proc/dust_animation(atom/anim_loc = src.loc) + if(isnull(anim_loc)) // the effect breaks if we have a null loc + return + var/obj/effect/temp_visual/dust_animation_filter/dustfx = new(anim_loc, REF(src)) + add_filter("dust_animation", 1, displacement_map_filter(render_source = dustfx.render_target, size = 256)) + add_filter("dust_color", 1, color_matrix_filter()) + transition_filter("dust_color", color_matrix_filter(COLOR_MATRIX_GRAYSCALE), DUST_ANIMATION_TIME - 0.3 SECONDS) + animate(src, alpha = 0, time = DUST_ANIMATION_TIME - 0.1 SECONDS, easing = SINE_EASING | EASE_IN) + +/// Holds the dust animation filter effect, so we can animate it +/obj/effect/temp_visual/dust_animation_filter + icon = 'icons/mob/dust_animation.dmi' + icon_state = "dust.1" + duration = DUST_ANIMATION_TIME + randomdir = FALSE + +/obj/effect/temp_visual/dust_animation_filter/Initialize(mapload, anim_id = "random_default_anti_collision_text") + . = ..() + // we manually animate this, rather than just using an animated icon state or flick, to work around byond animated state memes + // (normally, all animated icon states are synced to the same time, which would bad here) + for(var/i in 2 to duration) + if(PERFORM_ALL_TESTS(focus_only/runtime_icon_states) && !icon_exists(icon, "dust.[i]")) + stack_trace("Missing dust animation icon state: dust.[i]") + animate(src, time = 1, icon_state = "dust.[i]", flags = ANIMATION_CONTINUE) + if(PERFORM_ALL_TESTS(focus_only/runtime_icon_states) && icon_exists(icon, "dust.[duration + 1]")) + stack_trace("Extra dust animation icon state: dust.[duration + 1]") + render_target = "*dust-[anim_id]" + +#undef DUST_ANIMATION_TIME +/** + * Spawns dust / ash or remains where the mob was + * + * just_ash: If TRUE, just ash will spawn where the mob was, as opposed to remains + */ /mob/living/proc/spawn_dust(just_ash = FALSE) - new /obj/effect/decal/cleanable/ash(loc) + var/ash_type = /obj/effect/decal/cleanable/ash + if(mob_size >= MOB_SIZE_LARGE) + ash_type = /obj/effect/decal/cleanable/ash/large + + var/obj/effect/decal/cleanable/ash/ash = new ash_type(loc) + ash.pixel_z = -5 + ash.pixel_w = rand(-1, 1) /* * Called when the mob dies. Can also be called manually to kill a mob. diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 568e49866bd..444c5138244 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -140,22 +140,27 @@ /datum/emote/living/flap/run_emote(mob/user, params, type_override, intentional) . = ..() - if(ishuman(user)) - var/mob/living/carbon/human/human_user = user - var/open = FALSE - var/obj/item/organ/wings/functional/wings = human_user.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS) - - // open/close functional wings - if(istype(wings)) - if(wings.wings_open) - open = TRUE - wings.close_wings() - else - wings.open_wings() - addtimer(CALLBACK(wings, open ? TYPE_PROC_REF(/obj/item/organ/wings/functional, open_wings) : TYPE_PROC_REF(/obj/item/organ/wings/functional, close_wings)), wing_time) + if(!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + var/obj/item/organ/wings/wings = human_user.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS) - // play a flapping noise if the wing has this implemented - wings.make_flap_sound(human_user) + // play a flapping noise if the wing has this implemented + if(!istype(wings)) + return + wings.make_flap_sound(human_user) + + // open/close functional wings + var/obj/item/organ/wings/functional/wings_functional = wings + if(!istype(wings_functional)) + return + var/open = FALSE + if(wings_functional.wings_open) + open = TRUE + wings_functional.close_wings() + else + wings_functional.open_wings() + addtimer(CALLBACK(wings_functional, open ? TYPE_PROC_REF(/obj/item/organ/wings/functional, open_wings) : TYPE_PROC_REF(/obj/item/organ/wings/functional, close_wings)), wing_time) /datum/emote/living/flap/aflap key = "aflap" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 6576b63db9e..f30b2d479d2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -373,7 +373,7 @@ /mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE) if(!AM || !src) return FALSE - if(!(AM.can_be_pulled(src, state, force))) + if(!(AM.can_be_pulled(src, force))) return FALSE if(throwing || !(mobility_flags & MOBILITY_PULL)) return FALSE @@ -388,7 +388,7 @@ if(pulling) // Are we trying to pull something we are already pulling? Then just stop here, no need to continue. if(AM == pulling) - return + return FALSE stop_pulling() changeNext_move(CLICK_CD_GRABBING) @@ -456,6 +456,7 @@ update_pull_movespeed() set_pull_offsets(M, state) + return TRUE /mob/living/proc/set_pull_offsets(mob/living/M, grab_state = GRAB_PASSIVE) if(M.buckled) @@ -533,7 +534,7 @@ if (!CAN_SUCCUMB(src)) if(HAS_TRAIT(src, TRAIT_SUCCUMB_OVERRIDE)) if(whispered) - to_chat(src, span_notice("Your immortal body is keeping you alive. If you want to accept death, you must do so [span_bold("quietly")]."), type=MESSAGE_TYPE_INFO) + to_chat(src, span_notice("Your immortal body is keeping you alive! Unless you just press the UI button."), type=MESSAGE_TYPE_INFO) return else to_chat(src, span_warning("You are unable to succumb to death! This life continues."), type=MESSAGE_TYPE_INFO) @@ -1200,16 +1201,42 @@ /mob/living/resist_grab(moving_resist) . = TRUE - //If we're in an aggressive grab or higher, we're lying down, we're vulnerable to grabs, or we're staggered and we have some amount of stamina loss, we must resist - if(pulledby.grab_state || body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS) || get_timed_status_effect_duration(/datum/status_effect/staggered) && (getFireLoss()*0.5 + getBruteLoss()*0.5) >= 40) - var/altered_grab_state = pulledby.grab_state - if((body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS) || get_timed_status_effect_duration(/datum/status_effect/staggered)) && pulledby.grab_state < GRAB_KILL) //If prone, resisting out of a grab is equivalent to 1 grab state higher. won't make the grab state exceed the normal max, however - altered_grab_state++ - if(HAS_TRAIT(src, TRAIT_GRABRESISTANCE)) - altered_grab_state-- + + //Our effective grab state. GRAB_PASSIVE is equal to 0, so if we have no other altering factors to our grab state, we can break free immediately on resist. + var/effective_grab_state = pulledby.grab_state + //The amount of damage inflicted on a failed resist attempt. + var/damage_on_resist_fail = rand(7, 13) + + if(body_position == LYING_DOWN) //If prone, treat the grab state as one higher + effective_grab_state++ + + if(HAS_TRAIT(src, TRAIT_GRABWEAKNESS)) //If we have grab weakness from some source, treat the grab state as one higher + effective_grab_state++ + + if(get_timed_status_effect_duration(/datum/status_effect/staggered) && (getFireLoss() + getBruteLoss()) >= 40) //If we are staggered, and we have at least 40 damage, treat the grab state as one higher. + effective_grab_state++ + + if(HAS_TRAIT(src, TRAIT_GRABRESISTANCE)) //If we have grab resistance from some source, treat the grab state as one lower. + effective_grab_state-- + + //If our puller is a human, and they have an active hand they're grabbing with (please don't ask how people grab without hands), then apply their unarmed values to the grab values + if(pulledby && ishuman(pulledby)) + var/mob/living/carbon/human/human_puller = pulledby + var/obj/item/bodypart/grabbing_bodypart = human_puller.get_active_hand() + if(grabbing_bodypart) + damage_on_resist_fail += rand(grabbing_bodypart.unarmed_damage_low, grabbing_bodypart.unarmed_damage_high) + + //If our puller is a drunken brawler, they add more damage based on their own damage taken so long as they're drunk and treat the grab state as one higher + var/puller_drunkenness = human_puller.get_drunk_amount() + if(puller_drunkenness && HAS_TRAIT(human_puller, TRAIT_DRUNKEN_BRAWLER)) + damage_on_resist_fail += clamp((human_puller.getFireLoss() + human_puller.getBruteLoss()) / 10, 3, 20) + effective_grab_state ++ + + //We only resist our grab state if we are currently in a grab equal to or greater than GRAB_AGGRESSIVE (1). Otherwise, break out immediately! + if(effective_grab_state >= GRAB_AGGRESSIVE) // see defines/combat.dm, this should be baseline 60% // Resist chance divided by the value imparted by your grab state. It isn't until you reach neckgrab that you gain a penalty to escaping a grab. - var/resist_chance = altered_grab_state ? (BASE_GRAB_RESIST_CHANCE / altered_grab_state) : 100 + var/resist_chance = clamp(BASE_GRAB_RESIST_CHANCE / effective_grab_state, 0, 100) if(prob(resist_chance)) visible_message(span_danger("[src] breaks free of [pulledby]'s grip!"), \ span_danger("You break free of [pulledby]'s grip!"), null, null, pulledby) @@ -1218,7 +1245,7 @@ pulledby.stop_pulling() return FALSE else - adjustStaminaLoss(rand(15,20))//failure to escape still imparts a pretty serious penalty + adjustStaminaLoss(damage_on_resist_fail) //Do some stamina damage if we fail to resist visible_message(span_danger("[src] struggles as they fail to break free of [pulledby]'s grip!"), \ span_warning("You struggle as you fail to break free of [pulledby]'s grip!"), null, null, pulledby) to_chat(pulledby, span_danger("[src] struggles as they fail to break free of your grip!")) @@ -1890,7 +1917,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) "[C] leaps out of [src]'s way!"))) C.Paralyze(40) -/mob/living/can_be_pulled() +/mob/living/can_be_pulled(user, force) return ..() && !(buckled?.buckle_prevents_pull) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index e41a07d52cd..46cfb5980a2 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -62,7 +62,7 @@ var/nuking = FALSE var/obj/machinery/doomsday_device/doomsday_device - var/mob/camera/ai_eye/eyeobj + var/mob/eye/camera/ai/eyeobj var/sprint = 10 var/last_moved = 0 var/acceleration = TRUE @@ -120,7 +120,7 @@ create_eye() - if(target_ai.mind && target_ai.mind.active) + if((target_ai.mind && target_ai.mind.active) || SSticker.current_state == GAME_STATE_SETTING_UP) target_ai.mind.transfer_to(src) if(mind.special_role) to_chat(src, span_userdanger("You have been installed as an AI! ")) @@ -241,7 +241,7 @@ /// Removes all malfunction-related abilities from the AI /mob/living/silicon/ai/proc/remove_malf_abilities() QDEL_NULL(modules_action) - for(var/datum/ai_module/AM in current_modules) + for(var/datum/ai_module/malf/AM in current_modules) for(var/datum/action/A in actions) if(istype(A, initial(AM.power_type))) qdel(A) @@ -1139,8 +1139,8 @@ target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct . = ..() -/mob/living/silicon/ai/proc/camera_visibility(mob/camera/ai_eye/moved_eye) - GLOB.cameranet.visibility(moved_eye, client, all_eyes, TRUE) +/mob/living/silicon/ai/proc/camera_visibility(mob/eye/camera/ai/moved_eye) + GLOB.cameranet.visibility(moved_eye) /mob/living/silicon/ai/forceMove(atom/destination) . = ..() diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index 2c5e809e5d9..1f8c452f492 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -12,7 +12,6 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) var/list/obj/machinery/camera/cameras = list() /// The chunks of the map, mapping the areas that the cameras can see. var/list/chunks = list() - var/ready = 0 /// List of images cloned by all chunk static images put onto turfs cameras cant see /// Indexed by the plane offset to use @@ -56,16 +55,12 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) if(!.) chunks[key] = . = new /datum/camerachunk(x, y, lowest.z) -/// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or its location is set. -/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = TRUE) +/// Updates what the camera eye can see. It is recommended you use this when a camera eye moves or its location is set. +/datum/cameranet/proc/visibility(list/moved_eyes) if(!islist(moved_eyes)) moved_eyes = moved_eyes ? list(moved_eyes) : list() - if(islist(other_eyes)) - other_eyes = (other_eyes - moved_eyes) - else - other_eyes = list() - for(var/mob/camera/ai_eye/eye as anything in moved_eyes) + for(var/mob/eye/camera/eye as anything in moved_eyes) var/list/visibleChunks = list() //Get the eye's turf in case its located in an object like a mecha var/turf/eye_turf = get_turf(eye) @@ -129,6 +124,8 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) * to change the time between static updates. */ /datum/cameranet/proc/majorChunkChange(atom/c, choice, update_delay_buffer) + PROTECTED_PROC(TRUE) + if(QDELETED(c) && choice == 1) CRASH("Tried to add a qdeleting camera to the net") diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm index c21f08e2d6b..73908f162b8 100644 --- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm +++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm @@ -1,9 +1,9 @@ #define UPDATE_BUFFER_TIME (2.5 SECONDS) -// CAMERA CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the AI Eye to stream these chunks and know what it can and cannot see. +/** + * A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. \ + * Allows Camera Eyes to stream these chunks and know what it can and cannot see. + */ /datum/camerachunk ///turfs our cameras cant see but are inside our grid. associative list of the form: list(obscured turf = static image on that turf) @@ -16,7 +16,7 @@ ///list of all turfs, associative with that turf's static image ///turf -> /image var/list/turfs = list() - ///camera mobs that can see turfs in our grid + ///Camera mobs that can see turfs in our grid var/list/seenby = list() ///images currently in use on obscured turfs. var/list/active_static_images = list() @@ -27,24 +27,24 @@ var/lower_z var/upper_z -/// Add an AI eye to the chunk, then update if changed. -/datum/camerachunk/proc/add(mob/camera/ai_eye/eye) +/// Add a camera eye to the chunk, then update if changed. +/datum/camerachunk/proc/add(mob/eye/camera/eye) eye.visibleCameraChunks += src seenby += eye if(changed) update() var/client/client = eye.GetViewerClient() - if(client && eye.use_static) + if(client && eye.use_visibility) client.images += active_static_images -/// Remove an AI eye from the chunk -/datum/camerachunk/proc/remove(mob/camera/ai_eye/eye, remove_static_with_last_chunk = TRUE) +/// Remove a camera eye from the chunk +/datum/camerachunk/proc/remove(mob/eye/camera/ai/eye) eye.visibleCameraChunks -= src seenby -= eye var/client/client = eye.GetViewerClient() - if(client && eye.use_static) + if(client && eye.use_visibility) client.images -= active_static_images /// Called when a chunk has changed. I.E: A wall was deleted. @@ -56,6 +56,7 @@ /** * Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will * instead be flagged to update the next time an AI Eye moves near it. + * * update_delay_buffer is used for cameras that are moving around, which are cyborg inbuilt cameras and * mecha onboard cameras. This buffer should be usually lower than UPDATE_BUFFER_TIME because * otherwise a moving camera can run out of its own view before updating static. @@ -89,8 +90,8 @@ ///turfs that we could see last update but cant see now var/list/newly_obscured_turfs = visibleTurfs - updated_visible_turfs - for(var/mob/camera/ai_eye/client_eye as anything in seenby) - var/client/client = client_eye.ai?.client || client_eye.client + for(var/mob/eye/camera/client_eye as anything in seenby) + var/client/client = client_eye.GetViewerClient() if(!client) continue @@ -119,8 +120,8 @@ changed = FALSE - for(var/mob/camera/ai_eye/client_eye as anything in seenby) - var/client/client = client_eye.ai?.client || client_eye.client + for(var/mob/eye/camera/client_eye as anything in seenby) + var/client/client = client_eye.GetViewerClient() if(!client) continue diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index b049acacccc..a204d5a5448 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -1,43 +1,57 @@ -// AI EYE -// -// An invisible (no icon) mob that the AI controls to look around the station with. -// It streams chunks as it moves around, which will show it what the AI can and cannot see. -/mob/camera/ai_eye +/mob/eye/camera/ai name = "Inactive AI Eye" - icon_state = "ai_camera" - icon = 'icons/mob/silicon/cameramob.dmi' - invisibility = INVISIBILITY_MAXIMUM + hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST) - var/list/visibleCameraChunks = list() + /// The AI who owns this eye. var/mob/living/silicon/ai/ai = null + /// Whether this eye will transmit speech near it to the AI. var/relay_speech = FALSE - var/use_static = TRUE - var/static_visibility_range = 16 + /// Whether this eye can be found with AI detectors. var/ai_detector_visible = TRUE + /// The color of the area if the eye is detectable. var/ai_detector_color = COLOR_RED - interaction_range = INFINITY -/mob/camera/ai_eye/Initialize(mapload) +/mob/eye/camera/ai/Initialize(mapload) . = ..() - GLOB.aiEyes += src update_ai_detect_hud() - setLoc(loc, TRUE) -/mob/camera/ai_eye/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) - . = ..() - if(same_z_layer) - return - update_ai_detect_hud() - -/mob/camera/ai_eye/examine(mob/user) //Displays a silicon's laws to ghosts +/mob/eye/camera/ai/examine(mob/user) //Displays a silicon's laws to ghosts . = ..() if(istype(ai) && ai.laws && isobserver(user)) . += "[ai] aşağıdaki yasalara sahiptir:" for(var/law in ai.laws.get_law_list(include_zeroth = TRUE)) . += law -/mob/camera/ai_eye/proc/update_ai_detect_hud() +/mob/eye/camera/ai/Destroy() + if(ai) + ai.all_eyes -= src + ai = null + if(ai_detector_visible) + var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT] + hud.remove_atom_from_hud(src) + var/list/L = hud_list[AI_DETECT_HUD] + QDEL_LIST(L) + return ..() + +/** + * Returns a list of turfs visible to the client's viewsize. \ + * Note that this will return an empty list if the camera's loc is not a turf. + */ +/mob/eye/camera/ai/proc/get_visible_turfs() + RETURN_TYPE(/list/turf) + SHOULD_BE_PURE(TRUE) + SHOULD_CALL_PARENT(TRUE) + + if(!isturf(loc)) + return list() + var/client/C = GetViewerClient() + var/view = C ? getviewsize(C.view) : getviewsize(world.view) + var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z) + var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z) + return block(lowerleft, upperright) + +/mob/eye/camera/ai/proc/update_ai_detect_hud() var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT] var/list/old_images = hud_list[AI_DETECT_HUD] if(!ai_detector_visible) @@ -75,42 +89,17 @@ active_hud_list[AI_DETECT_HUD] = new_images hud.add_atom_to_hud(src) -/mob/camera/ai_eye/proc/get_visible_turfs() - if(!isturf(loc)) - return list() - var/client/C = GetViewerClient() - var/view = C ? getviewsize(C.view) : getviewsize(world.view) - var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z) - var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z) - return block(lowerleft, upperright) - -/// Used in cases when the eye is located in a movable object (i.e. mecha) -/mob/camera/ai_eye/proc/update_visibility() - SIGNAL_HANDLER - if(use_static) - ai.camera_visibility(src) - -// Use this when setting the aiEye's location. -// It will also stream the chunk that the new loc is in. - -/mob/camera/ai_eye/proc/setLoc(destination, force_update = FALSE) +/mob/eye/camera/ai/setLoc(destination, force_update = FALSE) if(!ai) return if(!isturf(ai.loc)) return - destination = get_turf(destination) - if(!force_update && (destination == get_turf(src))) - return //we are already here! - if (destination) - abstract_move(destination) - else - moveToNullspace() - if(use_static) - ai.camera_visibility(src) + + . = ..() + if(ai.client && !ai.multicam_on) ai.client.set_eye(src) update_ai_detect_hud() - update_parallax_contents() //Holopad if(istype(ai.current, /obj/machinery/holopad)) var/obj/machinery/holopad/H = ai.current @@ -122,33 +111,31 @@ if(ai.master_multicam) ai.master_multicam.refresh_view() -/mob/camera/ai_eye/zMove(dir, turf/target, z_move_flags = NONE, recursions_left = 1, list/falling_movs) - . = ..() - if(.) - setLoc(loc, force_update = TRUE) - -/mob/camera/ai_eye/Move() - return +/mob/eye/camera/ai/update_visibility() + if(ai) + ai.camera_visibility(src) + else + ..() -/mob/camera/ai_eye/proc/GetViewerClient() +/mob/eye/camera/ai/GetViewerClient() if(ai) return ai.client return null -/mob/camera/ai_eye/Destroy() - if(ai) - ai.all_eyes -= src - ai = null - for(var/V in visibleCameraChunks) - var/datum/camerachunk/c = V - c.remove(src) - GLOB.aiEyes -= src - if(ai_detector_visible) - var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT] - hud.remove_atom_from_hud(src) - var/list/L = hud_list[AI_DETECT_HUD] - QDEL_LIST(L) - return ..() +/mob/eye/camera/ai/examine(mob/user) //Displays a silicon's laws to ghosts + . = ..() + if(istype(ai) && ai.laws && isobserver(user)) + . += "[ai] has the following laws:" + for(var/law in ai.laws.get_law_list(include_zeroth = TRUE)) + . += law + +/mob/eye/camera/ai/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + . = ..() + if(same_z_layer) + return + update_ai_detect_hud() + +/*----------------------------------------------------*/ /atom/proc/move_camera_by_click() if(!isAI(usr)) @@ -189,7 +176,6 @@ sprint = initial(sprint) ai_tracking_tool.reset_tracking() - #undef SPRINT_PER_STEP #undef MAX_SPRINT #undef SPRINT_PER_TICK @@ -215,12 +201,11 @@ /mob/living/silicon/ai/proc/create_eye() if(eyeobj) return - eyeobj = new /mob/camera/ai_eye() + eyeobj = new /mob/eye/camera/ai() all_eyes += eyeobj eyeobj.ai = src - eyeobj.setLoc(loc) eyeobj.name = "[name] (AI Eye)" - eyeobj.real_name = eyeobj.name + eyeobj.setLoc(loc, TRUE) set_eyeobj_visible(TRUE) /mob/living/silicon/ai/proc/set_eyeobj_visible(state = TRUE) @@ -241,7 +226,7 @@ acceleration = !acceleration to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") -/mob/camera/ai_eye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) +/mob/eye/camera/ai/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) . = ..() if(relay_speech && speaker && ai && !radio_freq && speaker != ai && GLOB.cameranet.checkCameraVis(speaker)) ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods) diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm index 4ef437a5303..049243d90f1 100644 --- a/code/modules/mob/living/silicon/ai/multicam.dm +++ b/code/modules/mob/living/silicon/ai/multicam.dm @@ -4,11 +4,11 @@ var/mob/living/silicon/ai/ai var/mutable_appearance/highlighted_background var/highlighted = FALSE - var/mob/camera/ai_eye/pic_in_pic/aiEye + var/mob/eye/camera/ai/pic_in_pic/aiEye /atom/movable/screen/movable/pic_in_pic/ai/Initialize(mapload, datum/hud/hud_owner) . = ..() - aiEye = new /mob/camera/ai_eye/pic_in_pic() + aiEye = new /mob/eye/camera/ai/pic_in_pic() aiEye.screen = src /atom/movable/screen/movable/pic_in_pic/ai/Destroy() @@ -126,37 +126,38 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) //Dummy camera eyes -/mob/camera/ai_eye/pic_in_pic +/mob/eye/camera/ai/pic_in_pic name = "Secondary AI Eye" + icon_state = "ai_pip_camera" invisibility = INVISIBILITY_OBSERVER mouse_opacity = MOUSE_OPACITY_ICON - icon_state = "ai_pip_camera" + ai_detector_color = COLOR_ORANGE + var/atom/movable/screen/movable/pic_in_pic/ai/screen var/list/cameras_telegraphed = list() var/telegraph_cameras = TRUE var/telegraph_range = 7 - ai_detector_color = COLOR_ORANGE -/mob/camera/ai_eye/pic_in_pic/GetViewerClient() +/mob/eye/camera/ai/pic_in_pic/GetViewerClient() if(screen?.ai) return screen.ai.client -/mob/camera/ai_eye/pic_in_pic/setLoc(turf/destination, force_update = FALSE) - if (destination) - abstract_move(destination) - else - moveToNullspace() +/mob/eye/camera/ai/pic_in_pic/update_visibility() if(screen?.ai) screen.ai.camera_visibility(src) else - GLOB.cameranet.visibility(src) + ..() + +/mob/eye/camera/ai/pic_in_pic/setLoc(turf/destination, force_update = FALSE) + . = ..() update_camera_telegraphing() update_ai_detect_hud() -/mob/camera/ai_eye/pic_in_pic/get_visible_turfs() +/mob/eye/camera/ai/pic_in_pic/get_visible_turfs() + SHOULD_CALL_PARENT(FALSE) //we do our own thing here return screen ? screen.get_visible_turfs() : list() -/mob/camera/ai_eye/pic_in_pic/proc/update_camera_telegraphing() +/mob/eye/camera/ai/pic_in_pic/proc/update_camera_telegraphing() if(!telegraph_cameras) return var/list/obj/machinery/camera/add = list() @@ -185,7 +186,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) C.in_use_lights++ C.update_appearance() -/mob/camera/ai_eye/pic_in_pic/proc/disable_camera_telegraphing() +/mob/eye/camera/ai/pic_in_pic/proc/disable_camera_telegraphing() telegraph_cameras = FALSE for (var/obj/machinery/camera/C as anything in cameras_telegraphed) if(QDELETED(C)) @@ -194,7 +195,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) C.update_appearance() cameras_telegraphed.Cut() -/mob/camera/ai_eye/pic_in_pic/Destroy() +/mob/eye/camera/ai/pic_in_pic/Destroy() disable_camera_telegraphing() return ..() diff --git a/code/modules/mob/living/silicon/death.dm b/code/modules/mob/living/silicon/death.dm index 85e749d2765..67083168eb3 100644 --- a/code/modules/mob/living/silicon/death.dm +++ b/code/modules/mob/living/silicon/death.dm @@ -1,8 +1,13 @@ /mob/living/silicon/spawn_gibs() new /obj/effect/gibspawner/robot(drop_location(), src) -/mob/living/silicon/spawn_dust() - new /obj/effect/decal/remains/robot(loc) +/mob/living/silicon/spawn_dust(just_ash) + if(just_ash) + return ..() + + var/obj/effect/decal/remains/robot/robones = new(loc) + robones.pixel_z = -6 + robones.pixel_w = rand(-1, 1) /mob/living/silicon/death(gibbed) diag_hud_set_status() diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 5c4384302b8..99c5686aa53 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -7,12 +7,6 @@ QDEL_NULL(mmi) return ..() -/mob/living/silicon/robot/spawn_dust() - new /obj/effect/decal/remains/robot(loc) - -/mob/living/silicon/robot/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-r") - /mob/living/silicon/robot/death(gibbed) if(stat == DEAD) return diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index a205c9ae26d..eb719c4136d 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -136,6 +136,12 @@ QDEL_LIST(upgrades) QDEL_NULL(cell) QDEL_NULL(robot_suit) + + if (smoke_particles) + remove_shared_particles(smoke_particles) + if (spark_particles) + remove_shared_particles(spark_particles) + return ..() /mob/living/silicon/robot/Topic(href, href_list) diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index d9caad42292..8e222dd54c7 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -193,41 +193,34 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real var/brute_percent = bruteloss / maxHealth var/burn_percent = fireloss / maxHealth + var/old_smoke = smoke_particles if (brute_percent > MODERATE_DAMAGE_UPPER_BOUND) - if(!smoke_particles) - smoke_particles = new(src, /particles/smoke/cyborg/heavy_damage, PARTICLE_ATTACH_MOB) - else if(!istype(smoke_particles.particles, /particles/smoke/cyborg/heavy_damage)) //TODO: needs to be darker - QDEL_NULL(smoke_particles) - smoke_particles = new(src, /particles/smoke/cyborg/heavy_damage, PARTICLE_ATTACH_MOB) - + smoke_particles = /particles/smoke/cyborg/heavy_damage else if (brute_percent > LOW_DAMAGE_UPPER_BOUND) - if(!smoke_particles) - smoke_particles = new(src, /particles/smoke/cyborg, PARTICLE_ATTACH_MOB) - else if(!istype(smoke_particles.particles, /particles/smoke/cyborg)) - QDEL_NULL(smoke_particles) - smoke_particles = new(src, /particles/smoke/cyborg, PARTICLE_ATTACH_MOB) - + smoke_particles = /particles/smoke/cyborg else - if(smoke_particles) - QDEL_NULL(smoke_particles) + smoke_particles = null - if (burn_percent > MODERATE_DAMAGE_UPPER_BOUND) - if(!spark_particles) - spark_particles = new(src, /particles/embers/spark/severe, PARTICLE_ATTACH_MOB) - else if(!istype(spark_particles.particles, /particles/embers/spark/severe)) //TODO: needs to be more dramatic - QDEL_NULL(spark_particles) - spark_particles = new(src, /particles/embers/spark/severe, PARTICLE_ATTACH_MOB) + if (old_smoke != smoke_particles) + if (old_smoke) + remove_shared_particles(old_smoke) + if (smoke_particles) + add_shared_particles(smoke_particles) + var/old_sparks = spark_particles + if (burn_percent > MODERATE_DAMAGE_UPPER_BOUND) + spark_particles = /particles/embers/spark/severe else if (burn_percent > LOW_DAMAGE_UPPER_BOUND) - if(!spark_particles) - spark_particles = new(src, /particles/embers/spark, PARTICLE_ATTACH_MOB) - else if(!istype(spark_particles.particles, /particles/embers/spark)) - QDEL_NULL(spark_particles) - spark_particles = new(src, /particles/embers/spark, PARTICLE_ATTACH_MOB) - + spark_particles = /particles/embers/spark else - if(spark_particles) - QDEL_NULL(spark_particles) + spark_particles = null + + if (old_sparks != spark_particles) + if (old_sparks) + remove_shared_particles(old_sparks) + if (spark_particles) + add_shared_particles(spark_particles) + #undef LOW_DAMAGE_UPPER_BOUND #undef MODERATE_DAMAGE_UPPER_BOUND @@ -456,7 +449,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real if(EXPLODE_DEVASTATE) investigate_log("has been gibbed by an explosion.", INVESTIGATE_DEATHS) gib(DROP_ALL_REMAINS) - return + return TRUE if(EXPLODE_HEAVY) if (stat != DEAD) adjustBruteLoss(60) diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm index 6b7b41cc15f..83f14301519 100644 --- a/code/modules/mob/living/silicon/robot/robot_defines.dm +++ b/code/modules/mob/living/silicon/robot/robot_defines.dm @@ -118,10 +118,10 @@ var/low_power_mode = FALSE ///So they can initialize sparks whenever/N var/datum/effect_system/spark_spread/spark_system - ///Smoke particle holder for brute damage - var/obj/effect/abstract/particle_holder/smoke_particles = null - ///Spark particle holder for burn damage - var/obj/effect/abstract/particle_holder/spark_particles = null + ///Smoke particle type for brute damage + var/smoke_particles + ///Spark particle type for burn damage + var/spark_particles ///Jetpack-like effect. var/ionpulse = FALSE diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index 80e2b8c0c83..6a606209144 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -190,67 +190,63 @@ to_chat(user, span_notice("You complete the ED-209.")) qdel(src) -//Floorbot assemblies -/obj/item/bot_assembly/floorbot +//Repairbot assemblies +/obj/item/bot_assembly/repairbot desc = "It's a toolbox with tiles sticking out the top." - name = "tiles and toolbox" - icon_state = "toolbox_tiles" + name = "Repairbot Chasis" + icon_state = "repairbot_base" throwforce = 10 - created_name = "Floorbot" + created_name = "Repairbot" var/toolbox = /obj/item/storage/toolbox/mechanical var/toolbox_color = "" //Blank for blue, r for red, y for yellow, etc. -/obj/item/bot_assembly/floorbot/Initialize(mapload) +/obj/item/bot_assembly/repairbot/Initialize(mapload) . = ..() update_appearance() -/obj/item/bot_assembly/floorbot/update_name() - . = ..() - switch(build_step) - if(ASSEMBLY_SECOND_STEP) - name = "incomplete floorbot assembly" - else - name = initial(name) +/obj/item/bot_assembly/repairbot/proc/set_color(new_color) + add_atom_colour(new_color, FIXED_COLOUR_PRIORITY) + toolbox_color = new_color -/obj/item/bot_assembly/floorbot/update_desc() +/obj/item/bot_assembly/repairbot/update_desc() . = ..() switch(build_step) - if(ASSEMBLY_SECOND_STEP) - desc = "It's a toolbox with tiles sticking out the top and a sensor attached." + if(ASSEMBLY_FIRST_STEP) + desc = "It's a toolbox with a giant monitor sticking out!." else desc = initial(desc) -/obj/item/bot_assembly/floorbot/update_icon_state() +/obj/item/bot_assembly/repairbot/update_overlays() . = ..() - switch(build_step) - if(ASSEMBLY_FIRST_STEP) - icon_state = "[toolbox_color]toolbox_tiles" - if(ASSEMBLY_SECOND_STEP) - icon_state = "[toolbox_color]toolbox_tiles_sensor" + if(build_step >= ASSEMBLY_FIRST_STEP) + . += mutable_appearance(icon, "repairbot_base_sensor", appearance_flags = RESET_COLOR) + if(build_step >= ASSEMBLY_SECOND_STEP) + . += mutable_appearance(icon, "repairbot_base_arms", appearance_flags = RESET_COLOR) -/obj/item/bot_assembly/floorbot/attackby(obj/item/W, mob/user, params) +/obj/item/bot_assembly/repairbot/attackby(obj/item/item, mob/user, params) ..() switch(build_step) if(ASSEMBLY_FIRST_STEP) - if(isprox(W)) - if(!user.temporarilyRemoveItemFromInventory(W)) - return - to_chat(user, span_notice("You add [W] to [src].")) - qdel(W) - build_step++ - update_appearance() - + if(!istype(item, /obj/item/bodypart/arm/left/robot) && !istype(item, /obj/item/bodypart/arm/right/robot)) + return + if(!can_finish_build(item, user)) + return + build_step++ + to_chat(user, span_notice("You add [item] to [src]. Boop beep!")) + qdel(item) + update_appearance() if(ASSEMBLY_SECOND_STEP) - if(istype(W, /obj/item/bodypart/arm/left/robot) || istype(W, /obj/item/bodypart/arm/right/robot)) - if(!can_finish_build(W, user)) - return - var/mob/living/simple_animal/bot/floorbot/A = new(drop_location(), toolbox_color) - A.name = created_name - A.robot_arm = W.type - A.toolbox = toolbox - to_chat(user, span_notice("You add [W] to [src]. Boop beep!")) - qdel(W) - qdel(src) + if(!istype(item, /obj/item/stack/conveyor)) + return + if(!can_finish_build(item, user)) + return + var/mob/living/basic/bot/repairbot/repair = new(drop_location()) + repair.name = created_name + repair.toolbox = toolbox + repair.set_color(toolbox_color) + to_chat(user, span_notice("You add [item] to [src]. Boop beep!")) + qdel(item) + qdel(src) //Medbot Assembly diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm deleted file mode 100644 index ae17e58686c..00000000000 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ /dev/null @@ -1,448 +0,0 @@ -#define HULL_BREACH 1 -#define LINE_SPACE_MODE 2 -#define FIX_TILE 3 -#define AUTO_TILE 4 -#define PLACE_TILE 5 -#define REPLACE_TILE 6 -#define TILE_EMAG 7 - -//Floorbot -/mob/living/simple_animal/bot/floorbot - name = "\improper Floorbot" - desc = "A little floor repairing robot, he looks so excited!" - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "floorbot0" - density = FALSE - health = 25 - maxHealth = 25 - - req_one_access = list(ACCESS_ROBOTICS, ACCESS_CONSTRUCTION) - radio_key = /obj/item/encryptionkey/headset_eng - radio_channel = RADIO_CHANNEL_ENGINEERING - bot_type = FLOOR_BOT - hackables = "floor construction protocols" - path_image_color = "#FFA500" - possessed_message = "You are a floorbot! Repair the hull to the best of your ability!" - - var/process_type //Determines what to do when process_scan() receives a target. See process_scan() for details. - var/targetdirection - var/replacetiles = FALSE - var/placetiles = FALSE - var/maxtiles = 100 - var/obj/item/stack/tile/tilestack - var/fixfloors = TRUE - var/autotile = FALSE - var/turf/target - var/toolbox = /obj/item/storage/toolbox/mechanical - var/toolbox_color = "" - -/mob/living/simple_animal/bot/floorbot/Initialize(mapload, new_toolbox_color) - . = ..() - ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) - toolbox_color = new_toolbox_color - update_appearance(UPDATE_ICON) - - // Doing this hurts my soul, but simplebot access reworks are for another day. - var/datum/id_trim/job/engi_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/station_engineer] - access_card.add_access(engi_trim.access + engi_trim.wildcard_access) - prev_access = access_card.access.Copy() - - if(toolbox_color == "s") - health = 100 - maxHealth = 100 - -/mob/living/simple_animal/bot/floorbot/Exited(atom/movable/gone, direction) - if(tilestack == gone) - if(tilestack && tilestack.max_amount < tilestack.amount) //split the stack if it exceeds its normal max_amount - var/iterations = round(tilestack.amount/tilestack.max_amount) //round() without second arg floors the value - for(var/a in 1 to iterations) - if(a == iterations) - tilestack.split_stack(null, tilestack.amount - tilestack.max_amount) - else - tilestack.split_stack(null, tilestack.max_amount) - tilestack = null - -/mob/living/simple_animal/bot/floorbot/turn_on() - . = ..() - update_appearance() - -/mob/living/simple_animal/bot/floorbot/turn_off() - ..() - update_appearance() - -/mob/living/simple_animal/bot/floorbot/bot_reset() - ..() - target = null - toggle_magnet(FALSE) - -/mob/living/simple_animal/bot/floorbot/attackby(obj/item/W , mob/user, params) - if(istype(W, /obj/item/stack/tile/iron)) - to_chat(user, span_notice("The floorbot can produce normal tiles itself.")) - return - if(istype(W, /obj/item/stack/tile)) - var/old_amount = tilestack ? tilestack.amount : 0 - var/obj/item/stack/tile/tiles = W - if(tilestack) - if(!tiles.can_merge(tilestack)) - to_chat(user, span_warning("Different custom tiles are already inside the floorbot.")) - return - if(tilestack.amount >= maxtiles) - to_chat(user, span_warning("The floorbot can't hold any more custom tiles.")) - return - tiles.merge(tilestack, maxtiles) - else - if(tiles.amount > maxtiles) - tilestack = tilestack.split_stack(null, maxtiles) - else - tilestack = W - tilestack.forceMove(src) - to_chat(user, span_notice("You load [tilestack.amount - old_amount] tiles into the floorbot. It now contains [tilestack.amount] tiles.")) - return - else - ..() - -/mob/living/simple_animal/bot/floorbot/emag_act(mob/user, obj/item/card/emag/emag_card) - . = ..() - if(!(bot_cover_flags & BOT_COVER_EMAGGED)) - return - balloon_alert(user, "safeties disabled") - audible_message(span_danger("[src] buzzes oddly!")) - return TRUE - -///mobs should use move_resist instead of anchored. -/mob/living/simple_animal/bot/floorbot/proc/toggle_magnet(engage = TRUE, change_icon = TRUE) - if(engage) - ADD_TRAIT(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT) - move_resist = INFINITY - if(change_icon) - icon_state = "[toolbox_color]floorbot-c" - else - REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT) - move_resist = initial(move_resist) - if(change_icon) - update_icon() - -// Variables sent to TGUI -/mob/living/simple_animal/bot/floorbot/ui_data(mob/user) - var/list/data = ..() - if(!(bot_cover_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user)) - data["custom_controls"]["tile_hull"] = autotile - data["custom_controls"]["place_tiles"] = placetiles - data["custom_controls"]["place_custom"] = replacetiles - data["custom_controls"]["repair_damage"] = fixfloors - data["custom_controls"]["traction_magnets"] = !!HAS_TRAIT_FROM(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT) - data["custom_controls"]["tile_stack"] = 0 - data["custom_controls"]["line_mode"] = FALSE - if(tilestack) - data["custom_controls"]["tile_stack"] = tilestack.amount - if(targetdirection) - data["custom_controls"]["line_mode"] = dir2text(targetdirection) - return data - -// Actions received from TGUI -/mob/living/simple_animal/bot/floorbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - var/mob/user = ui.user - if(. || (bot_cover_flags & BOT_COVER_LOCKED && !HAS_SILICON_ACCESS(user))) - return - - switch(action) - if("place_custom") - replacetiles = !replacetiles - if("place_tiles") - placetiles = !placetiles - if("repair_damage") - fixfloors = !fixfloors - if("tile_hull") - autotile = !autotile - if("traction_magnets") - toggle_magnet(!HAS_TRAIT_FROM(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT), FALSE) - if("eject_tiles") - if(tilestack) - tilestack.forceMove(drop_location()) - if("line_mode") - var/setdir = tgui_input_list(user, "Select construction direction", "Direction", list("north", "east", "south", "west", "disable")) - if(isnull(setdir) || QDELETED(ui) || ui.status != UI_INTERACTIVE) - return - switch(setdir) - if("north") - targetdirection = 1 - if("south") - targetdirection = 2 - if("east") - targetdirection = 4 - if("west") - targetdirection = 8 - if("disable") - targetdirection = null - -/mob/living/simple_animal/bot/floorbot/handle_automated_action() - if(!..()) - return - - if(mode == BOT_REPAIRING) - return - - if(prob(5)) - audible_message("[src] makes an excited booping beeping sound!") - - var/list/tiles_scanned = list() - //Normal scanning procedure. We have tiles loaded, are not emagged. - if(!target && !(bot_cover_flags & BOT_COVER_EMAGGED)) - if(targetdirection != null) //The bot is in line mode. - var/turf/T = get_step(src, targetdirection) - if(isspaceturf(T)) //Check for space - target = T - process_type = LINE_SPACE_MODE - if(isfloorturf(T)) //Check for floor - target = T - if(!target) - process_type = HULL_BREACH //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones. - - tiles_scanned += list(/turf/open/space) - - if(!target && placetiles) //Finds a floor without a tile and gives it one. - process_type = PLACE_TILE //The target must be the floor and not a tile. The floor must not already have a floortile. - tiles_scanned += list(/turf/open/floor) - - if(!target && fixfloors) //Repairs damaged floors and tiles. - process_type = FIX_TILE - tiles_scanned += list(/turf/open/floor) - - if(!target && replacetiles && tilestack) //Replace a floor tile with custom tile - process_type = REPLACE_TILE //The target must be a tile. The floor must already have a floortile. - tiles_scanned += list(/turf/open/floor) - - if(!target && bot_cover_flags & BOT_COVER_EMAGGED) //We are emagged! Time to rip up the floors! - process_type = TILE_EMAG - tiles_scanned += list(/turf/open/floor) - - target = scan(tiles_scanned) - - if (!target) - if(bot_mode_flags & BOT_MODE_AUTOPATROL) - switch(mode) - if(BOT_IDLE, BOT_START_PATROL) - start_patrol() - if(BOT_PATROL) - bot_patrol() - return - - if(loc == target || loc == get_turf(target)) - if(check_bot(target)) //Target is not defined at the parent - if(prob(50)) //50% chance to still try to repair so we dont end up with 2 floorbots failing to fix the last breach - target = null - path = list() - return - if(isturf(target) && !(bot_cover_flags & BOT_COVER_EMAGGED)) - repair(target) - else if(bot_cover_flags & BOT_COVER_EMAGGED && isfloorturf(target)) - var/turf/open/floor/floor = target - toggle_magnet() - mode = BOT_REPAIRING - if(isplatingturf(floor)) - floor.attempt_lattice_replacement() - else - floor.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) - audible_message(span_danger("[src] makes an excited booping sound.")) - addtimer(CALLBACK(src, PROC_REF(go_idle)), 0.5 SECONDS) - path = list() - return - - if(!length(path)) - if(!isturf(target)) - var/turf/TL = get_turf(target) - path = get_path_to(src, TL, max_distance=30, access=access_card.GetAccess(), simulated_only = FALSE) - else - path = get_path_to(src, target, max_distance=30, access=access_card.GetAccess(), simulated_only = FALSE) - - if(!bot_move(target)) - add_to_ignore(target) - target = null - mode = BOT_IDLE - else if(!bot_move(target)) - target = null - mode = BOT_IDLE - -/mob/living/simple_animal/bot/floorbot/proc/go_idle() - if (QDELETED(src)) - return - toggle_magnet(FALSE) - mode = BOT_IDLE - target = null - -/mob/living/simple_animal/bot/floorbot/proc/is_hull_breach(turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas. - var/area/t_area = get_area(t) - if(t_area && (t_area.name == "Space" || findtext(t_area.name, "huttle"))) - return FALSE - else - return TRUE - -//Floorbots, having several functions, need sort out special conditions here. -/mob/living/simple_animal/bot/floorbot/process_scan(scan_target) - var/result - var/turf/open/floor/floor - move_resist = initial(move_resist) - switch(process_type) - if(HULL_BREACH) //The most common job, patching breaches in the station's hull. - if(is_hull_breach(scan_target)) //Ensure that the targeted space turf is actually part of the station, and not random space. - result = scan_target - move_resist = INFINITY //Prevent the floorbot being blown off-course while trying to reach a hull breach. - if(LINE_SPACE_MODE) //Space turfs in our chosen direction are considered. - if(get_dir(src, scan_target) == targetdirection) - result = scan_target - move_resist = INFINITY - if(PLACE_TILE) - floor = scan_target - if(isplatingturf(floor)) //The floor must not already have a tile. - result = floor - if(REPLACE_TILE) - floor = scan_target - if(isfloorturf(floor) && !isplatingturf(floor)) //The floor must already have a tile. - result = floor - if(FIX_TILE) //Selects only damaged floors. - floor = scan_target - if(istype(floor) && (floor.broken || floor.burnt)) - result = floor - if(TILE_EMAG) //Emag mode! Rip up the floor and cause breaches to space! - floor = scan_target - if(!isplatingturf(floor)) - result = floor - else //If no special processing is needed, simply return the result. - result = scan_target - return result - -/mob/living/simple_animal/bot/floorbot/proc/repair(turf/target_turf) - if(check_bot_working(target_turf)) - add_to_ignore(target_turf) - target = null - playsound(src, 'sound/mobs/non-humanoids/floorbot/whistlereset.ogg', 50, TRUE) - return - if(isspaceturf(target_turf)) - //Must be a hull breach or in line mode to continue. - if(!is_hull_breach(target_turf) && !targetdirection) - target = null - return - else if(!isfloorturf(target_turf)) - return - if(isspaceturf(target_turf)) //If we are fixing an area not part of pure space, it is - toggle_magnet() - visible_message(span_notice("[targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] ")) - mode = BOT_REPAIRING - if(!do_after(src, 5 SECONDS, target = target_turf) && mode == BOT_REPAIRING) - go_idle() - return - - if(!autotile) - target_turf.place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) - go_idle() - return - - if(replacetiles && tilestack) - target_turf.place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) //make sure a hull is actually below the floor tile - tilestack.place_tile(target_turf, src) - if(!tilestack) - speak("Requesting refill of custom floor tiles to continue replacing.") - else - target_turf.place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) //make sure a hull is actually below the floor tile - target_turf.place_on_top(/turf/open/floor/iron, flags = CHANGETURF_INHERIT_AIR) - go_idle() - return - - var/turf/open/floor/floor = target_turf - var/was_replacing = replacetiles - - if(floor.broken || floor.burnt || isplatingturf(floor)) - toggle_magnet() - mode = BOT_REPAIRING - visible_message(span_notice("[src] begins [(floor.broken || floor.burnt) ? "repairing the floor" : "placing a floor tile"].")) - if(!do_after(src, 5 SECONDS, target = floor) && mode == BOT_REPAIRING) - go_idle() - return - else if(replacetiles && tilestack && floor.type != tilestack.turf_type) - toggle_magnet() - mode = BOT_REPAIRING - visible_message(span_notice("[src] begins replacing the floor tiles.")) - if(do_after(src, 5 SECONDS, target = target_turf) && mode == BOT_REPAIRING && tilestack) - go_idle() - return - - var/area/is_this_maints = get_area(floor) - if(was_replacing && tilestack) //turn the tile into plating (if needed), then replace it - floor = floor.make_plating(TRUE) || floor - tilestack.place_tile(floor, src) - if(!tilestack) - speak("Requesting refill of custom floor tiles to continue replacing.") - go_idle() - return - - if(floor.broken || floor.burnt) //repair the tile and reset it to be undamaged (rather than replacing it) - floor.broken = FALSE - floor.burnt = FALSE - floor.update_appearance() - go_idle() - return - - if(istype(is_this_maints, /area/station/maintenance)) //place catwalk if it's plating and we're in maints - floor.place_on_top(/turf/open/floor/catwalk_floor, flags = CHANGETURF_INHERIT_AIR) - go_idle() - return - - //place normal tile if it's plating anywhere else - floor = floor.make_plating(TRUE) || floor - floor.place_on_top(/turf/open/floor/iron, flags = CHANGETURF_INHERIT_AIR) - go_idle() - -/mob/living/simple_animal/bot/floorbot/update_icon_state() - . = ..() - icon_state = "[toolbox_color]floorbot[get_bot_flag(bot_mode_flags, BOT_MODE_ON)]" - -/mob/living/simple_animal/bot/floorbot/explode() - target = null - var/atom/Tsec = drop_location() - - drop_part(toolbox, Tsec) - - new /obj/item/assembly/prox_sensor(Tsec) - - if(tilestack) - tilestack.forceMove(drop_location()) - - new /obj/item/stack/tile/iron/base(Tsec, 1) - return ..() - -/mob/living/simple_animal/bot/floorbot/UnarmedAttack(atom/target, proximity_flag, list/modifiers) - if(!can_unarmed_attack()) - return - - if (!isturf(target)) - return ..() - - if(!(bot_cover_flags & BOT_COVER_EMAGGED) || !isfloorturf(target)) - repair(target) - return - - var/turf/open/floor/floor = target - if(isplatingturf(floor)) - floor.attempt_lattice_replacement() - else - floor.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) - audible_message(span_danger("[src] makes an excited booping sound.")) - -/** - * Checks a given turf to see if another floorbot is there, working as well. - */ -/mob/living/simple_animal/bot/floorbot/proc/check_bot_working(turf/active_turf) - if(isturf(active_turf)) - for(var/mob/living/simple_animal/bot/floorbot/robot in active_turf) - if(robot.mode == BOT_REPAIRING) - return TRUE - return FALSE - -#undef HULL_BREACH -#undef LINE_SPACE_MODE -#undef FIX_TILE -#undef AUTO_TILE -#undef PLACE_TILE -#undef REPLACE_TILE -#undef TILE_EMAG diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index dd3303f3523..5e9018384be 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -379,7 +379,7 @@ if(!isliving(user)) return - if(!istype(AM) || isdead(AM) || iscameramob(AM) || istype(AM, /obj/effect/dummy/phased_mob)) + if(!istype(AM) || isdead(AM) || iseyemob(AM) || istype(AM, /obj/effect/dummy/phased_mob)) return load(AM) @@ -809,7 +809,7 @@ if(user.incapacitated || (istype(L) && L.body_position == LYING_DOWN)) return - if(!istype(AM) || iscameramob(AM) || istype(AM, /obj/effect/dummy/phased_mob)) //allows ghosts! + if(!istype(AM) || iseyemob(AM) || istype(AM, /obj/effect/dummy/phased_mob)) //allows ghosts! return load(AM) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm index 03d79c108d6..f57ea3e0582 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm @@ -121,7 +121,7 @@ IGNORE_PROC_IF_NOT_TARGET(attack_animal) /mob/living/simple_animal/hostile/asteroid/curseblob/bullet_act(obj/projectile/Proj) if(Proj.firer != set_target) - return + return BULLET_ACT_BLOCK return ..() /mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index e89522b6360..4efc847805f 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -668,7 +668,7 @@ return 0 // Infinite duration status effects technically are not "timed status effects" // by name or nature, but support is included just in case. - if(existing.duration == -1) + if(existing.duration == STATUS_EFFECT_PERMANENT) return INFINITY return existing.duration - world.time diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 1ca01ce3474..5e65fd1ce47 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -893,9 +893,8 @@ return if(!selected_hand) - selected_hand = (active_hand_index % held_items.len)+1 - - if(istext(selected_hand)) + selected_hand = active_hand_index + else if(istext(selected_hand)) selected_hand = LOWER_TEXT(selected_hand) if(selected_hand == "right" || selected_hand == "r") selected_hand = 2 @@ -904,8 +903,9 @@ if(selected_hand != active_hand_index) swap_hand(selected_hand) - else - mode() + + // _queue_verb requires a client, so when we don't have it (AI controlled mob) we don't use it + client ? mode() : execute_mode() /mob/proc/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //For sec bot threat assessment return 0 diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index bb3397cfec9..0d232ef3b66 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -172,6 +172,12 @@ var/min_x = -(strength*ICON_SIZE_X) var/min_y = -(strength*ICON_SIZE_Y) + if(C.prefs?.read_preference(/datum/preference/toggle/screen_shake_darken)) + var/type = /atom/movable/screen/fullscreen/flash/black + + M.overlay_fullscreen("flash", type) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, clear_fullscreen), "flash", 3 SECONDS), 3 SECONDS) + //How much time to allot for each pixel moved var/time_scalar = (1 / ICON_SIZE_ALL) * TILES_PER_SECOND var/last_x = oldx diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index a90a3fedc24..df33c71305e 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -261,7 +261,7 @@ return new_slime /mob/proc/become_overmind(starting_points = OVERMIND_STARTING_POINTS) - var/mob/camera/blob/B = new /mob/camera/blob(get_turf(src), starting_points) + var/mob/eye/blob/B = new /mob/eye/blob(get_turf(src), starting_points) B.key = key . = B qdel(src) diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index ee4725075aa..ecc3eaaedc2 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -26,7 +26,7 @@ return var/parts_to_check = parts - part if(part.loc == src) - if(!deploy(user, part) || (active && !delayed_seal_part(part))) + if(!deploy(user, part)) return SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user) for(var/obj/item/checking_part as anything in parts_to_check) @@ -35,7 +35,7 @@ choose_deploy(user) break else - if((active && !delayed_seal_part(part, silent = TRUE)) || !retract(user, part)) + if(!retract(user, part)) return SEND_SIGNAL(src, COMSIG_MOD_RETRACTED, user) for(var/obj/item/checking_part as anything in parts_to_check) @@ -64,12 +64,7 @@ if(deploy && part.loc == src) if(!deploy(null, part)) continue - if(active && !delayed_seal_part(part)) - retract(null, part) - return else if(!deploy && part.loc != src) - if(active && !delayed_seal_part(part)) - return retract(null, part) if(deploy) SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user) @@ -78,7 +73,7 @@ return TRUE /// Deploys a part of the suit onto the user. -/obj/item/mod/control/proc/deploy(mob/user, obj/item/part) +/obj/item/mod/control/proc/deploy(mob/user, obj/item/part, instant = FALSE) var/datum/mod_part/part_datum = get_part_datum(part) if(!wearer) playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) @@ -86,7 +81,7 @@ if(part.loc != src) if(!user) return FALSE - balloon_alert(user, "[part.name] already deployed!") + balloon_alert(user, "already deployed!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) if(part_datum.can_overslot) var/obj/item/overslot = wearer.get_item_by_slot(part.slot_flags) @@ -97,14 +92,21 @@ if(wearer.equip_to_slot_if_possible(part, part.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) ADD_TRAIT(part, TRAIT_NODROP, MOD_TRAIT) wearer.update_clothing(slot_flags) - if(!user) + SEND_SIGNAL(src, COMSIG_MOD_PART_DEPLOYED, user, part_datum) + if(user) + wearer.visible_message(span_notice("[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss."), + span_notice("[part] deploy[part.p_s()] with a mechanical hiss."), + span_hear("You hear a mechanical hiss.")) + playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + if(!active || part_datum.sealed) return TRUE - wearer.visible_message(span_notice("[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss."), - span_notice("[part] deploy[part.p_s()] with a mechanical hiss."), - span_hear("You hear a mechanical hiss.")) - playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - SEND_SIGNAL(src, COMSIG_MOD_PART_DEPLOYED, user, part) - return TRUE + if(instant) + seal_part(part, is_sealed = TRUE) + return TRUE + else if(delayed_seal_part(part)) + return TRUE + balloon_alert(user, "can't seal, retracting!") + retract(user, part, instant = TRUE) else if(part_datum.overslotting) var/obj/item/overslot = part_datum.overslotting @@ -117,14 +119,21 @@ return FALSE /// Retract a part of the suit from the user. -/obj/item/mod/control/proc/retract(mob/user, obj/item/part) +/obj/item/mod/control/proc/retract(mob/user, obj/item/part, instant = FALSE) var/datum/mod_part/part_datum = get_part_datum(part) if(part.loc == src) if(!user) return FALSE - balloon_alert(user, "[part.name] already retracted!") + balloon_alert(user, "already retracted!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE + if(active && part_datum.sealed) + if(instant) + seal_part(part, is_sealed = FALSE) + else if(!delayed_seal_part(part)) + balloon_alert(user, "can't unseal!") + playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE REMOVE_TRAIT(part, TRAIT_NODROP, MOD_TRAIT) wearer.transferItemToLoc(part, src, force = TRUE) if(part_datum.overslotting) @@ -132,7 +141,7 @@ if(!QDELING(wearer) && !wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE) wearer.update_clothing(slot_flags) - SEND_SIGNAL(src, COMSIG_MOD_PART_RETRACTED, user, part) + SEND_SIGNAL(src, COMSIG_MOD_PART_RETRACTED, user, part_datum) if(!user) return TRUE wearer.visible_message(span_notice("[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss."), @@ -145,7 +154,7 @@ /obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE) if(!wearer) if(!force_deactivate) - balloon_alert(user, "equip suit first!") + balloon_alert(user, "not equipped!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE)) @@ -156,16 +165,16 @@ playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(!get_charge() && !force_deactivate) - balloon_alert(user, "suit not powered!") + balloon_alert(user, "no power source!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(open && !force_deactivate) - balloon_alert(user, "close the suit panel!") + balloon_alert(user, "panel open!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(activating) if(!force_deactivate) - balloon_alert(user, "suit already [active ? "shutting down" : "starting up"]!") + balloon_alert(user, "already [active ? "shutting down" : "starting up"]!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE for(var/obj/item/mod/module/module as anything in modules) @@ -221,13 +230,12 @@ SEND_SIGNAL(src, COMSIG_MOD_TOGGLED, user) return TRUE -/obj/item/mod/control/proc/delayed_seal_part(obj/item/clothing/part, silent = FALSE) +/obj/item/mod/control/proc/delayed_seal_part(obj/item/clothing/part) . = FALSE var/datum/mod_part/part_datum = get_part_datum(part) if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), hidden = TRUE)) - if(!silent) - to_chat(wearer, span_notice("[part] [!part_datum.sealed ? part_datum.sealed_message : part_datum.unsealed_message].")) - playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + to_chat(wearer, span_notice("[part] [!part_datum.sealed ? part_datum.sealed_message : part_datum.unsealed_message].")) + playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) seal_part(part, is_sealed = !part_datum.sealed) return TRUE @@ -262,6 +270,7 @@ wearer.update_obscured_slots(part.visor_flags_inv) if((part.clothing_flags & (MASKINTERNALS|HEADINTERNALS)) && wearer.invalid_internals()) wearer.cutoff_internals() + SEND_SIGNAL(src, COMSIG_MOD_PART_SEALED, part_datum) if(is_sealed) if (!active) return @@ -308,12 +317,8 @@ /// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits. /obj/item/mod/control/proc/quick_activation() - for(var/obj/item/part as anything in get_parts()) - deploy(null, part) - for(var/obj/item/part as anything in get_parts()) - if(part.loc == src) - continue - seal_part(part, is_sealed = TRUE) control_activation(is_on = TRUE) + for(var/obj/item/part as anything in get_parts()) + deploy(null, part, instant = TRUE) #undef MOD_ACTIVATION_STEP_FLAGS diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm index 1336ff17074..62e9188fddd 100644 --- a/code/modules/mod/mod_ai.dm +++ b/code/modules/mod/mod_ai.dm @@ -3,19 +3,19 @@ if(!.) return if(!open) //mod must be open - balloon_alert(user, "suit must be open to transfer!") + balloon_alert(user, "panel closed!") return switch(interaction) if(AI_TRANS_TO_CARD) if(!ai_assistant) - balloon_alert(user, "no ai in suit!") + balloon_alert(user, "no ai in unit!") return balloon_alert(user, "transferring to card...") if(!do_after(user, 5 SECONDS, target = src)) balloon_alert(user, "interrupted!") return if(!ai_assistant) - balloon_alert(user, "no ai in suit!") + balloon_alert(user, "no ai in unit!") return balloon_alert(user, "ai transferred to card") ai_exit_mod(card) @@ -33,13 +33,13 @@ if(intAI.stat || !intAI.client) balloon_alert(user, "ai unresponsive!") return - balloon_alert(user, "transferring to suit...") + balloon_alert(user, "transferring to unit...") if(!do_after(user, 5 SECONDS, target = src)) balloon_alert(user, "interrupted!") return if(ai_assistant) return - balloon_alert(user, "ai transferred to suit") + balloon_alert(user, "ai transferred to unit") ai_enter_mod(intAI) card.AI = null @@ -74,14 +74,14 @@ if (isnull(card.pai?.mind)) balloon_alert(user, "pAI unresponsive!") return FALSE - balloon_alert(user, "transferring to suit...") + balloon_alert(user, "transferring to unit...") if (!do_after(user, 5 SECONDS, target = src)) balloon_alert(user, "interrupted!") return FALSE if (!user.transferItemToLoc(card, src)) balloon_alert(user, "transfer failed!") return FALSE - balloon_alert(user, "pAI transferred to suit") + balloon_alert(user, "pAI transferred to unit") var/mob/living/silicon/pai/pai_assistant = card.pai pai_assistant.can_transmit = TRUE pai_assistant.can_receive = TRUE @@ -99,14 +99,14 @@ return FALSE if (!forced) if (!open) - balloon_alert(user, "suit panel closed!") + balloon_alert(user, "panel closed!") return FALSE balloon_alert(user, "uninstalling card...") if (!do_after(user, 5 SECONDS, target = src)) balloon_alert(user, "interrupted!") return FALSE - balloon_alert(user, "pAI removed from suit") + balloon_alert(user, "pAI removed") var/mob/living/silicon/pai/pai_helper = ai_assistant pai_helper.can_holo = TRUE pai_helper.card.forceMove(get_turf(src)) @@ -115,7 +115,7 @@ /// Called when a new ai assistant is inserted /obj/item/mod/control/proc/on_gained_assistant(mob/living/silicon/new_helper) ai_assistant = new_helper - balloon_alert(new_helper, "transferred to a suit") + balloon_alert(new_helper, "transferred to a mod unit") for(var/datum/action/action as anything in actions) action.Grant(new_helper) @@ -134,7 +134,10 @@ #define AI_FALL_TIME (1 SECONDS) /obj/item/mod/control/relaymove(mob/user, direction) - if((!active && wearer) || get_charge() < CHARGE_PER_STEP || user != ai_assistant || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE)) + if((!active && wearer) || get_charge() < CHARGE_PER_STEP || user != ai_assistant || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE)) + return FALSE + var/datum/mod_part/legs_to_move = get_part_datum_from_slot(ITEM_SLOT_FEET) + if(wearer && (!legs_to_move || !legs_to_move.sealed)) return FALSE var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? sqrt(2) : 1) * (wearer ? WEARER_DELAY : LONE_DELAY) if(wearer && !wearer.Process_Spacemove(direction)) @@ -147,7 +150,7 @@ if(ismovable(wearer?.loc)) return wearer.loc.relaymove(wearer, direction) else if(wearer) - ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, MOD_TRAIT) + ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, REF(src)) addtimer(CALLBACK(src, PROC_REF(ai_fall)), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE) var/atom/movable/mover = wearer || src return mover.try_step_multiz(direction) @@ -160,7 +163,7 @@ /obj/item/mod/control/proc/ai_fall() if(!wearer) return - REMOVE_TRAIT(wearer, TRAIT_FORCED_STANDING, MOD_TRAIT) + REMOVE_TRAIT(wearer, TRAIT_FORCED_STANDING, REF(src)) /obj/item/mod/ai_minicard name = "AI mini-card" diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm index 29377c66c6d..8c5e7a2c490 100644 --- a/code/modules/mod/mod_clothes.dm +++ b/code/modules/mod/mod_clothes.dm @@ -44,6 +44,7 @@ cold_protection = HANDS|ARMS item_flags = IMMUTABLE_SLOW equip_sound = null + pickup_sound = null drop_sound = null /obj/item/clothing/shoes/mod diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm index 16ce70df945..3581c04e606 100644 --- a/code/modules/mod/mod_construction.dm +++ b/code/modules/mod/mod_construction.dm @@ -162,7 +162,7 @@ if(!istype(part, /obj/item/mod/core)) return if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "core stuck to your hand!") + balloon_alert(user, "it's stuck!") return playsound(src, 'sound/machines/click.ogg', 30, TRUE) balloon_alert(user, "core inserted") @@ -181,7 +181,7 @@ if(SCREWED_CORE_STEP) if(istype(part, /obj/item/mod/construction/helmet)) //Construct if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "helmet stuck to your hand!") + balloon_alert(user, "it's stuck!") return playsound(src, 'sound/machines/click.ogg', 30, TRUE) balloon_alert(user, "helmet added") @@ -194,7 +194,7 @@ if(HELMET_STEP) if(istype(part, /obj/item/mod/construction/chestplate)) //Construct if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "chestplate stuck to your hand!") + balloon_alert(user, "it's stuck!") return playsound(src, 'sound/machines/click.ogg', 30, TRUE) balloon_alert(user, "chestplate added") @@ -209,7 +209,7 @@ if(CHESTPLATE_STEP) if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "gauntlets stuck to your hand!") + balloon_alert(user, "it's stuck!") return playsound(src, 'sound/machines/click.ogg', 30, TRUE) balloon_alert(user, "gauntlets added") @@ -224,10 +224,10 @@ if(GAUNTLETS_STEP) if(istype(part, /obj/item/mod/construction/boots)) //Construct if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "boots added") + balloon_alert(user, "it's stuck!") return playsound(src, 'sound/machines/click.ogg', 30, TRUE) - balloon_alert(user, "fit [part.name]") + balloon_alert(user, "boots added") boots = part step = BOOTS_STEP else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct @@ -260,13 +260,14 @@ if(istype(part, /obj/item/mod/construction/plating)) //Construct var/obj/item/mod/construction/plating/external_plating = part if(!user.transferItemToLoc(part, src)) + balloon_alert(user, "it's stuck!") return playsound(src, 'sound/machines/click.ogg', 30, TRUE) var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core) core = null qdel(src) user.put_in_hands(mod) - mod.balloon_alert(user, "suit finished") + mod.balloon_alert(user, "unit finished") else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct if(part.use_tool(src, user, 0, volume=30)) balloon_alert(user, "assembly unscrewed") diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index 0f521e40691..436c9e7c20d 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -97,7 +97,6 @@ theme.set_up_parts(src, new_skin) for(var/obj/item/part as anything in get_parts()) RegisterSignal(part, COMSIG_ATOM_DESTRUCTION, PROC_REF(on_part_destruction)) - RegisterSignal(part, COMSIG_QDELETING, PROC_REF(on_part_deletion)) set_wires(new /datum/wires/mod(src)) if(length(req_access)) locked = TRUE @@ -118,14 +117,19 @@ QDEL_NULL(core) QDEL_NULL(mod_link) for(var/datum/mod_part/part_datum as anything in get_part_datums(all = TRUE)) + var/obj/item/part_item = part_datum.part_item part_datum.part_item = null part_datum.overslotting = null + mod_parts -= part_datum + if(!QDELING(part_item)) + qdel(part_item) return ..() /obj/item/mod/control/atom_destruction(damage_flag) + var/atom/visible_atom = wearer || src if(wearer) - wearer.visible_message(span_danger("[src] fall[p_s()] apart, completely destroyed!"), vision_distance = COMBAT_MESSAGE_RANGE) clean_up() + visible_atom.visible_message(span_bolddanger("[src] fall[p_s()] apart, completely destroyed!"), vision_distance = COMBAT_MESSAGE_RANGE) for(var/obj/item/mod/module/module as anything in modules) uninstall(module) if(ai_assistant) @@ -220,12 +224,12 @@ if(user != wearer) return ..() if(active) - balloon_alert(wearer, "deactivate the suit first!") + balloon_alert(wearer, "unit active!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) return for(var/obj/item/part as anything in get_parts()) if(part.loc != src) - balloon_alert(user, "retract parts first!") + balloon_alert(user, "parts extended!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) return FALSE @@ -233,12 +237,12 @@ if(user != wearer || !istype(over_object, /atom/movable/screen/inventory/hand)) return if(active) - balloon_alert(wearer, "deactivate the suit first!") + balloon_alert(wearer, "unit active!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) return for(var/obj/item/part as anything in get_parts()) if(part.loc != src) - balloon_alert(wearer, "retract parts first!") + balloon_alert(wearer, "parts extended!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) return if(!wearer.incapacitated) @@ -266,14 +270,15 @@ /obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver) if(active || activating || ai_controller) - balloon_alert(user, "deactivate suit first!") + balloon_alert(user, "unit active!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return ITEM_INTERACT_BLOCKING balloon_alert(user, "[open ? "closing" : "opening"] cover...") screwdriver.play_tool_sound(src, 100) if(screwdriver.use_tool(src, user, 1 SECONDS)) if(active || activating) - balloon_alert(user, "deactivate suit first!") + balloon_alert(user, "unit active!") + return ITEM_INTERACT_SUCCESS screwdriver.play_tool_sound(src, 100) balloon_alert(user, "cover [open ? "closed" : "opened"]") open = !open @@ -283,7 +288,7 @@ /obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar) if(!open) - balloon_alert(user, "open the cover first!") + balloon_alert(user, "cover closed!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return ITEM_INTERACT_BLOCKING if(!allowed(user)) @@ -315,14 +320,14 @@ /obj/item/mod/control/tool_act(mob/living/user, obj/item/tool, list/modifiers) if(istype(tool, /obj/item/pai_card)) if(!open) - balloon_alert(user, "open the cover first!") + balloon_alert(user, "cover closed!") return NONE // shoves the card in the storage anyways insert_pai(user, tool) return ITEM_INTERACT_SUCCESS if(istype(tool, /obj/item/mod/paint)) var/obj/item/mod/paint/paint_kit = tool if(active || activating) - balloon_alert(user, "suit is active!") + balloon_alert(user, "unit active!") return ITEM_INTERACT_BLOCKING if(LAZYACCESS(modifiers, RIGHT_CLICK)) // Right click if(paint_kit.editing_mod == src) @@ -341,7 +346,7 @@ return ITEM_INTERACT_SUCCESS if(istype(tool, /obj/item/mod/module)) if(!open) - balloon_alert(user, "open the cover first!") + balloon_alert(user, "cover closed!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return ITEM_INTERACT_BLOCKING install(tool, user) @@ -349,11 +354,11 @@ return ITEM_INTERACT_SUCCESS if(istype(tool, /obj/item/mod/core)) if(!open) - balloon_alert(user, "open the cover first!") + balloon_alert(user, "cover closed!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return ITEM_INTERACT_BLOCKING if(core) - balloon_alert(user, "core already installed!") + balloon_alert(user, "already has core!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return ITEM_INTERACT_BLOCKING var/obj/item/mod/core/attacking_core = tool @@ -385,7 +390,7 @@ /obj/item/mod/control/emag_act(mob/user, obj/item/card/emag/emag_card) locked = !locked - balloon_alert(user, "suit access [locked ? "locked" : "unlocked"]") + balloon_alert(user, "access [locked ? "locked" : "unlocked"]") return TRUE /obj/item/mod/control/emp_act(severity) @@ -445,12 +450,11 @@ CRASH("get_part_datum called with incorrect item [part] passed.") /obj/item/mod/control/proc/get_part_from_slot(slot) - slot = "[slot]" - for(var/part_slot in mod_parts) - if(slot != part_slot) - continue - var/datum/mod_part/part = mod_parts[part_slot] - return part.part_item + var/datum/mod_part/part = mod_parts["[slot]"] + return part?.part_item + +/obj/item/mod/control/proc/get_part_datum_from_slot(slot) + return mod_parts["[slot]"] /obj/item/mod/control/proc/set_wearer(mob/living/carbon/human/user) if(wearer == user) @@ -511,7 +515,7 @@ for(var/obj/item/part as anything in get_parts()) seal_part(part, is_sealed = FALSE) for(var/obj/item/part as anything in get_parts()) - retract(null, part) + INVOKE_ASYNC(src, PROC_REF(retract), wearer, part, /* instant = */ TRUE) // async to appease spaceman DMM because the branch we don't run has a do_after if(active) control_activation(is_on = FALSE) mod_link?.end_call() @@ -579,24 +583,24 @@ for(var/obj/item/mod/module/old_module as anything in modules) if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules)) if(user) - balloon_alert(user, "[new_module] incompatible with [old_module]!") + balloon_alert(user, "incompatible with [old_module]!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return var/complexity_with_module = complexity complexity_with_module += new_module.complexity if(complexity_with_module > complexity_max) if(user) - balloon_alert(user, "[new_module] would make [src] too complex!") + balloon_alert(user, "above complexity max!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return if(!new_module.has_required_parts(mod_parts)) if(user) - balloon_alert(user, "[new_module] incompatible with [src]'s parts!") + balloon_alert(user, "lacking required parts!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return if(!new_module.can_install(src)) if(user) - balloon_alert(user, "[new_module] cannot be installed into [src]!") + balloon_alert(user, "can't install!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return new_module.forceMove(src) @@ -661,6 +665,12 @@ /obj/item/mod/control/proc/check_charge(amount) return core?.check_charge(amount) || FALSE +/obj/item/mod/control/proc/get_chargebar_color() + return core?.get_chargebar_color() || "transparent" + +/obj/item/mod/control/proc/get_chargebar_string() + return core?.get_chargebar_string() || "No Core Detected" + /** * Updates the wearer's hud according to the current state of the MODsuit */ @@ -706,6 +716,9 @@ uninstall(part) return if(part in get_parts()) + if(QDELING(part) && !QDELING(src)) + qdel(src) + return var/datum/mod_part/part_datum = get_part_datum(part) if(part_datum.sealed) seal_part(part, is_sealed = FALSE) @@ -714,7 +727,7 @@ if(!wearer) part.forceMove(src) return - retract(wearer, part) + INVOKE_ASYNC(src, PROC_REF(retract), wearer, part, /* instant = */ TRUE) // async to appease spaceman DMM because the branch we don't run has a do_after /obj/item/mod/control/proc/on_part_destruction(obj/item/part, damage_flag) SIGNAL_HANDLER @@ -723,14 +736,6 @@ return atom_destruction(damage_flag) -/obj/item/mod/control/proc/on_part_deletion(obj/item/part) - SIGNAL_HANDLER - - if(QDELING(src)) - return - part.moveToNullspace() - qdel(src) - /obj/item/mod/control/proc/on_overslot_exit(obj/item/part, atom/movable/overslot, direction) SIGNAL_HANDLER diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm index 79a8eff5e29..01e0902b6b0 100644 --- a/code/modules/mod/mod_core.dm +++ b/code/modules/mod/mod_core.dm @@ -25,30 +25,45 @@ mod.update_charge_alert() mod = null +/// Returns the item responsible for charging the suit, like a power cell, an ethereal's stomach, the core itself, etc. /obj/item/mod/core/proc/charge_source() return +/// Returns the amount of charge in the core. /obj/item/mod/core/proc/charge_amount() return 0 +/// Returns the max amount of charge stored in the core. /obj/item/mod/core/proc/max_charge_amount() return 1 +/// Adds a set amount of charge to the core. /obj/item/mod/core/proc/add_charge(amount) return FALSE +/// Subtracts a set amount of charge from the core. /obj/item/mod/core/proc/subtract_charge(amount) return FALSE +/// Checks if there's enough charge in the core to use an amount of energy. /obj/item/mod/core/proc/check_charge(amount) return FALSE -/** - * Gets what icon state to display on the HUD for the charge level of this core - */ +/// Returns what icon state to display on the HUD for the charge level of this core /obj/item/mod/core/proc/get_charge_icon_state() return "0" +/// Gets what the UI should use for the charge bar color. +/obj/item/mod/core/proc/get_chargebar_color() + return "bad" + +/// Gets what the UI should use for the charge bar text. +/obj/item/mod/core/proc/get_chargebar_string() + var/charge_amount = charge_amount() + var/max_charge_amount = max_charge_amount() + return "[display_energy(charge_amount)] of [display_energy(max_charge_amount())] \ + ([round((100 * charge_amount) / max_charge_amount, 1)]%)" + /obj/item/mod/core/infinite name = "MOD infinite core" icon_state = "mod-core-infinite" @@ -76,6 +91,12 @@ /obj/item/mod/core/infinite/get_charge_icon_state() return "high" +/obj/item/mod/core/infinite/get_chargebar_color() + return "teal" + +/obj/item/mod/core/infinite/get_chargebar_string() + return "Infinite" + /obj/item/mod/core/standard name = "MOD standard core" icon_state = "mod-core-standard" @@ -163,6 +184,22 @@ return "empty" +/obj/item/mod/core/standard/get_chargebar_color() + if(isnull(charge_source())) + return "transparent" + switch(round(charge_amount() / max_charge_amount(), 0.01)) + if(-INFINITY to 0.33) + return "bad" + if(0.33 to 0.66) + return "average" + if(0.66 to INFINITY) + return "good" + +/obj/item/mod/core/standard/get_chargebar_string() + if(isnull(charge_source())) + return "Power Cell Missing" + return ..() + /obj/item/mod/core/standard/proc/install_cell(new_cell) cell = new_cell cell.forceMove(src) @@ -222,11 +259,11 @@ if(!istype(attacking_item, /obj/item/stock_parts/power_store/cell)) return FALSE if(!mod.open) - mod.balloon_alert(user, "open the cover first!") + mod.balloon_alert(user, "cover closed!") playsound(mod, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(cell) - mod.balloon_alert(user, "cell already installed!") + mod.balloon_alert(user, "already has cell!") playsound(mod, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE install_cell(attacking_item) @@ -260,7 +297,9 @@ name = "MOD ethereal core" icon_state = "mod-core-ethereal" desc = "A reverse engineered core of a Modular Outerwear Device. Using natural liquid electricity from Ethereals, \ - preventing the need to use external sources to convert electric charge." + preventing the need to use external sources to convert electric charge. As the suits are naturally charged by \ + liquid electricity, this core makes it much more efficient, running all soft, hard, and wetware with several \ + times less energy usage." /// A modifier to all charge we use, ethereals don't need to spend as much energy as normal suits. var/charge_modifier = 0.1 @@ -279,22 +318,41 @@ /obj/item/mod/core/ethereal/add_charge(amount) var/obj/item/organ/stomach/ethereal/charge_source = charge_source() - if(!charge_source) + if(isnull(charge_source)) return FALSE - charge_source.adjust_charge(amount*charge_modifier) + charge_source.adjust_charge(amount * charge_modifier) return TRUE /obj/item/mod/core/ethereal/subtract_charge(amount) var/obj/item/organ/stomach/ethereal/charge_source = charge_source() - if(!charge_source) + if(isnull(charge_source)) return FALSE - return -charge_source.adjust_charge(-amount*charge_modifier) + return -charge_source.adjust_charge(-amount * charge_modifier) /obj/item/mod/core/ethereal/check_charge(amount) - return charge_amount() >= amount*charge_modifier + return charge_amount() >= amount * charge_modifier /obj/item/mod/core/ethereal/get_charge_icon_state() - return charge_source() ? "0" : "missing" + return isnull(charge_source()) ? "missing" : "0" + +/obj/item/mod/core/ethereal/get_chargebar_color() + if(isnull(charge_source())) + return "transparent" + switch(charge_amount()) + if(-INFINITY to ETHEREAL_CHARGE_LOWPOWER) + return "bad" + if(ETHEREAL_CHARGE_LOWPOWER to ETHEREAL_CHARGE_NORMAL) + return "average" + if(ETHEREAL_CHARGE_NORMAL to ETHEREAL_CHARGE_FULL) + return "good" + if(ETHEREAL_CHARGE_FULL to INFINITY) + return "teal" + +/obj/item/mod/core/ethereal/get_chargebar_string() + var/obj/item/organ/stomach/ethereal/charge_source = charge_source() + if(isnull(charge_source()) || isnull(charge_source.cell)) + return "Biological Battery Missing" + return ..() #define PLASMA_CORE_ORE_CHARGE (1.5 * STANDARD_CELL_CHARGE) #define PLASMA_CORE_SHEET_CHARGE (2 * STANDARD_CELL_CHARGE) @@ -355,6 +413,13 @@ return "empty" +/obj/item/mod/core/plasma/get_chargebar_color() + switch(round(charge_amount() / max_charge_amount(), 0.01)) + if(-INFINITY to 0.33) + return "bad" + if(0.33 to INFINITY) + return "purple" + /obj/item/mod/core/plasma/proc/on_mod_interaction(datum/source, mob/living/user, obj/item/thing) SIGNAL_HANDLER diff --git a/code/modules/mod/mod_link.dm b/code/modules/mod/mod_link.dm index e7a5a20d9f3..a93ebec8b57 100644 --- a/code/modules/mod/mod_link.dm +++ b/code/modules/mod/mod_link.dm @@ -213,7 +213,7 @@ if(!user.transferItemToLoc(attacked_by, src)) return cell = attacked_by - balloon_alert(user, "installed [cell.name]") + balloon_alert(user, "cell installed") /obj/item/clothing/neck/link_scryer/update_name(updates) . = ..() @@ -227,7 +227,7 @@ /obj/item/clothing/neck/link_scryer/attack_hand_secondary(mob/user, list/modifiers) if(!cell) return SECONDARY_ATTACK_CONTINUE_CHAIN - balloon_alert(user, "removed [cell.name]") + balloon_alert(user, "cell removed") user.put_in_hands(cell) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN @@ -409,7 +409,7 @@ if(!link_user) return if(HAS_TRAIT(link_user, TRAIT_IN_CALL)) - holder.balloon_alert(user, "user already in call!") + holder.balloon_alert(user, "already calling!") return var/mob/living/link_target = called.get_user_callback.Invoke() if(!link_target) @@ -511,6 +511,7 @@ desc = "Someone is calling you! Left-click this to accept the call. Right-click to deny it." icon_state = "called" timeout = 10 SECONDS + clickable_glow = TRUE var/end_message = "call timed out!" /// A weak reference to the MODlink that is calling. var/datum/weakref/caller_ref diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm index e3e42e55f61..2f658e9df24 100644 --- a/code/modules/mod/mod_paint.dm +++ b/code/modules/mod/mod_paint.dm @@ -153,10 +153,10 @@ return NONE var/obj/item/mod/control/mod = attacked_atom if(mod.active || mod.activating) - balloon_alert(user, "suit is active!") + balloon_alert(user, "unit active!") return ITEM_INTERACT_BLOCKING if(!(skin in mod.theme.variants)) - balloon_alert(user, "incompatible theme!") + balloon_alert(user, "wrong theme for skin!") return ITEM_INTERACT_BLOCKING mod.theme.set_skin(mod, skin) balloon_alert(user, "skin applied") diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm index 2a8ccf7b4bf..9a8e77f7e8e 100644 --- a/code/modules/mod/mod_ui.dm +++ b/code/modules/mod/mod_ui.dm @@ -9,8 +9,10 @@ // Suit information var/suit_status = list( "core_name" = core?.name, - "cell_charge_current" = get_charge(), - "cell_charge_max" = get_max_charge(), + "charge_current" = get_charge(), + "charge_max" = get_max_charge(), + "chargebar_color" = get_chargebar_color(), + "chargebar_string" = get_chargebar_string(), "active" = active, "ai_name" = ai_assistant?.name, "has_pai" = ispAI(ai_assistant), @@ -56,23 +58,25 @@ "configuration_data" = module.get_configuration(user), )) data["module_custom_status"] = module_custom_status - data["module_info"] = module_info - return data - -/obj/item/mod/control/ui_static_data(mob/user) - var/data = list() - data["ui_theme"] = ui_theme data["control"] = name - data["complexity_max"] = complexity_max + data["module_info"] = module_info var/part_info = list() for(var/obj/item/part as anything in get_parts()) part_info += list(list( "slot" = english_list(parse_slot_flags(part.slot_flags)), "name" = part.name, + "deployed" = part.loc != src, + "ref" = REF(part), )) data["parts"] = part_info return data +/obj/item/mod/control/ui_static_data(mob/user) + var/data = list() + data["ui_theme"] = ui_theme + data["complexity_max"] = complexity_max + return data + /obj/item/mod/control/ui_state(mob/user) if(user == ai_assistant) return GLOB.contained_state @@ -89,7 +93,7 @@ if("lock") if(!locked || allowed(ui.user)) locked = !locked - balloon_alert(ui.user, "[locked ? "locked" : "unlocked"]!") + balloon_alert(ui.user, "[locked ? "locked" : "unlocked"]") else balloon_alert(ui.user, "access insufficent!") playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) @@ -115,6 +119,14 @@ if(!module) return module.pin(ui.user) + if("deploy") + var/obj/item/mod_part = locate(params["ref"]) in get_parts() + if(!mod_part) + return + if(mod_part.loc == src) + deploy(ui.user, mod_part) + else + retract(ui.user, mod_part) if("eject_pai") if (!ishuman(ui.user)) return diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index 28a588609a7..565919c07ec 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -300,7 +300,7 @@ /obj/item/mod/module/proc/add_ui_data() return list() -/// Creates a list of configuring options for this module +/// Creates a list of configuring options for this module, possible configs include number, bool, color, list, button. /obj/item/mod/module/proc/get_configuration(mob/user) return list() @@ -418,7 +418,7 @@ /obj/item/mod/module/anomaly_locked name = "MOD anomaly locked module" desc = "A form of a module, locked behind an anomalous core to function." - incompatible_modules = list(/obj/item/mod/module/anomaly_locked) + incompatible_modules = list() /// The core item the module runs off. var/obj/item/assembly/signaler/anomaly/core /// Accepted types of anomaly cores. @@ -491,7 +491,7 @@ balloon_alert(user, "no core!") return if(!core_removable) - balloon_alert(user, "can't remove core!") + balloon_alert(user, "already has core!") return balloon_alert(user, "removing core...") if(!do_after(user, 3 SECONDS, target = src)) diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm index 3c9ae3310b7..733e5ab40d9 100644 --- a/code/modules/mod/modules/module_kinesis.dm +++ b/code/modules/mod/modules/module_kinesis.dm @@ -17,7 +17,7 @@ accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) required_slots = list(ITEM_SLOT_GLOVES) /// Range of the knesis grab. - var/grab_range = 5 + var/grab_range = 8 /// Time between us hitting objects with kinesis. var/hit_cooldown_time = 1 SECONDS /// Stat required for us to grab a mob. diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm index f0a92e3a051..487844bc2a0 100644 --- a/code/modules/mod/modules/module_pathfinder.dm +++ b/code/modules/mod/modules/module_pathfinder.dm @@ -117,19 +117,19 @@ /obj/item/implant/mod/proc/recall() if(!module?.mod) - balloon_alert(imp_in, "no connected suit!") + balloon_alert(imp_in, "no connected unit!") return FALSE if(module.mod.open) - balloon_alert(imp_in, "suit is open!") + balloon_alert(imp_in, "cover open!") return FALSE if(module.mod.ai_controller) - balloon_alert(imp_in, "already in transit!") + balloon_alert(imp_in, "already moving!") return FALSE if(ismob(get_atom_on_turf(module.mod))) balloon_alert(imp_in, "already on someone!") return FALSE if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE) - balloon_alert(imp_in, "too far away!") + balloon_alert(imp_in, "too far!") return FALSE var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod) module.mod.ai_controller = mod_ai diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm index 0401dbfdbe9..ec550f3cfde 100644 --- a/code/modules/mod/modules/modules_antag.dm +++ b/code/modules/mod/modules/modules_antag.dm @@ -52,9 +52,10 @@ playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) balloon_alert(mod.wearer, "armor boosted, EVA lost") actual_speed_added = max(0, min(mod.slowdown_active, speed_added)) - var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES) - if(istype(head_cover)) - ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) + var/datum/mod_part/head_cover = mod.get_part_datum_from_slot(ITEM_SLOT_HEAD) || mod.get_part_datum_from_slot(ITEM_SLOT_MASK) || mod.get_part_datum_from_slot(ITEM_SLOT_EYES) + if(head_cover) + RegisterSignal(mod, COMSIG_MOD_PART_SEALED, PROC_REF(seal_helmet)) + seal_helmet(mod, head_cover) var/list/mod_parts = mod.get_parts(all = TRUE) for(var/obj/item/part as anything in mod.get_parts(all = TRUE)) part.set_armor(part.get_armor().add_other_armor(armor_mod)) @@ -71,9 +72,10 @@ if(!deleting) playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) balloon_alert(mod.wearer, "armor retracts, EVA ready") - var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES) - if(istype(head_cover)) - REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) + var/datum/mod_part/head_cover = mod.get_part_datum_from_slot(ITEM_SLOT_HEAD) || mod.get_part_datum_from_slot(ITEM_SLOT_MASK) || mod.get_part_datum_from_slot(ITEM_SLOT_EYES) + if(head_cover) + UnregisterSignal(mod, COMSIG_MOD_PART_SEALED) + REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src)) var/list/mod_parts = mod.get_parts(all = TRUE) for(var/obj/item/part as anything in mod.get_parts(all = TRUE)) part.set_armor(part.get_armor().subtract_other_armor(armor_mod)) @@ -91,6 +93,15 @@ overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]" return ..() +/obj/item/mod/module/armor_booster/proc/seal_helmet(datum/source, datum/mod_part/part) + var/datum/mod_part/head_cover = mod.get_part_datum_from_slot(ITEM_SLOT_HEAD) || mod.get_part_datum_from_slot(ITEM_SLOT_MASK) || mod.get_part_datum_from_slot(ITEM_SLOT_EYES) + if(part != head_cover) + return + if(part.sealed) + ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src)) + else + REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src)) + ///Energy Shield - Gives you a rechargeable energy shield that nullifies attacks. /obj/item/mod/module/energy_shield name = "MOD energy shield module" @@ -184,10 +195,10 @@ required_slots = list(ITEM_SLOT_BACK) /obj/item/mod/module/anti_magic/on_part_activation() - mod.wearer.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), REF(src)) /obj/item/mod/module/anti_magic/on_part_deactivation(deleting = FALSE) - mod.wearer.remove_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), REF(src)) /obj/item/mod/module/anti_magic/wizard name = "MOD magic neutralizer module" @@ -199,10 +210,10 @@ required_slots = list() /obj/item/mod/module/anti_magic/wizard/on_part_activation() - mod.wearer.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_ANTIMAGIC_NO_SELFBLOCK), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_ANTIMAGIC_NO_SELFBLOCK), REF(src)) /obj/item/mod/module/anti_magic/wizard/on_part_deactivation(deleting = FALSE) - mod.wearer.remove_traits(list(TRAIT_ANTIMAGIC, TRAIT_ANTIMAGIC_NO_SELFBLOCK), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_ANTIMAGIC, TRAIT_ANTIMAGIC_NO_SELFBLOCK), REF(src)) ///Insignia - Gives you a skin specific stripe. /obj/item/mod/module/insignia @@ -262,24 +273,20 @@ required_slots = list(ITEM_SLOT_FEET) /obj/item/mod/module/noslip/on_part_activation() - ADD_TRAIT(mod.wearer, TRAIT_NO_SLIP_WATER, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_NO_SLIP_WATER, REF(src)) /obj/item/mod/module/noslip/on_part_deactivation(deleting = FALSE) - REMOVE_TRAIT(mod.wearer, TRAIT_NO_SLIP_WATER, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_NO_SLIP_WATER, REF(src)) //Bite of 87 Springlock - Equips faster, disguised as DNA lock. /obj/item/mod/module/springlock/bite_of_87 + name = /obj/item/mod/module/dna_lock::name + desc = /obj/item/mod/module/dna_lock::desc + icon_state = /obj/item/mod/module/dna_lock::icon_state + complexity = /obj/item/mod/module/dna_lock::complexity + use_energy_cost = /obj/item/mod/module/dna_lock::use_energy_cost step_change = 0.1 -/obj/item/mod/module/springlock/bite_of_87/Initialize(mapload) - . = ..() - var/obj/item/mod/module/dna_lock/the_dna_lock_behind_the_slaughter = /obj/item/mod/module/dna_lock - name = initial(the_dna_lock_behind_the_slaughter.name) - desc = initial(the_dna_lock_behind_the_slaughter.desc) - icon_state = initial(the_dna_lock_behind_the_slaughter.icon_state) - complexity = initial(the_dna_lock_behind_the_slaughter.complexity) - use_energy_cost = initial(the_dna_lock_behind_the_slaughter.use_energy_cost) - /obj/item/mod/module/springlock/bite_of_87/on_part_activation() ..() if(check_holidays(APRIL_FOOLS) || prob(1)) @@ -404,7 +411,7 @@ /obj/item/mod/module/chameleon/used() if(mod.active || mod.activating) - balloon_alert(mod.wearer, "suit active!") + balloon_alert(mod.wearer, "unit active!") return FALSE return ..() @@ -514,7 +521,7 @@ mod.item_flags &= ~EXAMINE_SKIP /obj/item/mod/module/infiltrator/on_part_activation() - mod.wearer.add_traits(traits_to_add, MOD_TRAIT) + mod.wearer.add_traits(traits_to_add, REF(src)) RegisterSignal(mod.wearer, COMSIG_TRY_MODIFY_SPEECH, PROC_REF(on_speech_modification)) var/obj/item/organ/tongue/user_tongue = mod.wearer.get_organ_slot(ORGAN_SLOT_TONGUE) user_tongue.temp_say_mod = "states" @@ -523,7 +530,7 @@ head_cover.flash_protect = FLASH_PROTECTION_WELDER_HYPER_SENSITIVE /obj/item/mod/module/infiltrator/on_part_deactivation(deleting = FALSE) - mod.wearer.remove_traits(traits_to_add, MOD_TRAIT) + mod.wearer.remove_traits(traits_to_add, REF(src)) UnregisterSignal(mod.wearer, COMSIG_TRY_MODIFY_SPEECH) var/obj/item/organ/tongue/user_tongue = mod.wearer.get_organ_slot(ORGAN_SLOT_TONGUE) user_tongue.temp_say_mod = initial(user_tongue.temp_say_mod) @@ -573,7 +580,7 @@ balloon_alert(mod.wearer, "can't reach that!") return if(istype(target, /obj/machinery/power/apc)) //Bit too strong for a module so this is blacklisted - balloon_alert(mod.wearer, "cant disable apc!") + balloon_alert(mod.wearer, "can't disable apc!") return var/list/things_to_disrupt = list(target) diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm index aea9a415cf4..9c96d9edd82 100644 --- a/code/modules/mod/modules/modules_engineering.dm +++ b/code/modules/mod/modules/modules_engineering.dm @@ -62,12 +62,12 @@ var/list/active_traits = list(TRAIT_NO_SLIP_WATER, TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE, TRAIT_NEGATES_GRAVITY) /obj/item/mod/module/magboot/on_activation() - mod.wearer.add_traits(active_traits, MOD_TRAIT) + mod.wearer.add_traits(active_traits, REF(src)) mod.slowdown += slowdown_active mod.wearer.update_equipment_speed_mods() /obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE) - mod.wearer.remove_traits(active_traits, MOD_TRAIT) + mod.wearer.remove_traits(active_traits, REF(src)) mod.slowdown -= slowdown_active mod.wearer.update_equipment_speed_mods() @@ -104,7 +104,7 @@ /obj/item/mod/module/tether/get_configuration() . = ..() - .["cut_tethers"] = add_ui_configuration("Cut Tethers", "pin", TRUE) + .["cut_tethers"] = add_ui_configuration("Cut Tethers", "button", "scissors") /obj/item/mod/module/tether/configure_edit(key, value) if (key != "cut_tethers") @@ -267,14 +267,14 @@ /obj/item/mod/module/rad_protection/on_part_activation() AddComponent(/datum/component/geiger_sound) - ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, REF(src)) RegisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation)) for(var/obj/item/part in mod.get_parts(all = TRUE)) ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) /obj/item/mod/module/rad_protection/on_part_deactivation(deleting = FALSE) qdel(GetComponent(/datum/component/geiger_sound)) - REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, REF(src)) UnregisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION) for(var/obj/item/part in mod.get_parts(all = TRUE)) REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) @@ -309,10 +309,10 @@ required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/constructor/on_part_activation() - ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, REF(src)) /obj/item/mod/module/constructor/on_part_deactivation(deleting = FALSE) - REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, REF(src)) /obj/item/mod/module/constructor/on_use() rcd_scan(src, fade_time = 10 SECONDS) @@ -332,10 +332,10 @@ required_slots = list(ITEM_SLOT_HEAD) /obj/item/mod/module/headprotector/on_part_activation() - ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src)) /obj/item/mod/module/headprotector/on_part_deactivation(deleting = FALSE) - REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src)) ///Mister - Sprays water over an area. /obj/item/mod/module/mister diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 14fc328aed7..d1817e5170b 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -148,9 +148,9 @@ if (!isnull(mod) && !isnull(mod.wearer) && mod.wearer.get_item_by_slot(slot_flags) == src) if (!stabilize) - ADD_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, REF(src)) else - REMOVE_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, REF(src)) /obj/item/mod/module/jetpack/get_configuration() . = ..() @@ -171,11 +171,11 @@ /obj/item/mod/module/jetpack/on_activation() mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/full_speed) if (!stabilize) - ADD_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, REF(src)) /obj/item/mod/module/jetpack/on_deactivation(display_message = TRUE, deleting = FALSE) mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/full_speed) - REMOVE_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_NOGRAV_ALWAYS_DRIFT, REF(src)) /obj/item/mod/module/jetpack/advanced name = "MOD advanced ion jetpack module" @@ -672,10 +672,10 @@ return ..() /obj/item/mod/module/plasma_stabilizer/on_equip() - ADD_TRAIT(mod.wearer, TRAIT_HEAD_ATMOS_SEALED, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_HEAD_ATMOS_SEALED, REF(src)) /obj/item/mod/module/plasma_stabilizer/on_unequip() - REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_ATMOS_SEALED, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_ATMOS_SEALED, REF(src)) //Finally, https://pipe.miroware.io/5b52ba1d94357d5d623f74aa/mspfa/Nuke%20Ops/Panels/0648.gif can be real: @@ -731,10 +731,10 @@ required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/signlang_radio/on_part_activation() - ADD_TRAIT(mod.wearer, TRAIT_CAN_SIGN_ON_COMMS, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_CAN_SIGN_ON_COMMS, REF(src)) /obj/item/mod/module/signlang_radio/on_part_deactivation(deleting = FALSE) - REMOVE_TRAIT(mod.wearer, TRAIT_CAN_SIGN_ON_COMMS, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_CAN_SIGN_ON_COMMS, REF(src)) ///A module that recharges the suit by an itsy tiny bit whenever the user takes a step. Originally called "magneto module" but the videogame reference sounds cooler. /obj/item/mod/module/joint_torsion @@ -926,7 +926,7 @@ icon_state = "fishing_glove" complexity = 1 overlay_state_inactive = "fishing_glove" - incompatible_modules = (/obj/item/mod/module/fishing_glove) + incompatible_modules = list(/obj/item/mod/module/fishing_glove) required_slots = list(ITEM_SLOT_GLOVES) var/obj/item/fishing_rod/equipped @@ -952,7 +952,7 @@ if(!istype(tool, /obj/item/fishing_rod)) return ..() if(equipped) - balloon_alert(user, "remove current rod first!") + balloon_alert(user, "already has rod!") if(!user.transferItemToLoc(tool, src)) user.balloon_alert(user, "it's stuck!") equipped = tool @@ -985,7 +985,7 @@ return gloves.AddComponent(/datum/component/adjust_fishing_difficulty, 5) if(equipped) - gloves.AddComponent(/datum/component/profound_fisher, equipped) + gloves.AddComponent(/datum/component/profound_fisher, equipped, delete_rod_when_deleted = FALSE) /obj/item/mod/module/fishing_glove/on_part_deactivation(deleting = FALSE) var/obj/item/gloves = mod.get_part_from_slot(ITEM_SLOT_GLOVES) diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index f6b978d0535..cd5a575302d 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -295,8 +295,8 @@ mod.wearer.AddElement(/datum/element/forced_gravity, NEGATIVE_GRAVITY) RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(check_upstairs)) RegisterSignal(mod.wearer, COMSIG_MOB_SAY, PROC_REF(on_talk)) - ADD_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, MOD_TRAIT) - passtable_on(mod.wearer, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, REF(src)) + passtable_on(mod.wearer, REF(src)) check_upstairs() //todo at some point flip your screen around /obj/item/mod/module/atrocinator/deactivate(display_message = TRUE, deleting = FALSE) @@ -312,8 +312,8 @@ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) UnregisterSignal(mod.wearer, COMSIG_MOB_SAY) step_count = 0 - REMOVE_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, MOD_TRAIT) - passtable_off(mod.wearer, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, REF(src)) + passtable_off(mod.wearer, REF(src)) var/turf/open/openspace/current_turf = get_turf(mod.wearer) if(istype(current_turf)) current_turf.zFall(mod.wearer, falling_from_move = TRUE) diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm index 154717cbcc1..7c4650ff81a 100644 --- a/code/modules/mod/modules/modules_medical.dm +++ b/code/modules/mod/modules/modules_medical.dm @@ -77,13 +77,13 @@ /obj/item/mod/module/quick_carry/on_part_activation() . = ..() - ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) - ADD_TRAIT(mod.wearer, quick_carry_trait, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_FASTMED, REF(src)) + ADD_TRAIT(mod.wearer, quick_carry_trait, REF(src)) /obj/item/mod/module/quick_carry/on_part_deactivation(deleting = FALSE) . = ..() - REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) - REMOVE_TRAIT(mod.wearer, quick_carry_trait, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, REF(src)) + REMOVE_TRAIT(mod.wearer, quick_carry_trait, REF(src)) /obj/item/mod/module/quick_carry/advanced name = "MOD advanced quick carry module" @@ -200,13 +200,10 @@ var/mob/living/carbon/human/organ_receiver = target var/succeed = FALSE if(organ_receiver.surgeries.len) - for(var/datum/surgery/procedure as anything in organ_receiver.surgeries) + for(var/datum/surgery/organ_manipulation/procedure in organ_receiver.surgeries) if(procedure.location != organ.zone) continue - if(!istype(procedure, /datum/surgery/organ_manipulation)) - continue - var/datum/surgery_step/surgery_step = procedure.get_surgery_step() - if(!istype(surgery_step, /datum/surgery_step/manipulate_organs)) + if(!ispath(procedure.steps[procedure.status], /datum/surgery_step/manipulate_organs)) continue succeed = TRUE break diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm index c4e8962c447..a6ade06a909 100644 --- a/code/modules/mod/modules/modules_ninja.dm +++ b/code/modules/mod/modules/modules_ninja.dm @@ -73,11 +73,11 @@ /obj/item/mod/module/stealth/ninja/on_activation() . = ..() - ADD_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, REF(src)) /obj/item/mod/module/stealth/ninja/on_deactivation(display_message = TRUE, deleting = FALSE) . = ..() - REMOVE_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_SILENT_FOOTSTEPS, REF(src)) ///Camera Vision - Prevents flashes, blocks tracking. /obj/item/mod/module/welding/camera_vision @@ -173,16 +173,16 @@ var/accepted_type = /obj/item/energy_katana /obj/item/mod/module/weapon_recall/on_part_activation() - mod.wearer.add_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), REF(src)) /obj/item/mod/module/weapon_recall/on_part_deactivation(deleting = FALSE) - mod.wearer.remove_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), REF(src)) /obj/item/mod/module/weapon_recall/on_use() if(!linked_weapon) var/obj/item/weapon_to_link = mod.wearer.is_holding_item_of_type(accepted_type) if(!weapon_to_link) - balloon_alert(mod.wearer, "can't locate weapon!") + balloon_alert(mod.wearer, "no linked weapon!") return set_weapon(weapon_to_link) balloon_alert(mod.wearer, "[linked_weapon.name] linked") @@ -191,7 +191,7 @@ balloon_alert(mod.wearer, "already on self!") return var/distance = get_dist(mod.wearer, linked_weapon) - var/in_view = (linked_weapon in view(mod.wearer)) + var/in_view = (linked_weapon in view(mod.wearer)) && !(linked_weapon in get_turf(mod.wearer)) if(!in_view && !drain_power(use_energy_cost * distance)) balloon_alert(mod.wearer, "not enough charge!") return diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm index 409b4423366..8cb15d35370 100644 --- a/code/modules/mod/modules/modules_science.dm +++ b/code/modules/mod/modules/modules_science.dm @@ -14,10 +14,10 @@ required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /obj/item/mod/module/reagent_scanner/on_activation() - ADD_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, REF(src)) /obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE) - REMOVE_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, REF(src)) /obj/item/mod/module/reagent_scanner/advanced name = "MOD advanced reagent scanner module" @@ -31,12 +31,12 @@ /obj/item/mod/module/reagent_scanner/advanced/on_activation() . = ..() - ADD_TRAIT(mod.wearer, TRAIT_RESEARCH_SCANNER, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_RESEARCH_SCANNER, REF(src)) RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, PROC_REF(sense_explosion)) /obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE) . = ..() - REMOVE_TRAIT(mod.wearer, TRAIT_RESEARCH_SCANNER, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_RESEARCH_SCANNER, REF(src)) UnregisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION) /obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter, @@ -55,9 +55,9 @@ desc = "A module that uses a gravitational core to make the user completely weightless." icon_state = "antigrav" module_type = MODULE_TOGGLE - complexity = 3 + complexity = 2 active_power_cost = DEFAULT_CHARGE_DRAIN * 0.7 - incompatible_modules = list(/obj/item/mod/module/anomaly_locked, /obj/item/mod/module/atrocinator) + incompatible_modules = list(/obj/item/mod/module/atrocinator, /obj/item/mod/module/anomaly_locked/antigrav) accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) @@ -88,21 +88,34 @@ icon_state = "teleporter" module_type = MODULE_ACTIVE complexity = 3 - use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 - cooldown_time = 5 SECONDS + use_energy_cost = DEFAULT_CHARGE_DRAIN * 25 + cooldown_time = 4 SECONDS + incompatible_modules = list(/obj/item/mod/module/anomaly_locked/teleporter) accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace) required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// Time it takes to teleport - var/teleport_time = 3 SECONDS + var/teleport_time = 1 SECONDS + /// Maximum turf range + var/max_range = 9 /obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target) . = ..() if(!.) return var/turf/open/target_turf = get_turf(target) - if(!istype(target_turf) || target_turf.is_blocked_turf_ignore_climbable() || !(target_turf in view(mod.wearer))) + if(get_dist(target_turf, mod.wearer) > max_range) + balloon_alert(mod.wearer, "too far!") + return + if(!istype(target_turf)) balloon_alert(mod.wearer, "invalid target!") return + if(target_turf.is_blocked_turf_ignore_climbable() || !los_check(mod.wearer, target, pass_args = PASSTABLE|PASSGLASS|PASSGRILLE|PASSMOB|PASSMACHINE|PASSSTRUCTURE|PASSFLAPS|PASSWINDOW)) + balloon_alert(mod.wearer, "blocked destination!") + return + // check early so we don't go through the whole loops + if(!check_teleport_valid(mod.wearer, target_turf, channel = TELEPORT_CHANNEL_BLUESPACE, original_destination = target_turf)) + balloon_alert(mod.wearer, "something holds you back!") + return balloon_alert(mod.wearer, "teleporting...") var/matrix/pre_matrix = matrix() pre_matrix.Scale(4, 0.25) diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index c25dd752755..09032318304 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -123,7 +123,7 @@ balloon_alert(mod.wearer, "nothing to holster!") return if(!istype(holding) || holding.w_class > WEIGHT_CLASS_BULKY) - balloon_alert(mod.wearer, "it doesn't fit!") + balloon_alert(mod.wearer, "doesn't fit!") return if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE)) holstered = holding @@ -522,11 +522,11 @@ idle_power_cost = 0 if(STORMTROOPER_MODE) idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.4 - mod.wearer.add_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_DOUBLE_TAP), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_DOUBLE_TAP), REF(src)) RegisterSignal(mod.wearer, COMSIG_MOB_FIRED_GUN, PROC_REF(stormtrooper_fired_gun)) if(SHARPSHOOTER_MODE) idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.6 - mod.wearer.add_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_NICE_SHOT), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_NICE_SHOT), REF(src)) RegisterSignal(mod.wearer, COMSIG_MOB_FIRED_GUN, PROC_REF(sharpshooter_fired_gun)) RegisterSignal(mod.wearer, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, PROC_REF(apply_ricochet)) mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/shooting_assistant) @@ -535,10 +535,10 @@ switch(selected_mode) if(STORMTROOPER_MODE) UnregisterSignal(mod.wearer, COMSIG_MOB_FIRED_GUN) - mod.wearer.remove_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_DOUBLE_TAP), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_DOUBLE_TAP), REF(src)) if(SHARPSHOOTER_MODE) UnregisterSignal(mod.wearer, list(COMSIG_MOB_FIRED_GUN, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE)) - mod.wearer.remove_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_NICE_SHOT), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_NO_GUN_AKIMBO, TRAIT_NICE_SHOT), REF(src)) mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/shooting_assistant) /obj/item/mod/module/shooting_assistant/drain_power(amount) @@ -582,10 +582,10 @@ required_slots = list(ITEM_SLOT_OCLOTHING) /obj/item/mod/module/shove_blocker/on_part_activation() - mod.wearer.add_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), REF(src)) /obj/item/mod/module/shove_blocker/on_part_deactivation(deleting = FALSE) - mod.wearer.remove_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), REF(src)) /obj/item/mod/module/shove_blocker/locked name = "superglued MOD bulwark module" @@ -602,8 +602,8 @@ /obj/item/mod/module/quick_cuff/on_part_activation() . = ..() - ADD_TRAIT(mod.wearer, TRAIT_FAST_CUFFING, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_FAST_CUFFING, REF(src)) /obj/item/mod/module/quick_cuff/on_part_deactivation(deleting = FALSE) . = ..() - REMOVE_TRAIT(mod.wearer, TRAIT_FAST_CUFFING, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_FAST_CUFFING, REF(src)) diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm index 79d2d3779e0..70c11f069f0 100644 --- a/code/modules/mod/modules/modules_service.dm +++ b/code/modules/mod/modules/modules_service.dm @@ -46,7 +46,7 @@ if(!isitem(target)) return if(!isturf(target.loc)) - balloon_alert(mod.wearer, "must be on the floor!") + balloon_alert(mod.wearer, "not in storage!") return var/obj/item/microwave_target = target var/datum/effect_system/spark_spread/spark_effect = new() @@ -80,7 +80,7 @@ var/obj/item/shoes = mod.get_part_from_slot(ITEM_SLOT_FEET) if(shoes) shoes.AddComponent(/datum/component/squeak, list('sound/effects/footstep/clownstep1.ogg'=1,'sound/effects/footstep/clownstep2.ogg'=1), 50, falloff_exponent = 20) //die off quick please - mod.wearer.AddElementTrait(TRAIT_WADDLING, MOD_TRAIT, /datum/element/waddling) + mod.wearer.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling) if(is_clown_job(mod.wearer.mind?.assigned_role)) mod.wearer.add_mood_event("clownshoes", /datum/mood_event/clownshoes) @@ -88,6 +88,6 @@ var/obj/item/shoes = mod.get_part_from_slot(ITEM_SLOT_FEET) if(shoes && !deleting) qdel(shoes.GetComponent(/datum/component/squeak)) - REMOVE_TRAIT(mod.wearer, TRAIT_WADDLING, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_WADDLING, REF(src)) if(is_clown_job(mod.wearer.mind?.assigned_role)) mod.wearer.clear_mood_event("clownshoes") diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index 8be66e0b3e3..bc34cf8781e 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -61,7 +61,7 @@ return stored_crates += picked_crate picked_crate.forceMove(src) - balloon_alert(mod.wearer, "picked up [picked_crate]") + balloon_alert(mod.wearer, "picked up crate") drain_power(use_energy_cost) else if(length(stored_crates)) var/turf/target_turf = get_turf(target) @@ -407,11 +407,11 @@ )) /obj/item/mod/module/ash_accretion/on_part_activation() - mod.wearer.add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), MOD_TRAIT) + mod.wearer.add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), REF(src)) RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) /obj/item/mod/module/ash_accretion/on_part_deactivation(deleting = FALSE) - mod.wearer.remove_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), MOD_TRAIT) + mod.wearer.remove_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), REF(src)) UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) if(!traveled_tiles) return @@ -496,10 +496,10 @@ mod.wearer.base_pixel_y -= 4 animate(mod.wearer, animate_time, pixel_y = mod.wearer.base_pixel_y, flags = ANIMATION_PARALLEL) mod.wearer.SpinAnimation(1.5) - mod.wearer.add_traits(user_traits, MOD_TRAIT) + mod.wearer.add_traits(user_traits, REF(src)) mod.wearer.RemoveElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6) mod.wearer.AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE) - mod.wearer.add_movespeed_mod_immunities(MOD_TRAIT, /datum/movespeed_modifier/damage_slowdown) + mod.wearer.add_movespeed_mod_immunities(REF(src), /datum/movespeed_modifier/damage_slowdown) mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/sphere) RegisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE, PROC_REF(on_statchange)) @@ -509,8 +509,8 @@ mod.wearer.base_pixel_y += 4 animate(mod.wearer, animate_time, pixel_y = mod.wearer.base_pixel_y) addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/datum, remove_filter), list("mod_ball", "mod_blur", "mod_outline")), animate_time) - mod.wearer.remove_traits(user_traits, MOD_TRAIT) - mod.wearer.remove_movespeed_mod_immunities(MOD_TRAIT, /datum/movespeed_modifier/damage_slowdown) + mod.wearer.remove_traits(user_traits, REF(src)) + mod.wearer.remove_movespeed_mod_immunities(REF(src), /datum/movespeed_modifier/damage_slowdown) mod.wearer.RemoveElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE) mod.wearer.AddElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6) mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/sphere) diff --git a/code/modules/mod/modules/modules_timeline.dm b/code/modules/mod/modules/modules_timeline.dm index c14825bf3c7..bfb22da0c20 100644 --- a/code/modules/mod/modules/modules_timeline.dm +++ b/code/modules/mod/modules/modules_timeline.dm @@ -133,13 +133,13 @@ ///Signal fired when wearer attempts to trigger modules, if attempting while time is stopped /obj/item/mod/module/timestopper/proc/on_module_triggered(datum/source) SIGNAL_HANDLER - balloon_alert(mod.wearer, "not while channelling timestop!") + balloon_alert(mod.wearer, "not while stopping time!") return MOD_ABORT_USE ///Signal fired when wearer attempts to activate/deactivate suits, if attempting while time is stopped /obj/item/mod/module/timestopper/proc/on_activate_block(datum/source, user) SIGNAL_HANDLER - balloon_alert(user, "not while channelling timestop!") + balloon_alert(user, "not while stopping time!") return MOD_CANCEL_ACTIVATE ///Timeline Jumper - Infinite phasing. needs some special effects diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm index d4b6154f886..64ddc69d2ed 100644 --- a/code/modules/mod/modules/modules_visor.dm +++ b/code/modules/mod/modules/modules_visor.dm @@ -14,12 +14,12 @@ /obj/item/mod/module/visor/on_activation() if(length(visor_traits)) - mod.wearer.add_traits(visor_traits, MOD_TRAIT) + mod.wearer.add_traits(visor_traits, REF(src)) mod.wearer.update_sight() /obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE) if(length(visor_traits)) - mod.wearer.remove_traits(visor_traits, MOD_TRAIT) + mod.wearer.remove_traits(visor_traits, REF(src)) mod.wearer.update_sight() //Medical Visor - Gives you a medical HUD. diff --git a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm index f851dada495..e62aa35a608 100644 --- a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm +++ b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm @@ -11,12 +11,18 @@ circuit_comp_type = /obj/item/circuit_component/mod_program/camera /// Camera built-into the tablet. - var/obj/item/camera/internal_camera + var/obj/item/camera/app/internal_camera /// Latest picture taken by the app. var/datum/picture/internal_picture /// How many pictures were taken already, used for the camera's TGUI photo display var/picture_number = 1 +// Special type of camera for this exact usecase to prevent harddels +/obj/item/camera/app + name = "internal camera" + desc = "Specialized internal camera protected from the hellish depths of SSWardrobe. \ + Yell at coders if you somehow manage to see this" + /datum/computer_file/program/maintenance/camera/on_install() . = ..() internal_camera = new(computer) diff --git a/code/modules/modular_computers/file_system/programs/virtual_pet.dm b/code/modules/modular_computers/file_system/programs/virtual_pet.dm index 746824f8d8b..ed30fdbd703 100644 --- a/code/modules/modular_computers/file_system/programs/virtual_pet.dm +++ b/code/modules/modular_computers/file_system/programs/virtual_pet.dm @@ -445,7 +445,7 @@ GLOBAL_LIST_EMPTY(virtual_pets_list) for(var/type_index as anything in hat_selections) if(level >= hat_selections[type_index]) var/obj/item/hat = type_index - var/obj/item/hat_name = initial(hat.name) + var/hat_name = initial(hat.name) if(length(SSachievements.achievements)) // The Achievements subsystem is active. var/datum/award/required_cheevo = cheevo_hats[hat] if(required_cheevo && !user.client.get_award_status(required_cheevo)) diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index 50c833ca89f..036584ec2b1 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -49,6 +49,7 @@ if(user.can_perform_action(src)) name = "folder[(inputvalue ? " - '[inputvalue]'" : null)]" + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) /obj/item/folder/proc/remove_item(obj/item/Item, mob/user) if(istype(Item)) diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 938d41da51c..5ffe1ccbaf2 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -116,6 +116,13 @@ labels_left = initial(labels_left) //Yes, it's capped at its initial value return ITEM_INTERACT_SUCCESS +/obj/item/hand_labeler/examine() + . = ..() + if(labels_left > 0) + . += span_notice("It looks like it could label [labels_left] more thing\s.") + else + . += span_notice("It's out of labels.") + /obj/item/hand_labeler/borg name = "cyborg-hand labeler" diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 6cd1b504d28..173eae60ace 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -683,6 +683,8 @@ // Safe to assume there are writing implement details as user.can_write(...) fails with an invalid writing implement. var/writing_implement_data = holding.get_writing_implement_details() + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) + add_raw_text(paper_input, writing_implement_data["font"], writing_implement_data["color"], writing_implement_data["use_bold"], check_rights_for(user?.client, R_FUN)) log_paper("[key_name(user)] wrote to [name]: \"[paper_input]\"") diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm index e240e94292d..3cac28663ff 100644 --- a/code/modules/photography/photos/photo.dm +++ b/code/modules/photography/photos/photo.dm @@ -82,6 +82,7 @@ return var/txt = tgui_input_text(user, "What would you like to write on the back?", "Photo Writing", max_length = 128) if(txt && user.can_perform_action(src)) + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) scribble = txt else return ..() diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm index bd0a69e6d5e..c631e26def6 100644 --- a/code/modules/plumbing/plumbers/grinder_chemical.dm +++ b/code/modules/plumbing/plumbers/grinder_chemical.dm @@ -74,7 +74,7 @@ to_chat(user, span_notice("You dump items from [tool] into the grinder.")) for(var/obj/item/obj_item in tool.contents) - grind(obj_item) + blend(obj_item) return ITEM_INTERACT_SUCCESS else if(!tool.tool_behaviour) var/action = "[grinding ? "grind" : "juice"]" @@ -83,7 +83,7 @@ return ITEM_INTERACT_BLOCKING to_chat(user, span_notice("You attempt to [action] [tool].")) - grind(tool) + blend(tool) return ITEM_INTERACT_SUCCESS /obj/machinery/plumbing/grinder_chemical/CanAllowThrough(atom/movable/mover, border_dir) @@ -97,33 +97,45 @@ /obj/machinery/plumbing/grinder_chemical/proc/on_entered(datum/source, atom/movable/AM) SIGNAL_HANDLER - INVOKE_ASYNC(src, PROC_REF(grind), AM) + if(!isitem(AM)) + return + + INVOKE_ASYNC(src, PROC_REF(blend), AM) + + +/obj/machinery/plumbing/grinder_chemical/blended(obj/item/blended_item, grinded) + //don't delete slime extracts + if(istype(blended_item, /obj/item/slime_extract)) + //so you can't regrind them for extra stuff + blended_item.grind_results = null + + blended_item.forceMove(drop_location()) + + return TRUE + + return ..() /** * Grinds/Juices the atom * Arguments * * [AM][atom] - the atom to grind or juice */ -/obj/machinery/plumbing/grinder_chemical/proc/grind(atom/AM) +/obj/machinery/plumbing/grinder_chemical/proc/blend(obj/item/I) PRIVATE_PROC(TRUE) if(!is_operational || !anchored) return if(reagents.holder_full()) return - if(!isitem(AM)) - return - var/obj/item/I = AM if((I.item_flags & ABSTRACT) || (I.flags_1 & HOLOGRAM_1)) return + if(!I.blend_requirements(src)) + return - var/result if(!grinding) - result = I.juice(reagents, usr) + I.juice(reagents, usr, src) else if(length(I.grind_results) || I.reagents?.total_volume) - result = I.grind(reagents, usr) + I.grind(reagents, usr, src) use_energy(active_power_usage) - if(result) - qdel(I) diff --git a/code/modules/plumbing/plumbers/pill_press.dm b/code/modules/plumbing/plumbers/pill_press.dm index 23a7c7b03e5..b5b0744589a 100644 --- a/code/modules/plumbing/plumbers/pill_press.dm +++ b/code/modules/plumbing/plumbers/pill_press.dm @@ -1,7 +1,5 @@ ///the minimum size of a pill or patch #define MIN_VOLUME 5 -///the maximum size a pill or patch can be -#define MAX_VOLUME 50 ///max amount of pills allowed on our tile before we start storing them instead #define MAX_FLOOR_PRODUCTS 10 @@ -11,16 +9,16 @@ desc = "A press that makes pills, patches and bottles." icon_state = "pill_press" - /// current operating product (pills or patches) - var/product = "pill" /// selected size of the product var/current_volume = 10 + /// maximum printable volume of the product + var/max_volume = 50 /// prefix for the product name var/product_name = "factory" /// All packaging types wrapped up in 1 big list var/static/list/packaging_types = null ///The type of packaging to use - var/packaging_type + var/obj/item/reagent_containers/packaging_type ///Category of packaging var/packaging_category /// list of products stored in the machine, so we dont have 610 pills on one tile @@ -52,34 +50,28 @@ packaging_types += list(category_item) - packaging_type = REF(GLOB.reagent_containers[CAT_PILLS][1]) - decode_category() + packaging_type = GLOB.reagent_containers[CAT_PILLS][1] + max_volume = initial(packaging_type.volume) + current_volume = clamp(current_volume, MIN_VOLUME, max_volume) AddComponent(/datum/component/plumbing/simple_demand, bolt, layer) +/obj/machinery/plumbing/pill_press/Destroy(force) + QDEL_LAZYLIST(stored_products) + return ..() + /obj/machinery/plumbing/pill_press/examine(mob/user) . = ..() . += span_notice("The [name] currently has [stored_products.len] stored. There needs to be less than [MAX_FLOOR_PRODUCTS] on the floor to continue dispensing.") -/// decode product category from its type path and returns the decoded typepath -/obj/machinery/plumbing/pill_press/proc/decode_category() - var/obj/item/reagent_containers/container = locate(packaging_type) - if(ispath(container, /obj/item/reagent_containers/pill/patch)) - packaging_category = CAT_PATCHES - else if(ispath(container, /obj/item/reagent_containers/pill)) - packaging_category = CAT_PILLS - else - packaging_category = "Bottles" - return container - /obj/machinery/plumbing/pill_press/process(seconds_per_tick) if(!is_operational) return //shift & check to account for floating point inaccuracies if(reagents.total_volume >= current_volume) - var/obj/item/reagent_containers/container = locate(packaging_type) - container = new container(src) + var/obj/item/reagent_containers/container = new packaging_type(src) + var/suffix switch(packaging_category) if(CAT_PILLS) @@ -121,7 +113,6 @@ var/list/data = list() data["min_volume"] = MIN_VOLUME - data["max_volume"] = MAX_VOLUME data["packaging_types"] = packaging_types return data @@ -130,8 +121,9 @@ var/list/data = list() data["current_volume"] = current_volume + data["max_volume"] = max_volume data["product_name"] = product_name - data["packaging_type"] = packaging_type + data["packaging_type"] = REF(packaging_type) data["packaging_category"] = packaging_category return data @@ -141,21 +133,57 @@ if(.) return - . = TRUE switch(action) if("change_current_volume") - current_volume = round(clamp(text2num(params["volume"]), MIN_VOLUME, MAX_VOLUME)) + var/value = params["volume"] + if(isnull(value)) + return FALSE + + value = text2num(value) + if(isnull(value)) + return FALSE + + current_volume = clamp(value, MIN_VOLUME, max_volume) + return TRUE + if("change_product_name") var/formatted_name = html_encode(params["name"]) if (length(formatted_name) > MAX_NAME_LEN) product_name = copytext(formatted_name, 1, MAX_NAME_LEN + 1) else product_name = formatted_name + return TRUE + if("change_product") - packaging_type = params["ref"] - var/obj/item/reagent_containers/container = decode_category() - current_volume = clamp(current_volume, MIN_VOLUME, initial(container.volume)) + var/container = params["ref"] + if(!container) + return FALSE + + //is a valid option + var/container_found = FALSE + for(var/list/category as anything in packaging_types) + if(container_found) + break + for(var/list/package_item as anything in category["products"]) + if(container == package_item["ref"]) + container_found = TRUE + break + if(!container_found) + return FALSE + + //decode container & its category + packaging_type = locate(container) + if(ispath(packaging_type, /obj/item/reagent_containers/pill/patch)) + packaging_category = CAT_PATCHES + else if(ispath(packaging_type, /obj/item/reagent_containers/pill)) + packaging_category = CAT_PILLS + else + packaging_category = "Bottles" + + //get new volumes + max_volume = initial(packaging_type.volume) + current_volume = clamp(current_volume, MIN_VOLUME, max_volume) + return TRUE #undef MIN_VOLUME -#undef MAX_VOLUME #undef MAX_FLOOR_PRODUCTS diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index 89c50ad54b0..570a65b781a 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -581,15 +581,13 @@ else if(istype(user) && user.dna.check_mutation(/datum/mutation/human/telekinesis)) to_chat(user, span_notice("You telekinetically remove the light [fitting].")) else - var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm") - if(affecting?.receive_damage( 0, 5 )) // 5 burn damage - user.update_damage_overlays() + var/obj/item/bodypart/affecting = user.get_active_hand() + user.apply_damage(5, BURN, affecting, wound_bonus = CANT_WOUND) if(HAS_TRAIT(user, TRAIT_LIGHTBULB_REMOVER)) - to_chat(user, span_notice("You feel your [affecting] burning, and the light beginning to budge.")) + to_chat(user, span_notice("You feel your [affecting.plaintext_zone] burning, but the light begins to budge...")) if(!do_after(user, 5 SECONDS, target = src)) return - if(affecting?.receive_damage( 0, 10 )) // 10 more burn damage - user.update_damage_overlays() + user.apply_damage(10, BURN, user.get_active_hand(), wound_bonus = CANT_WOUND) to_chat(user, span_notice("You manage to remove the light [fitting], shattering it in process.")) break_light_tube() else @@ -730,7 +728,7 @@ plane = FLOOR_PLANE light_type = /obj/item/light/bulb fitting = "bulb" - nightshift_brightness = 3 + nightshift_brightness = 4 fire_brightness = 4.5 /obj/machinery/light/floor/get_light_offset() diff --git a/code/modules/power/singularity/reality_tear.dm b/code/modules/power/singularity/reality_tear.dm index e43301541a0..55a126fc0cf 100644 --- a/code/modules/power/singularity/reality_tear.dm +++ b/code/modules/power/singularity/reality_tear.dm @@ -52,9 +52,7 @@ return var/mob/living/jedi = user to_chat(jedi, span_userdanger("You don't feel like you are real anymore.")) - jedi.dust_animation() - jedi.spawn_dust() - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, attack_hand), jedi), 0.5 SECONDS) + jedi.dust(just_ash = TRUE) return COMPONENT_CANCEL_ATTACK_CHAIN //The temporary tears in reality. Collapses into nothing, and has a significantly lower gravity pull range, but consumes more widely. diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 1993a51e29b..a53d959c9e3 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -149,7 +149,7 @@ if(current_size <= STAGE_TWO) investigate_log("has been destroyed by a heavy explosion.", INVESTIGATE_ENGINE) qdel(src) - return + return TRUE energy -= round(((energy + 1) / 2), 1) if(EXPLODE_HEAVY) diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 2e2931c835c..ab210fe1986 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -243,7 +243,7 @@ //This also means we have no need to track distance, as the doview() proc does it all for us. //Darkness fucks oview up hard. I've tried dview() but it doesn't seem to work - //I hate existance + //I hate existence for(var/atom/A as anything in typecache_filter_list(oview(zap_range+2, source), things_to_shock)) if(!(zap_flags & ZAP_ALLOW_DUPLICATES) && LAZYACCESS(shocked_targets, A)) continue diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 7e174b544ea..d542f229914 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -225,7 +225,7 @@ update_appearance() /obj/projectile/kinetic/on_range() - strike_thing() + strike_thing(loc) ..() /obj/projectile/kinetic/on_hit(atom/target, blocked = 0, pierce_hit) @@ -258,6 +258,17 @@ //mecha_kineticgun version of the projectile /obj/projectile/kinetic/mech range = 5 + damage = 80 + +/obj/projectile/kinetic/mech/strike_thing(atom/target) + . = ..() + new /obj/effect/temp_visual/explosion/fast(target) + for(var/turf/closed/mineral/mineral_turf in RANGE_TURFS(1, target) - target) + mineral_turf.gets_drilled(firer, TRUE) + for(var/mob/living/living_mob in range(1, target) - firer - target) + var/armor = living_mob.run_armor_check(def_zone, armor_flag, armour_penetration = armour_penetration) + living_mob.apply_damage(damage, damage_type, def_zone, armor) + to_chat(living_mob, span_userdanger("You're struck by a [name]!")) //Modkits /obj/item/borg/upgrade/modkit diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 7c6d207ec92..d93312b80aa 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -177,7 +177,7 @@ /obj/projectile/beam/laser/accelerator/Range() ..() damage += 7 - transform = 0 + transform = matrix() transform *= min(1 + (decayedRange - range) * size_per_tile, max_scale) ///X-ray gun diff --git a/code/modules/projectiles/guns/special/medbeam.dm b/code/modules/projectiles/guns/special/medbeam.dm index 0ad5caf2fec..95da571baf5 100644 --- a/code/modules/projectiles/guns/special/medbeam.dm +++ b/code/modules/projectiles/guns/special/medbeam.dm @@ -83,56 +83,26 @@ last_check = world.time - if(!los_check(loc, current_target)) + if(!los_check(loc, current_target, mid_check = CALLBACK(src, PROC_REF(mid_los_check)))) QDEL_NULL(current_beam)//this will give the target lost message return if(current_target) on_beam_tick(current_target) -/obj/item/gun/medbeam/proc/los_check(atom/movable/user, mob/target) - var/turf/user_turf = user.loc - if(mounted) - user_turf = get_turf(user) - else if(!istype(user_turf)) - return FALSE - var/obj/dummy = new(user_turf) - dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE //Grille/Glass so it can be used through common windows - var/turf/previous_step = user_turf - var/first_step = TRUE - for(var/turf/next_step as anything in (get_line(user_turf, target) - user_turf)) - if(first_step) - for(var/obj/blocker in user_turf) - if(!blocker.density || !(blocker.flags_1 & ON_BORDER_1)) - continue - if(blocker.CanPass(dummy, get_dir(user_turf, next_step))) - continue - return FALSE // Could not leave the first turf. - first_step = FALSE - if(mounted && next_step == user_turf) - - continue //Mechs are dense and thus fail the check - if(next_step.density) +/obj/item/gun/medbeam/proc/mid_los_check(atom/movable/user, mob/target, pass_args = PASSTABLE|PASSGLASS|PASSGRILLE, turf/next_step, obj/dummy) + for(var/obj/effect/ebeam/medical/B in next_step)// Don't cross the str-beams! + if(QDELETED(current_beam)) + break //We shouldn't be processing anymore. + if(QDELETED(B)) + continue + if(!B.owner) + stack_trace("beam without an owner! [B]") + continue + if(B.owner.origin != current_beam.origin) + explosion(B.loc, heavy_impact_range = 3, light_impact_range = 5, flash_range = 8, explosion_cause = src) qdel(dummy) return FALSE - for(var/atom/movable/movable as anything in next_step) - if(!movable.CanPass(dummy, get_dir(next_step, previous_step))) - qdel(dummy) - return FALSE - for(var/obj/effect/ebeam/medical/B in next_step)// Don't cross the str-beams! - if(QDELETED(current_beam)) - break //We shouldn't be processing anymore. - if(QDELETED(B)) - continue - if(!B.owner) - stack_trace("beam without an owner! [B]") - continue - if(B.owner.origin != current_beam.origin) - explosion(B.loc, heavy_impact_range = 3, light_impact_range = 5, flash_range = 8, explosion_cause = src) - qdel(dummy) - return FALSE - previous_step = next_step - qdel(dummy) return TRUE /obj/item/gun/medbeam/proc/on_beam_hit(mob/living/target) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 0b03f412931..3d2834edc62 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -448,11 +448,11 @@ target = select_target(target_turf, target) continue - if (SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT) + if (SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, src) & PROJECTILE_INTERRUPT_HIT) qdel(src) return - if (SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_PREHIT, args) & PROJECTILE_INTERRUPT_HIT) + if (SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_PREHIT, target) & PROJECTILE_INTERRUPT_HIT) qdel(src) return diff --git a/code/modules/projectiles/projectile/energy/nuclear_particle.dm b/code/modules/projectiles/projectile/energy/nuclear_particle.dm index b82ff478a05..6626cb9b5ed 100644 --- a/code/modules/projectiles/projectile/energy/nuclear_particle.dm +++ b/code/modules/projectiles/projectile/energy/nuclear_particle.dm @@ -29,7 +29,7 @@ if (ishuman(target)) SSradiation.irradiate(target) - ..() + return ..() /atom/proc/fire_nuclear_particle(angle = rand(0,360)) //used by fusion to fire random nuclear particles. Fires one particle in a random direction. var/obj/projectile/energy/nuclear_particle/P = new /obj/projectile/energy/nuclear_particle(src) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 24525a978d2..cb94f24de1e 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -225,9 +225,11 @@ var/datum/reagent/temp = GLOB.chemical_reagents_list[re] if(temp) var/chemname = temp.name + var/chemcolor = temp.color if(is_hallucinating && prob(5)) chemname = "[pick_list_replacements("hallucination.json", "chemicals")]" - chemicals += list(list("title" = chemname, "id" = temp.name, "pH" = temp.ph, "pHCol" = convert_ph_to_readable_color(temp.ph))) + chemcolor = random_colour() + chemicals += list(list("title" = chemname, "id" = temp.name, "pH" = temp.ph, "color" = chemcolor, "pHCol" = convert_ph_to_readable_color(temp.ph))) .["chemicals"] = chemicals .["recipes"] = saved_recipes diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 87ba535a149..bcb6cac2f18 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -393,7 +393,6 @@ . = TRUE if(. && !QDELETED(src)) //transferring volatile reagents can cause a explosion & destory us update_appearance(UPDATE_OVERLAYS) - return . /obj/machinery/chem_master/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) . = ..() @@ -447,9 +446,27 @@ if("selectContainer") var/obj/item/reagent_containers/target = locate(params["ref"]) + + //is this even a valid type path if(!ispath(target)) return FALSE + //are we printing a valid container + var/container_found = FALSE + for(var/category in printable_containers) + //container found in previous iteration + if(container_found) + break + + //find for matching typepath + for(var/obj/item/reagent_containers/container as anything in printable_containers[category]) + if(target == container) + container_found = TRUE + break + if(!container_found) + return FALSE + + //set the container selected_container = target return TRUE diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm index 141fb7c4e6f..7716784e4b4 100644 --- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm +++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm @@ -434,43 +434,33 @@ playsound(src, 'sound/machines/juicer.ogg', 20, TRUE) var/total_weight - for(var/obj/item/weapon in src) - if((weapon in component_parts) || weapon == beaker) - continue + var/item_weight + for(var/obj/item/ingredient in contents) if(beaker.reagents.holder_full()) break + if((ingredient in component_parts) || ingredient == beaker) + continue + + //record item weight before & after blending + item_weight = ingredient.w_class - //recursively process everything inside this atom - var/item_processed = FALSE - var/item_weight = weapon.w_class - for(var/obj/item/ingredient as anything in weapon.get_all_contents_type(/obj/item)) - if(beaker.reagents.holder_full()) - break - - if(juicing) - if(!ingredient.juice(beaker.reagents, user)) - to_chat(user, span_danger("[src] shorts out as it tries to juice up [ingredient], and transfers it back to storage.")) - continue - item_processed = TRUE - else if(length(ingredient.grind_results) || ingredient.reagents?.total_volume) - if(!ingredient.grind(beaker.reagents, user)) - if(isstack(ingredient)) - to_chat(user, span_notice("[src] attempts to grind as many pieces of [ingredient] as possible.")) - else - to_chat(user, span_danger("[src] shorts out as it tries to grind up [ingredient], and transfers it back to storage.")) - continue - item_processed = TRUE + if(juicing) + if(!ingredient.juice(beaker.reagents, user)) + to_chat(user, span_danger("[src] shorts out as it tries to juice up [ingredient], and transfers it back to storage.")) + continue + else if(!ingredient.grind(beaker.reagents, user)) + if(isstack(ingredient)) + to_chat(user, span_notice("[src] attempts to grind as many pieces of [ingredient] as possible.")) + else + to_chat(user, span_danger("[src] shorts out as it tries to grind up [ingredient], and transfers it back to storage.")) + continue //happens only for stacks where some of the sheets were grinded so we roughly compute the weight grinded - if(item_weight != weapon.w_class) - total_weight += item_weight - weapon.w_class + if(item_weight != ingredient.w_class) + total_weight += item_weight - ingredient.w_class else total_weight += item_weight - //delete only if operation was successfull for atleast 1 item(also delete atoms for whom only some of its contents were processed as they are non functional now) - if(item_processed) - qdel(weapon) - //use power according to the total weight of items grinded use_energy((active_power_usage * (duration / 1 SECONDS)) * (total_weight / maximum_weight)) diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 4ed21b0e298..f65383fbfcc 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -82,7 +82,7 @@ var/purge_multiplier = 1 ///The default reagent container for the reagent, used for icon generation - var/obj/item/reagent_containers/default_container = /obj/item/reagent_containers/cup/bottle + var/obj/default_container = /obj/item/reagent_containers/cup/bottle // Used for restaurants. ///The amount a robot will pay for a glass of this (20 units but can be higher if you pour more, be frugal!) diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 48905153f87..2a59ee0fc13 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -109,7 +109,7 @@ /datum/reagent/consumable/ethanol/beer name = "Beer" description = "An alcoholic beverage brewed since ancient times on Old Earth. Still popular today." - color = "#664300" // rgb: 102, 67, 0 + color = "#D7BC31" // rgb: 215, 188, 49 nutriment_factor = 1 boozepwr = 25 taste_description = "mild carbonated malt" @@ -1531,7 +1531,7 @@ /datum/reagent/consumable/ethanol/eggnog name = "Eggnog" description = "For enjoying the most wonderful time of the year." - color = "#fcfdc6" // rgb: 252, 253, 198 + color = "#ffe2ad" // rgb: 255, 226, 173 nutriment_factor = 2 boozepwr = 1 quality = DRINK_VERYGOOD @@ -1541,7 +1541,7 @@ /datum/reagent/consumable/ethanol/dreadnog name = "Dreadnog" description = "For suffering during a period of joy." - color = "#abb862" // rgb: 252, 253, 198 + color = "#f7ffad" // rgb: 247, 255, 173 nutriment_factor = 3 * REAGENTS_METABOLISM boozepwr = 1 quality = DRINK_REVOLTING @@ -1573,7 +1573,7 @@ /datum/reagent/consumable/ethanol/creme_de_menthe name = "Creme de Menthe" description = "A minty liqueur excellent for refreshing, cool drinks." - color = "#00cc00" + color = "#467446" //rgb: 70, 116, 70 boozepwr = 20 taste_description = "a minty, cool, and invigorating splash of cold streamwater" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -1581,7 +1581,7 @@ /datum/reagent/consumable/ethanol/creme_de_cacao name = "Creme de Cacao" description = "A chocolatey liqueur excellent for adding dessert notes to beverages and bribing sororities." - color = "#996633" + color = "#350900" // rgb: 53, 9, 0 boozepwr = 20 taste_description = "a slick and aromatic hint of chocolates swirling in a bite of alcohol" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index 4bb381a9e2a..305f09410f1 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -30,7 +30,7 @@ /datum/reagent/consumable/limejuice name = "Lime Juice" description = "The sweet-sour juice of limes." - color = "#365E30" // rgb: 54, 94, 48 + color = "#a6f19a" // rgb: 166, 241, 154 taste_description = "unbearable sourness" ph = 2.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -73,14 +73,14 @@ /datum/reagent/consumable/applejuice name = "Apple Juice" description = "The sweet juice of an apple, fit for all ages." - color = "#ECFF56" // rgb: 236, 255, 86 + color = "#fff06b" // rgb: 255, 240, 107 taste_description = "apples" ph = 3.2 // ~ 2.7 -> 3.7 /datum/reagent/consumable/poisonberryjuice name = "Poison Berry Juice" description = "A tasty juice blended from various kinds of very deadly and toxic berries." - color = "#863353" // rgb: 134, 51, 83 + color = "#792b49" // rgb: 121, 43, 73 taste_description = "berries" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -92,14 +92,14 @@ /datum/reagent/consumable/watermelonjuice name = "Watermelon Juice" description = "Delicious juice made from watermelon." - color = "#863333" // rgb: 134, 51, 51 + color = "#af5e5e" // rgb: 175, 94, 94 taste_description = "juicy watermelon" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/consumable/lemonjuice name = "Lemon Juice" description = "This juice is VERY sour." - color = "#863333" // rgb: 175, 175, 0 + color = "#ebeb9e" // rgb: 235, 235, 158 taste_description = "sourness" ph = 2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -107,7 +107,7 @@ /datum/reagent/consumable/banana name = "Banana Juice" description = "The raw essence of a banana. HONK" - color = "#863333" // rgb: 175, 175, 0 + color = "#FFFCB9" // rgb: 255, 252, 185 taste_description = "banana" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -167,7 +167,7 @@ name = "Potato Juice" description = "Juice of the potato. Bleh." nutriment_factor = 2 - color = "#302000" // rgb: 48, 32, 0 + color = "#E8A856" // rgb: 234, 157, 58 taste_description = "irish sadness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -175,7 +175,7 @@ name = "Pickle Juice" description = "More accurately, this is the brine the pickle was floating in" nutriment_factor = 2 - color = "#302000" // rgb: 48, 32, 0 + color = "#cde65e" // rgb: 205, 230, 94 taste_description = "vinegar brine" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -362,7 +362,7 @@ /datum/reagent/consumable/icecoffee name = "Iced Coffee" description = "Coffee and ice, refreshing and cool." - color = "#102838" // rgb: 16, 40, 56 + color = "#462b15" // rgb: 70, 43, 21 nutriment_factor = 0 overdose_threshold = 80 taste_description = "bitter coldness" @@ -383,7 +383,7 @@ /datum/reagent/consumable/hot_ice_coffee name = "Hot Ice Coffee" description = "Coffee with pulsing ice shards" - color = "#102838" // rgb: 16, 40, 56 + color = "#462b15" // rgb: 70, 43, 21 nutriment_factor = 0 overdose_threshold = 80 taste_description = "bitter coldness and a hint of smoke" @@ -972,7 +972,7 @@ name = "Hot Coco" description = "Made with love! And coco beans." nutriment_factor = 4 - color = "#403010" // rgb: 64, 48, 16 + color = "#3b240e" // rgb: 59, 36, 14 taste_description = "creamy chocolate" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -1117,7 +1117,7 @@ /datum/reagent/consumable/aloejuice name = "Aloe Juice" - color = "#A3C48B" + color = "#b3c5a7" // rgb: 179, 197, 167 description = "A healthy and refreshing juice." taste_description = "vegetable" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -1161,7 +1161,7 @@ /datum/reagent/consumable/toechtauese_juice name = "Töchtaüse Juice" description = "An unpleasant juice made from töchtaüse berries. Best made into a syrup, unless you enjoy pain." - color = "#554862" + color = "#554862" // rgb: 85, 72, 98 nutriment_factor = 0 taste_description = "fiery itchy pain" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -1169,7 +1169,7 @@ /datum/reagent/consumable/toechtauese_syrup name = "Töchtaüse Syrup" description = "A harsh spicy and bitter syrup, made from töchtaüse berries. Useful as an ingredient, both for food and cocktails." - color = "#554862" + color = "#554862" // rgb: 85, 72, 98 nutriment_factor = 0 taste_description = "sugar, spice, and nothing nice" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -1225,14 +1225,14 @@ /datum/reagent/consumable/cucumberjuice name = "Cucumber Juice" description = "Ordinary cucumber juice, nothing from the fantasy world." - color = "#6cd87a" + color = "#B1D861" // rgb: 177, 216, 97 taste_description = "light cucumber" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/consumable/cucumberlemonade name = "Cucumber Lemonade" description = "Cucumber juice, sugar, and soda; what else do I need?" - color = "#6cd87a" + color = "#cbe248" // rgb: 203, 226, 72 quality = DRINK_GOOD taste_description = "citrus soda with cucumber" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED diff --git a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm index c2dc3da6d45..64656126542 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm @@ -95,6 +95,7 @@ name = "glass of amaretto" desc = "A sweet and syrupy looking drink." icon_state = "amarettoglass" + /datum/glass_style/drinking_glass/cognac required_drink_type = /datum/reagent/consumable/ethanol/cognac name = "glass of cognac" @@ -155,7 +156,6 @@ required_drink_type = /datum/reagent/consumable/ethanol/pruno name = "glass of pruno" desc = "Fermented prison wine made from fruit, sugar, and despair. Security loves to confiscate this, which is the only kind thing Security has ever done." - icon_state = "glass_orange" /datum/glass_style/drinking_glass/navy_rum required_drink_type = /datum/reagent/consumable/ethanol/navy_rum @@ -187,7 +187,6 @@ name = "glass of yūyake" desc = "It's the saccharine essence of the 70s in a glass... the 1970s, that is!" icon = 'icons/obj/drinks/drinks.dmi' - icon_state = "glass_red" /datum/glass_style/drinking_glass/shochu required_drink_type = /datum/reagent/consumable/ethanol/shochu @@ -262,7 +261,6 @@ required_drink_type = /datum/reagent/consumable/ethanol/hooch name = "Hooch" desc = "You've really hit rock bottom now... your liver packed its bags and left last night." - icon_state = "glass_brown2" /datum/glass_style/shot_glass/goldschlager required_drink_type = /datum/reagent/consumable/ethanol/goldschlager diff --git a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/coffee_tea.dm b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/coffee_tea.dm index d8813fba96a..a6662121881 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/coffee_tea.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/coffee_tea.dm @@ -4,7 +4,6 @@ required_drink_type = /datum/reagent/consumable/coffee name = "glass of coffee" desc = "Don't drop it, or you'll send scalding liquid and glass shards everywhere." - icon_state = "glass_brown" /datum/glass_style/drinking_glass/tea required_drink_type = /datum/reagent/consumable/tea @@ -66,7 +65,6 @@ required_drink_type = /datum/reagent/consumable/hot_coco name = "glass of hot coco" desc = "A favorite winter drink to warm you up." - icon_state = "chocolateglass" drink_type = SUGAR | DAIRY /datum/glass_style/drinking_glass/italian_coco diff --git a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/dairy.dm b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/dairy.dm index 24e565dd290..ac60a884915 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/dairy.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/dairy.dm @@ -4,7 +4,6 @@ required_drink_type = /datum/reagent/consumable/milk name = "glass of milk" desc = "White and nutritious goodness!" - icon_state = "glass_white" drink_type = DAIRY | BREAKFAST /datum/glass_style/has_foodtype/juicebox/milk @@ -25,20 +24,17 @@ required_drink_type = /datum/reagent/consumable/soymilk name = "glass of soy milk" desc = "White and nutritious soy goodness!" - icon_state = "glass_white" /datum/glass_style/drinking_glass/cream required_drink_type = /datum/reagent/consumable/cream name = "glass of cream" desc = "Ewwww..." - icon_state = "glass_white" /datum/glass_style/drinking_glass/coconut_milk required_drink_type = /datum/reagent/consumable/coconut_milk name = "glass of coconut milk" desc = "The essence of the tropics, contained safely within a glass." icon = 'icons/obj/drinks/drinks.dmi' - icon_state = "glass_white" /datum/glass_style/drinking_glass/ayran required_drink_type = /datum/reagent/consumable/ayran diff --git a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/juices.dm b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/juices.dm index 6f837d8f515..e7ecfa331fa 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/juices.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/juices.dm @@ -4,7 +4,6 @@ required_drink_type = /datum/reagent/consumable/orangejuice name = "glass of orange juice" desc = "Vitamins! Yay!" - icon_state = "glass_orange" drink_type = FRUIT | BREAKFAST /datum/glass_style/has_foodtype/juicebox/orangejuice @@ -62,13 +61,11 @@ required_drink_type = /datum/reagent/consumable/tomatojuice name = "glass of tomato juice" desc = "Are you sure this is tomato juice?" - icon_state = "glass_red" /datum/glass_style/drinking_glass/limejuice required_drink_type = /datum/reagent/consumable/limejuice name = "glass of lime juice" desc = "A glass of sweet-sour lime juice." - icon_state = "glass_green" /datum/glass_style/drinking_glass/carrotjuice required_drink_type = /datum/reagent/consumable/carrotjuice @@ -80,19 +77,16 @@ required_drink_type = /datum/reagent/consumable/berryjuice name = "glass of berry juice" desc = "Berry juice. Or maybe it's jam. Who cares?" - icon_state = "berryjuice" /datum/glass_style/drinking_glass/poisonberryjuice required_drink_type = /datum/reagent/consumable/poisonberryjuice name = "glass of berry juice" desc = "Berry juice. Or maybe it's poison. Who cares?" - icon_state = "poisonberryjuice" /datum/glass_style/drinking_glass/watermelonjuice required_drink_type = /datum/reagent/consumable/watermelonjuice name = "glass of watermelon juice" desc = "A glass of watermelon juice." - icon_state = "glass_red" /datum/glass_style/drinking_glass/lemonjuice required_drink_type = /datum/reagent/consumable/lemonjuice @@ -116,43 +110,36 @@ required_drink_type = /datum/reagent/consumable/potato_juice name = "glass of potato juice" desc = "Bleh..." - icon_state = "glass_brown" /datum/glass_style/drinking_glass/bungojuice required_drink_type = /datum/reagent/consumable/bungojuice name = "glass of bungo juice" desc = "Exotic! You feel like you are on vacation already." - icon_state = "glass_yellow" /datum/glass_style/drinking_glass/prunomix required_drink_type = /datum/reagent/consumable/prunomix name = "glass of pruno mixture" desc = "Fruit, sugar, yeast, and water pulped together into a pungent slurry." - icon_state = "glass_orange" /datum/glass_style/drinking_glass/aloejuice required_drink_type = /datum/reagent/consumable/aloejuice name = "glass of aloe juice" desc = "A healthy and refreshing juice." - icon_state = "glass_yellow" /datum/glass_style/drinking_glass/toechtauese_juice required_drink_type = /datum/reagent/consumable/toechtauese_juice name = "glass of töchtaüse juice" desc = "Raw, unadulterated töchtaüse juice. One swig will fill you with regrets." - icon_state = "toechtauese_syrup" /datum/glass_style/drinking_glass/toechtauese_syrup required_drink_type = /datum/reagent/consumable/toechtauese_syrup name = "glass of töchtaüse syrup" desc = "Not for drinking on its own." - icon_state = "toechtauese_syrup" /datum/glass_style/drinking_glass/cucumberjuice required_drink_type = /datum/reagent/consumable/cucumberjuice name = "glass of cucumber juice" desc = "A glass of cucumber juice." - icon_state = "glass_cucumber" // Effectively misc @@ -161,7 +148,6 @@ required_drink_type = /datum/reagent/consumable/menthol name = "glass of menthol" desc = "Tastes naturally minty, and imparts a very mild numbing sensation." - icon_state = "glass_green" /datum/glass_style/drinking_glass/grenadine required_drink_type = /datum/reagent/consumable/grenadine diff --git a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/mixed_alcohol.dm b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/mixed_alcohol.dm index 67802b9af20..ee741191771 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/mixed_alcohol.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/mixed_alcohol.dm @@ -2,7 +2,6 @@ required_drink_type = /datum/reagent/consumable/ethanol/bilk name = "glass of bilk" desc = "A brew of milk and beer. For those alcoholics who fear osteoporosis." - icon_state = "glass_brown" /datum/glass_style/drinking_glass/threemileisland required_drink_type = /datum/reagent/consumable/ethanol/threemileisland @@ -400,7 +399,6 @@ required_drink_type = /datum/reagent/consumable/ethanol/bacchus_blessing name = "Bacchus' Blessing" desc = "You didn't think it was possible for a liquid to be so utterly revolting. Are you sure about this...?" - icon_state = "glass_brown2" /datum/glass_style/drinking_glass/atomicbomb required_drink_type = /datum/reagent/consumable/ethanol/atomicbomb @@ -434,7 +432,6 @@ required_drink_type = /datum/reagent/consumable/ethanol/eggnog name = "eggnog" desc = "For enjoying the most wonderful time of the year." - icon_state = "glass_yellow" drink_type = FRUIT /datum/glass_style/has_foodtype/juicebox/eggnog @@ -461,25 +458,21 @@ required_drink_type = /datum/reagent/consumable/ethanol/triple_sec name = "Triple Sec" desc = "A glass of straight Triple Sec." - icon_state = "glass_orange" /datum/glass_style/drinking_glass/creme_de_menthe required_drink_type = /datum/reagent/consumable/ethanol/creme_de_menthe name = "Creme de Menthe" desc = "You can almost feel the first breath of spring just looking at it." - icon_state = "glass_green" /datum/glass_style/drinking_glass/creme_de_cacao required_drink_type = /datum/reagent/consumable/ethanol/creme_de_cacao name = "Creme de Cacao" desc = "A million hazing lawsuits and alcohol poisonings have started with this humble ingredient." - icon_state = "glass_brown" /datum/glass_style/drinking_glass/creme_de_coconut required_drink_type = /datum/reagent/consumable/ethanol/creme_de_coconut name = "Creme de Coconut" desc = "An unintimidating glass of coconut liqueur." - icon_state = "glass_white" /datum/glass_style/drinking_glass/quadruple_sec required_drink_type = /datum/reagent/consumable/ethanol/quadruple_sec @@ -787,7 +780,6 @@ /datum/glass_style/drinking_glass/mushi_kombucha required_drink_type = /datum/reagent/consumable/ethanol/mushi_kombucha name = "glass of mushi kombucha" - icon_state = "glass_orange" /datum/glass_style/drinking_glass/triumphal_arch required_drink_type = /datum/reagent/consumable/ethanol/triumphal_arch diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm index b8a22507225..ebd392f5368 100644 --- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm @@ -480,6 +480,7 @@ Basically, we fill the time between now and 2s from now with hands based off the overdose_threshold = 20 self_consuming = TRUE //No pesky liver shenanigans chemical_flags = REAGENT_DONOTSPLIT | REAGENT_DEAD_PROCESS + affected_organ_flags = NONE ///If we brought someone back from the dead var/back_from_the_dead = FALSE /// List of trait buffs to give to the affected mob, and remove as needed. diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index c89495755e9..439bd9edc84 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -17,7 +17,6 @@ required_drink_type = /datum/reagent/blood name = "glass of tomato juice" desc = "Are you sure this is tomato juice?" - icon_state = "glass_red" // FEED ME /datum/reagent/blood/on_hydroponics_apply(obj/machinery/hydroponics/mytray, mob/user) diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index 8816ec87dfb..acde1703668 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -248,7 +248,7 @@ for(var/atom/movable/X in orange(range, T)) if(X.anchored) continue - if(iseffect(X) || iscameramob(X) || isdead(X)) + if(iseffect(X) || iseyemob(X) || isdead(X)) continue var/distance = get_dist(X, T) var/moving_power = max(range - distance, 1) @@ -413,7 +413,7 @@ live.apply_damage(damage)//Since this can be called multiple times if(movey.anchored) continue - if(iseffect(movey) || iscameramob(movey) || isdead(movey)) + if(iseffect(movey) || iseyemob(movey) || isdead(movey)) continue if(implosion) var/distance = get_dist(movey, this_turf) diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index e47aba24b8b..7050bee61d2 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -576,7 +576,7 @@ /datum/chemical_reaction/corgium/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) var/location = get_turf(holder.my_atom) - for(var/i in rand(1, created_volume) to created_volume) // More lulz. + for(var/i in 1 to rand(1, created_volume)) // More lulz. new /mob/living/basic/pet/dog/corgi(location) ..() @@ -632,7 +632,7 @@ /datum/chemical_reaction/butterflium/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) var/location = get_turf(holder.my_atom) - for(var/i in rand(1, created_volume) to created_volume) + for(var/i in 1 to rand(1, created_volume)) new /mob/living/basic/butterfly(location) ..() //scream powder @@ -981,7 +981,7 @@ /datum/chemical_reaction/ant_slurry/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) var/location = get_turf(holder.my_atom) - for(var/i in rand(1, created_volume) to created_volume) + for(var/i in 1 to rand(1, created_volume)) new /mob/living/basic/ant(location) ..() diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index 21d076e949f..41622550f8f 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -384,7 +384,7 @@ /datum/chemical_reaction/slime/slimeoil - results = list(/datum/reagent/consumable/nutriment/fat/oil = 10) + results = list(/datum/reagent/consumable/nutriment/fat/oil/corn = 10) required_reagents = list(/datum/reagent/blood = 1) required_container = /obj/item/slime_extract/oil diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 7c4921ec05a..d0d5ae98c43 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -122,6 +122,7 @@ if(custom_label) labelled = TRUE name = "blood pack - [custom_label]" + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) balloon_alert(user, "new label set") else labelled = FALSE diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index b4493eb2ae4..0c6d638fca1 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -506,11 +506,17 @@ if(grinded) to_chat(user, span_warning("There is something inside already!")) return - if(I.juice_typepath || I.grind_results) + if(!I.blend_requirements(src)) + to_chat(user, span_warning("Cannot grind this!")) + return + if(length(I.grind_results) || I.reagents?.total_volume) I.forceMove(src) grinded = I - return - to_chat(user, span_warning("You can't grind this!")) + +/obj/item/reagent_containers/cup/mortar/blended(obj/item/blended_item, grinded) + src.grinded = null + + return ..() /obj/item/reagent_containers/cup/mortar/proc/grind_item(obj/item/item, mob/living/carbon/human/user) if(item.flags_1 & HOLOGRAM_1) @@ -520,13 +526,12 @@ if(!item.grind(reagents, user)) if(isstack(item)) - to_chat(usr, span_notice("[src] attempts to grind as many pieces of [item] as possible.")) + to_chat(user, span_notice("[src] attempts to grind as many pieces of [item] as possible.")) else to_chat(user, span_danger("You fail to grind [item].")) return + to_chat(user, span_notice("You grind [item] into a nice powder.")) - grinded = null - QDEL_NULL(item) /obj/item/reagent_containers/cup/mortar/proc/juice_item(obj/item/item, mob/living/carbon/human/user) if(item.flags_1 & HOLOGRAM_1) @@ -537,9 +542,8 @@ if(!item.juice(reagents, user)) to_chat(user, span_notice("You fail to juice [item].")) return + to_chat(user, span_notice("You juice [item] into a fine liquid.")) - grinded = null - QDEL_NULL(item) //Coffeepots: for reference, a standard cup is 30u, to allow 20u for sugar/sweetener/milk/creamer /obj/item/reagent_containers/cup/coffeepot diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 2259cda34d7..f733a4dac88 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -544,6 +544,7 @@ return if(user.can_perform_action(src)) + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) name = "[(inputvalue ? "[inputvalue]" : null)] bottle" //types of syrups diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 17ce2fea149..04ff332a9ea 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -402,6 +402,7 @@ spray_range = 2 spray_sound = 'sound/effects/snap.ogg' possible_transfer_amounts = list(5) + reagent_container_liquid_sound = null /obj/item/reagent_containers/spray/chemsprayer/party/spray(atom/A, mob/user) . = ..() diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 51faf4807a7..6a330ddb05c 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -122,6 +122,7 @@ if(!str || !length(str)) to_chat(user, span_warning("Invalid text!")) return + playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE) user.visible_message(span_notice("[user] labels [src] as [str].")) name = "[name] ([str])" diff --git a/code/modules/religion/religion_structures.dm b/code/modules/religion/religion_structures.dm index 1b30d021268..8c8783f16eb 100644 --- a/code/modules/religion/religion_structures.dm +++ b/code/modules/religion/religion_structures.dm @@ -109,7 +109,7 @@ new /obj/effect/decal/cleanable/ash(drop_location()) qdel(src) -/obj/item/ritual_totem/can_be_pulled(user, grab_state, force) +/obj/item/ritual_totem/can_be_pulled(user, force) . = ..() return FALSE //no diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index 24ba3cb59d9..b1cb05c9521 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -20,9 +20,8 @@ LAZYADD(GLOB.religious_sect.active_rites, src) /datum/religion_rites/Destroy() - if(!GLOB?.religious_sect) - return - LAZYREMOVE(GLOB.religious_sect.active_rites, src) + if(GLOB?.religious_sect) + LAZYREMOVE(GLOB.religious_sect.active_rites, src) return ..() /datum/religion_rites/proc/can_afford(mob/living/user) diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm index 21deb54280c..2f43fe58133 100644 --- a/code/modules/research/designs/mecha_designs.dm +++ b/code/modules/research/designs/mecha_designs.dm @@ -750,7 +750,7 @@ desc = "Equipment for medical exosuits. A mounted sleeper that stabilizes patients and can inject reagents in the exosuit's reserves." id = "mech_sleeper" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/medical/sleeper + build_path = /obj/item/mecha_parts/mecha_equipment/sleeper/medical materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT*5) construction_time = 10 SECONDS category = list( diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 90b8acee031..56764318f1b 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -1046,6 +1046,18 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE +/datum/design/bluespace_fish_tank + name = "Bluespace Fish Tank Kit" + desc = "The required components to upgrade the capacity of a fish tank." + id = "bluespace_fish_tank_kit" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 3, /datum/material/titanium = SMALL_MATERIAL_AMOUNT, /datum/material/bluespace = SMALL_MATERIAL_AMOUNT * 3) + build_path = /obj/item/aquarium_upgrade/bluespace_tank + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_SERVICE, + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE + // Coffeemaker Stuff /datum/design/coffeepot @@ -1121,3 +1133,21 @@ RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SCIENCE ) departmental_flags = DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/perceptomatrix + name = "Perceptomatrix Helm" + id = "perceptomatrix" + desc = "This helm harnesses hallucinative energies to shield its owner from perceptual anomalies. It requires a Hallucination Anomaly Core in order to function." + build_type = PROTOLATHE | AWAY_LATHE + build_path = /obj/item/clothing/head/helmet/perceptomatrix + materials = list( + /datum/material/titanium = SHEET_MATERIAL_AMOUNT * 2, + /datum/material/silver = SHEET_MATERIAL_AMOUNT * 1, + /datum/material/gold = SHEET_MATERIAL_AMOUNT * 0.5, + /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 3, + /datum/material/uranium = SHEET_MATERIAL_AMOUNT, + ) + category = list( + RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SCIENCE + ) + departmental_flags = DEPARTMENT_BITFLAG_SCIENCE diff --git a/code/modules/research/designs/power_designs.dm b/code/modules/research/designs/power_designs.dm index deb2256ff10..38edc5b5593 100644 --- a/code/modules/research/designs/power_designs.dm +++ b/code/modules/research/designs/power_designs.dm @@ -167,10 +167,12 @@ ) departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING -/datum/design/board/bioelec_gen +/datum/design/bioelec_gen name = "Aquarium Bioelectricity Kit" desc = "The required components to convert an aquarium into a bioelectricity generator." id = "bioelec_gen" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/gold = SMALL_MATERIAL_AMOUNT) build_path = /obj/item/aquarium_upgrade/bioelec_gen category = list( RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING, diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 0e6dd68c3e0..c68410caa04 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -39,6 +39,8 @@ . += span_notice("An item can be loaded inside via [EXAMINE_HINT("Left-Click")].") /obj/machinery/rnd/destructive_analyzer/base_item_interaction(mob/living/user, obj/item/weapon, list/modifiers) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + return ..() if(user.combat_mode) return ..() if(!is_insertion_ready(user)) diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm index a82e818758c..95839344469 100644 --- a/code/modules/research/stock_parts.dm +++ b/code/modules/research/stock_parts.dm @@ -18,13 +18,6 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good . = ..() create_storage(storage_type = /datum/storage/rped) -/obj/item/storage/part_replacer/pre_attack(obj/attacked_object, mob/living/user, params) - . = ..() - if(.) - return . - - return part_replace_action(attacked_object, user) - /obj/item/storage/part_replacer/proc/part_replace_action(obj/attacked_object, mob/living/user) if(!ismachinery(attacked_object) || istype(attacked_object, /obj/machinery/computer)) return FALSE diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index b6d3bd74d69..79b8b27986c 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -34,7 +34,7 @@ var/category = "Misc" /// The list of experiments required to research the node var/list/required_experiments = list() - /// If completed, these experiments give a specific point amount discount to the node.area + /// If completed, these experiments give a specific point amount discount to the node. var/list/discount_experiments = list() /// When this node is completed, allows these experiments to be performed. var/list/experiments_to_unlock = list() diff --git a/code/modules/research/techweb/nodes/atmos_nodes.dm b/code/modules/research/techweb/nodes/atmos_nodes.dm index f673d1eb9f0..e6a88bcc78b 100644 --- a/code/modules/research/techweb/nodes/atmos_nodes.dm +++ b/code/modules/research/techweb/nodes/atmos_nodes.dm @@ -64,7 +64,7 @@ "plasmacutter", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS) - required_experiments = list(/datum/experiment/ordnance/gaseous/plasma) + discount_experiments = list(/datum/experiment/ordnance/gaseous/plasma = TECHWEB_TIER_2_POINTS) announce_channels = list(RADIO_CHANNEL_ENGINEERING) /datum/techweb_node/fusion diff --git a/code/modules/research/techweb/nodes/mech_nodes.dm b/code/modules/research/techweb/nodes/mech_nodes.dm index 82d6484bf02..bf520b2fc3d 100644 --- a/code/modules/research/techweb/nodes/mech_nodes.dm +++ b/code/modules/research/techweb/nodes/mech_nodes.dm @@ -96,6 +96,7 @@ "clarke_right_arm", "clarke_main", "clarke_peri", + "mecha_kineticgun", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS) announce_channels = list(RADIO_CHANNEL_SCIENCE, RADIO_CHANNEL_SUPPLY) diff --git a/code/modules/research/techweb/nodes/mining_nodes.dm b/code/modules/research/techweb/nodes/mining_nodes.dm index 1bfa3a9284a..9c0ab9d879d 100644 --- a/code/modules/research/techweb/nodes/mining_nodes.dm +++ b/code/modules/research/techweb/nodes/mining_nodes.dm @@ -56,7 +56,6 @@ description = "Research of Proto-Kinetic Accelerators (PKAs), pneumatic guns renowned for their exceptional performance in low-pressure environments." prereq_ids = list(TECHWEB_NODE_MINING, TECHWEB_NODE_GAS_COMPRESSION) design_ids = list( - "mecha_kineticgun", "damagemod", "rangemod", "cooldownmod", diff --git a/code/modules/research/techweb/nodes/research_nodes.dm b/code/modules/research/techweb/nodes/research_nodes.dm index f05199ab65e..3b58551ec9c 100644 --- a/code/modules/research/techweb/nodes/research_nodes.dm +++ b/code/modules/research/techweb/nodes/research_nodes.dm @@ -49,7 +49,7 @@ "gigabeacon", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS) - required_experiments = list(/datum/experiment/scanning/points/bluespace_crystal) + discount_experiments = list(/datum/experiment/scanning/points/bluespace_crystal) announce_channels = list(RADIO_CHANNEL_SCIENCE, RADIO_CHANNEL_MEDICAL, RADIO_CHANNEL_SERVICE, RADIO_CHANNEL_SUPPLY) /datum/techweb_node/bluespace_travel @@ -94,7 +94,8 @@ "cybernetic_heart_anomalock", "wormholeprojector", "gravitygun", - "polymorph_belt" + "polymorph_belt", + "perceptomatrix", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS) discount_experiments = list(/datum/experiment/scanning/points/anomalies = TECHWEB_TIER_5_POINTS) diff --git a/code/modules/research/techweb/nodes/service_nodes.dm b/code/modules/research/techweb/nodes/service_nodes.dm index 7809ea9580b..d3653c13790 100644 --- a/code/modules/research/techweb/nodes/service_nodes.dm +++ b/code/modules/research/techweb/nodes/service_nodes.dm @@ -175,6 +175,7 @@ "auto_reel", "fish_analyzer", "bluespace_fish_case", + "bluespace_fish_tank_kit", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS) required_experiments = list(/datum/experiment/scanning/fish) diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index 227f1be5165..9376ca9441f 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -59,8 +59,8 @@ /datum/status_effect/slimerecall id = "slime_recall" - duration = -1 //Will be removed by the extract. - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT //Will be removed by the extract. + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null var/interrupted = FALSE var/mob/target @@ -96,7 +96,7 @@ /datum/status_effect/frozenstasis id = "slime_frozen" status_type = STATUS_EFFECT_UNIQUE - duration = -1 //Will remove self when block breaks. + duration = STATUS_EFFECT_PERMANENT //Will remove self when block breaks. alert_type = /atom/movable/screen/alert/status_effect/freon/stasis var/obj/structure/ice_stasis/cube @@ -125,7 +125,7 @@ /datum/status_effect/slime_clone id = "slime_cloned" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null var/mob/living/clone var/datum/mind/originalmind //For when the clone gibs. @@ -169,7 +169,7 @@ /datum/status_effect/slime_clone_decay id = "slime_clonedecay" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/clone_decay /datum/status_effect/slime_clone_decay/tick(seconds_between_ticks) @@ -230,7 +230,7 @@ /datum/status_effect/rebreathing id = "rebreathing" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null /datum/status_effect/rebreathing/tick(seconds_between_ticks) @@ -440,7 +440,7 @@ /datum/status_effect/stabilized //The base stabilized extract effect, has no effect of its' own. id = "stabilizedbase" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null /// Item which provides this buff var/obj/item/slimecross/stabilized/linked_extract @@ -836,7 +836,7 @@ /datum/status_effect/pinkdamagetracker id = "pinkdamagetracker" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null var/damage = 0 var/lasthealth diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm index c3eb811fa42..f114d3ad55d 100644 --- a/code/modules/research/xenobiology/crossbreeding/burning.dm +++ b/code/modules/research/xenobiology/crossbreeding/burning.dm @@ -211,9 +211,6 @@ Burning extracts: effect_desc = "The user gets a dull arm blade in the hand it is used in." /obj/item/slimecross/burning/green/do_effect(mob/user) - var/which_hand = "l_hand" - if(!(user.active_hand_index % 2)) - which_hand = "r_hand" var/mob/living/L = user if(!istype(user)) return @@ -226,7 +223,7 @@ Burning extracts: else user.visible_message(span_danger("[src] sublimates the flesh around [user]'s arm, transforming the bone into a gruesome blade!")) user.emote("scream") - L.apply_damage(30,BURN,which_hand) + L.apply_damage(30, BURN, L.get_active_hand()) ..() /obj/item/slimecross/burning/pink diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm index 0a22cd0380f..c1c599da345 100644 --- a/code/modules/research/xenobiology/crossbreeding/chilling.dm +++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm @@ -252,9 +252,6 @@ Chilling extracts: effect_desc = "Creates a bone gun in the hand it is used in, which uses blood as ammo." /obj/item/slimecross/chilling/green/do_effect(mob/user) - var/which_hand = "l_hand" - if(!(user.active_hand_index % 2)) - which_hand = "r_hand" var/mob/living/L = user if(!istype(user)) return @@ -267,7 +264,7 @@ Chilling extracts: else user.visible_message(span_danger("[src] chills and snaps off the front of the bone on [user]'s arm, leaving behind a strange, gun-like structure!")) user.emote("scream") - L.apply_damage(30,BURN,which_hand) + L.apply_damage(30, BURN, L.get_active_hand()) ..() /obj/item/slimecross/chilling/pink diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index 68ffe5e9248..66cbd4b93de 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -1,22 +1,20 @@ //Xenobio control console -/mob/camera/ai_eye/remote/xenobio - visible_icon = TRUE - icon = 'icons/mob/silicon/cameramob.dmi' - icon_state = "generic_camera" +/mob/eye/camera/remote/xenobio + visible_to_user = TRUE var/allowed_area = null -/mob/camera/ai_eye/remote/xenobio/Initialize(mapload) +/mob/eye/camera/remote/xenobio/Initialize(mapload) var/area/our_area = get_area(loc) allowed_area = our_area.name . = ..() -/mob/camera/ai_eye/remote/xenobio/setLoc(turf/destination, force_update = FALSE) +/mob/eye/camera/remote/xenobio/setLoc(turf/destination, force_update = FALSE) var/area/new_area = get_area(destination) if(new_area && new_area.name == allowed_area || new_area && (new_area.area_flags & XENOBIOLOGY_COMPATIBLE)) return ..() -/mob/camera/ai_eye/remote/xenobio/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) +/mob/eye/camera/remote/xenobio/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) . = ..() if(!.) return @@ -84,11 +82,9 @@ stored_slimes -= gone /obj/machinery/computer/camera_advanced/xenobio/CreateEye() - eyeobj = new /mob/camera/ai_eye/remote/xenobio(get_turf(src)) - eyeobj.origin = src - eyeobj.visible_icon = TRUE - eyeobj.icon = 'icons/mob/silicon/cameramob.dmi' - eyeobj.icon_state = "generic_camera" + eyeobj = new /mob/eye/camera/remote/xenobio(get_turf(src), src) + + return TRUE /obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user) . = ..() @@ -151,7 +147,7 @@ Boilerplate check for a valid area to perform a camera action in. Checks if the AI eye is on a valid turf and then checks if the target turf is xenobiology compatible Due to keyboard shortcuts, the second one is not necessarily the remote eye's location. */ -/obj/machinery/computer/camera_advanced/xenobio/proc/validate_area(mob/living/user, mob/camera/ai_eye/remote/xenobio/remote_eye, turf/open/target_turf) +/obj/machinery/computer/camera_advanced/xenobio/proc/validate_area(mob/living/user, mob/eye/camera/remote/xenobio/remote_eye, turf/open/target_turf) if(!GLOB.cameranet.checkTurfVis(remote_eye.loc)) to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) return FALSE @@ -169,6 +165,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo stored_slime.forceMove(target_turf) stored_slime.visible_message(span_notice("[stored_slime] warps in!")) stored_slimes -= stored_slime + REMOVE_TRAIT(stored_slime, TRAIT_STASIS, XENOBIO_CONSOLE_TRAIT) ///Places every slime not controlled by a player into the internal storage, respecting its limits ///Returns TRUE to signal it hitting the limit, in case its being called from a loop and we want it to stop @@ -184,6 +181,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo target_slime.visible_message(span_notice("[target_slime] vanishes in a flash of light!")) target_slime.forceMove(src) stored_slimes += target_slime + ADD_TRAIT(target_slime, TRAIT_STASIS, XENOBIO_CONSOLE_TRAIT) return FALSE @@ -226,7 +224,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!target || !isliving(owner)) return var/mob/living/owner_mob = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) @@ -243,7 +241,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!target || !isliving(owner)) return var/mob/living/owner_mob = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) @@ -262,7 +260,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!target || !isliving(owner)) return var/mob/living/living_owner = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = living_owner.remote_control + var/mob/eye/camera/remote/xenobio/remote_eye = living_owner.remote_control var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) @@ -280,7 +278,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!target || !isliving(owner)) return var/mob/living/owner_mob = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target var/obj/machinery/monkey_recycler/recycler = xeno_console.connected_recycler @@ -303,7 +301,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!target || !isliving(owner)) return var/mob/living/owner_mob = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) @@ -322,7 +320,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo return var/mob/living/owner_mob = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) @@ -373,8 +371,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!isslime(target_slime)) return - var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve() if(!xeno_console.validate_area(user, remote_eye, target_slime.loc)) return @@ -389,8 +387,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo /obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickShift(mob/living/user, mob/living/basic/slime/target_slime) SIGNAL_HANDLER - var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve() if(!xeno_console.validate_area(user, remote_eye, target_slime.loc)) return @@ -402,8 +400,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo SIGNAL_HANDLER var/mob/living/user_mob = user - var/mob/camera/ai_eye/remote/xenobio/remote_eye = user_mob.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + var/mob/eye/camera/remote/xenobio/remote_eye = user_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve() if(!xeno_console.validate_area(user, remote_eye, target_turf)) return @@ -428,8 +426,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo return var/cleanup = FALSE - var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve() if(!xeno_console.validate_area(user, remote_eye, target_turf)) return @@ -447,8 +445,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!ismonkey(target_mob)) return - var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve() if(!xeno_console.connected_recycler) to_chat(user, span_warning("There is no connected monkey recycler. Use a multitool to link one.")) @@ -464,8 +462,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo if(!isslime(target_slime)) return - var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve() if(!xeno_console.validate_area(user, remote_eye, target_slime.loc)) return diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm index 3eec11d0b40..7c588e06dc0 100644 --- a/code/modules/shuttle/navigation_computer.dm +++ b/code/modules/shuttle/navigation_computer.dm @@ -107,8 +107,8 @@ shuttle_port = null return - eyeobj = new /mob/camera/ai_eye/remote/shuttle_docker(null, src) - var/mob/camera/ai_eye/remote/shuttle_docker/the_eye = eyeobj + eyeobj = new /mob/eye/camera/remote/shuttle_docker(null, src) + var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj the_eye.setDir(shuttle_port.dir) var/turf/origin = locate(shuttle_port.x + x_offset, shuttle_port.y + y_offset, shuttle_port.z) for(var/area/shuttle_area as anything in shuttle_port.shuttle_areas) @@ -125,10 +125,12 @@ I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT the_eye.placement_images[I] = list(x_off, y_off) + return TRUE + /obj/machinery/computer/camera_advanced/shuttle_docker/give_eye_control(mob/user) ..() if(!QDELETED(user) && user.client) - var/mob/camera/ai_eye/remote/shuttle_docker/the_eye = eyeobj + var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj var/list/to_add = list() to_add += the_eye.placement_images to_add += the_eye.placed_images @@ -141,7 +143,7 @@ /obj/machinery/computer/camera_advanced/shuttle_docker/remove_eye_control(mob/living/user) ..() if(!QDELETED(user) && user.client) - var/mob/camera/ai_eye/remote/shuttle_docker/the_eye = eyeobj + var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj var/list/to_remove = list() to_remove += the_eye.placement_images to_remove += the_eye.placed_images @@ -155,7 +157,7 @@ if(designating_target_loc || !current_user) return - var/mob/camera/ai_eye/remote/shuttle_docker/the_eye = eyeobj + var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj var/landing_clear = checkLandingSpot() if(designate_time && (landing_clear != SHUTTLE_DOCKER_BLOCKED)) to_chat(current_user, span_warning("Targeting transit location, please wait [DisplayTimeText(designate_time)]...")) @@ -223,7 +225,7 @@ return TRUE /obj/machinery/computer/camera_advanced/shuttle_docker/proc/rotateLandingSpot() - var/mob/camera/ai_eye/remote/shuttle_docker/the_eye = eyeobj + var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj var/list/image_cache = the_eye.placement_images the_eye.setDir(turn(the_eye.dir, -90)) for(var/i in 1 to image_cache.len) @@ -239,7 +241,7 @@ checkLandingSpot() /obj/machinery/computer/camera_advanced/shuttle_docker/proc/checkLandingSpot() - var/mob/camera/ai_eye/remote/shuttle_docker/the_eye = eyeobj + var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj var/turf/eyeturf = get_turf(the_eye) if(!eyeturf) return SHUTTLE_DOCKER_BLOCKED @@ -316,22 +318,17 @@ add_jumpable_port(dock.shuttle_id) return TRUE -/mob/camera/ai_eye/remote/shuttle_docker - visible_icon = FALSE - use_static = FALSE +/mob/eye/camera/remote/shuttle_docker + use_visibility = FALSE var/list/image/placement_images = list() var/list/image/placed_images = list() -/mob/camera/ai_eye/remote/shuttle_docker/Initialize(mapload, obj/machinery/computer/camera_advanced/origin) - src.origin = origin - return ..() - -/mob/camera/ai_eye/remote/shuttle_docker/setLoc(turf/destination, force_update = FALSE) +/mob/eye/camera/remote/shuttle_docker/setLoc(turf/destination, force_update = FALSE) . = ..() - var/obj/machinery/computer/camera_advanced/shuttle_docker/console = origin + var/obj/machinery/computer/camera_advanced/shuttle_docker/console = origin_ref?.resolve() console.checkLandingSpot() -/mob/camera/ai_eye/remote/shuttle_docker/update_remote_sight(mob/living/user) +/mob/eye/camera/remote/shuttle_docker/update_remote_sight(mob/living/user) user.set_sight(BLIND|SEE_TURFS) // Pale blue, should look nice I think user.lighting_color_cutoffs = list(30, 40, 50) @@ -346,8 +343,8 @@ /datum/action/innate/shuttledocker_rotate/Activate() if(QDELETED(owner) || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control - var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin + var/mob/eye/camera/remote/remote_eye = owner.remote_control + var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin_ref.resolve() origin.rotateLandingSpot() /datum/action/innate/shuttledocker_place @@ -358,8 +355,8 @@ /datum/action/innate/shuttledocker_place/Activate() if(QDELETED(owner) || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control - var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin + var/mob/eye/camera/remote/remote_eye = owner.remote_control + var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin_ref.resolve() origin.placeLandingSpot(owner) /datum/action/innate/camera_jump/shuttle_docker @@ -369,8 +366,8 @@ /datum/action/innate/camera_jump/shuttle_docker/Activate() if(QDELETED(owner) || !isliving(owner)) return - var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control - var/obj/machinery/computer/camera_advanced/shuttle_docker/console = remote_eye.origin + var/mob/eye/camera/remote/remote_eye = owner.remote_control + var/obj/machinery/computer/camera_advanced/shuttle_docker/console = remote_eye.origin_ref.resolve() playsound(console, 'sound/machines/terminal/terminal_prompt_deny.ogg', 25, FALSE) diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 98b998baf42..b5b7cc6f6ef 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -151,7 +151,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( investigate_log("Chef's [SSshuttle.chef_groceries.len] sized produce order arrived. Cost was deducted from orderer, not cargo.", INVESTIGATE_CARGO) for(var/datum/orderable_item/item as anything in SSshuttle.chef_groceries)//every order for(var/amt in 1 to SSshuttle.chef_groceries[item])//every order amount - new item.item_path(grocery_crate) + new item.purchase_path(grocery_crate) SSshuttle.chef_groceries.Cut() //This lets the console know it can order another round. if(!SSshuttle.shopping_list.len) @@ -277,7 +277,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists()) for(var/turf/shuttle_turf as anything in zlevel_turfs) for(var/atom/movable/exporting_atom in shuttle_turf) - if(iscameramob(exporting_atom)) + if(iseyemob(exporting_atom)) continue if(exporting_atom.anchored) continue diff --git a/code/modules/spells/spell_types/jaunt/bloodcrawl.dm b/code/modules/spells/spell_types/jaunt/bloodcrawl.dm index 2795efc2b20..aa6a58b1341 100644 --- a/code/modules/spells/spell_types/jaunt/bloodcrawl.dm +++ b/code/modules/spells/spell_types/jaunt/bloodcrawl.dm @@ -5,7 +5,7 @@ */ /datum/action/cooldown/spell/jaunt/bloodcrawl name = "Blood Crawl" - desc = "Allows you to phase in and out of existance via pools of blood." + desc = "Allows you to phase in and out of existence via pools of blood." background_icon_state = "bg_demon" overlay_icon_state = "bg_demon_border" @@ -158,7 +158,7 @@ */ /datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon name = "Voracious Blood Crawl" - desc = "Allows you to phase in and out of existance via pools of blood. If you are dragging someone in critical or dead, \ + desc = "Allows you to phase in and out of existence via pools of blood. If you are dragging someone in critical or dead, \ they will be consumed by you, fully healing you." /// The sound played when someone's consumed. var/consume_sound = 'sound/effects/magic/demon_consume.ogg' @@ -285,7 +285,7 @@ */ /datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon/funny name = "Friendly Blood Crawl" - desc = "Allows you to phase in and out of existance via pools of blood. If you are dragging someone in critical or dead - I mean, \ + desc = "Allows you to phase in and out of existence via pools of blood. If you are dragging someone in critical or dead - I mean, \ sleeping, when entering a blood pool, they will be invited to a party and fully heal you!" consume_sound = 'sound/misc/scary_horn.ogg' @@ -332,7 +332,7 @@ // Heals them back to state one if(!friend.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE)) continue - playsound(release_turf, consumed_mobs, 50, TRUE, -1) + friend.playsound_local(release_turf, 'sound/effects/magic/exit_blood.ogg', 50, TRUE, -1) to_chat(friend, span_clown("You leave [source]'s warm embrace, and feel ready to take on the world.")) diff --git a/code/modules/spells/spell_types/pointed/_pointed.dm b/code/modules/spells/spell_types/pointed/_pointed.dm index 9189106d874..a96e2fe9fd2 100644 --- a/code/modules/spells/spell_types/pointed/_pointed.dm +++ b/code/modules/spells/spell_types/pointed/_pointed.dm @@ -101,7 +101,7 @@ */ /datum/action/cooldown/spell/pointed/projectile /// What projectile we create when we shoot our spell. - var/obj/projectile/magic/projectile_type = /obj/projectile/magic/teleport + var/obj/projectile/projectile_type = /obj/projectile/magic/teleport /// How many projectiles we can fire per cast. Not all at once, per click, kinda like charges var/projectile_amount = 1 /// How many projectiles we have yet to fire, based on projectile_amount diff --git a/code/modules/spells/spell_types/self/splattercasting_spell.dm b/code/modules/spells/spell_types/self/splattercasting_spell.dm index 184a2afab7c..e76f8e3c1b9 100644 --- a/code/modules/spells/spell_types/self/splattercasting_spell.dm +++ b/code/modules/spells/spell_types/self/splattercasting_spell.dm @@ -28,7 +28,7 @@ brings unimaginable momentary torment as your heart stops, and your skin grows cold. You are now \ merely a vessel for the arcane flow. Soon, all that is left is not pain, but hunger.")) - cast_on.set_species(/datum/species/vampire) + cast_on.set_species(/datum/species/human/vampire) cast_on.blood_volume = BLOOD_VOLUME_NORMAL ///for predictable blood total amounts when the spell is first cast. cast_on.AddComponent(/datum/component/splattercasting) diff --git a/code/modules/spells/spell_types/shapeshift/_shape_status.dm b/code/modules/spells/spell_types/shapeshift/_shape_status.dm index 2f8d53eeac2..9e1284d27f6 100644 --- a/code/modules/spells/spell_types/shapeshift/_shape_status.dm +++ b/code/modules/spells/spell_types/shapeshift/_shape_status.dm @@ -194,7 +194,7 @@ if(owner?.contents) // Prevent round removal and consuming stuff when losing shapeshift for(var/atom/movable/thing as anything in owner.contents) - if(thing == caster_mob) + if(thing == caster_mob || HAS_TRAIT(thing, TRAIT_NOT_BARFABLE)) continue thing.forceMove(get_turf(owner)) @@ -243,6 +243,7 @@ desc = "Your form is not your own... you're shapeshifted into another creature! \ A wizard could turn you back - or maybe you're stuck like this for good?" icon_state = "shapeshifted" + clickable_glow = TRUE /atom/movable/screen/alert/status_effect/shapeshifted/Click(location, control, params) . = ..() diff --git a/code/modules/spells/spell_types/tower_of_babel.dm b/code/modules/spells/spell_types/tower_of_babel.dm index 618711a8d95..6c7374b4420 100644 --- a/code/modules/spells/spell_types/tower_of_babel.dm +++ b/code/modules/spells/spell_types/tower_of_babel.dm @@ -53,7 +53,7 @@ GLOBAL_DATUM(tower_of_babel, /datum/tower_of_babel) to_chat(to_curse, span_notice("You have a strange feeling for a moment, but then it passes.")) return - to_curse.apply_status_effect(/datum/status_effect/tower_of_babel/magical, INFINITE) + to_curse.apply_status_effect(/datum/status_effect/tower_of_babel/magical, INFINITY) return TRUE /// Mainly so admin triggered tower of babel can be undone diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 683afe605c9..7467f85d79e 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -212,7 +212,7 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) var/turf/point = get_front_turf() var/turf/target = get_target_turf() - var/atom/movable/blocker + var/atom/blocker for(var/T in get_line(get_step(point, dir), target)) var/turf/tile = T if(SEND_SIGNAL(tile, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index dec8efb154e..2f9a42e0d1f 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -40,11 +40,12 @@ ///Get the bodypart for whatever hand we have active, Only relevant for carbons /mob/proc/get_active_hand() + RETURN_TYPE(/obj/item/bodypart) return FALSE /mob/living/carbon/get_active_hand() var/which_hand = BODY_ZONE_PRECISE_L_HAND - if(!(active_hand_index % RIGHT_HANDS)) + if(IS_RIGHT_INDEX(active_hand_index)) which_hand = BODY_ZONE_PRECISE_R_HAND return get_bodypart(check_zone(which_hand)) @@ -54,7 +55,7 @@ /mob/living/carbon/get_inactive_hand() var/which_hand = BODY_ZONE_PRECISE_R_HAND - if(!(active_hand_index % RIGHT_HANDS)) + if(IS_RIGHT_INDEX(active_hand_index)) which_hand = BODY_ZONE_PRECISE_L_HAND return get_bodypart(check_zone(which_hand)) @@ -64,7 +65,7 @@ /mob/living/carbon/has_left_hand(check_disabled = TRUE) for(var/obj/item/bodypart/hand_instance in hand_bodyparts) - if(!(hand_instance.held_index % RIGHT_HANDS) || (check_disabled && hand_instance.bodypart_disabled)) + if(IS_RIGHT_INDEX(hand_instance.held_index) || (check_disabled && hand_instance.bodypart_disabled)) continue return TRUE return FALSE @@ -80,7 +81,7 @@ /mob/living/carbon/has_right_hand(check_disabled = TRUE) for(var/obj/item/bodypart/hand_instance in hand_bodyparts) - if(hand_instance.held_index % RIGHT_HANDS || (check_disabled && hand_instance.bodypart_disabled)) + if(IS_LEFT_INDEX(hand_instance.held_index) || (check_disabled && hand_instance.bodypart_disabled)) continue return TRUE return FALSE diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm index 25f66f40af9..c7e1f7f7846 100644 --- a/code/modules/surgery/bodyparts/parts.dm +++ b/code/modules/surgery/bodyparts/parts.dm @@ -106,7 +106,7 @@ bodypart_flags = BODYPART_UNREMOVABLE max_damage = LIMB_MAX_HP_ALIEN_CORE acceptable_bodyshape = BODYSHAPE_HUMANOID - wing_types = NONE + wing_types = null /obj/item/bodypart/chest/larva icon = 'icons/mob/human/species/alien/bodyparts.dmi' @@ -119,7 +119,7 @@ max_damage = LIMB_MAX_HP_ALIEN_LARVA bodytype = BODYTYPE_LARVA_PLACEHOLDER | BODYTYPE_ORGANIC acceptable_bodytype = BODYTYPE_LARVA_PLACEHOLDER - wing_types = NONE + wing_types = null /// Parent Type for arms, should not appear in game. /obj/item/bodypart/arm diff --git a/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm index 756cda32fa3..3b1d56ee5b6 100644 --- a/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm @@ -22,7 +22,7 @@ is_dimorphic = FALSE dmg_overlay_type = null brute_modifier = 1.25 //ethereal are weak to brute damages - wing_types = NONE + wing_types = null bodypart_traits = list(TRAIT_NO_UNDERWEAR) /obj/item/bodypart/chest/ethereal/update_limb(dropping_limb, is_creating) diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index 08b22373d32..74219a32cf2 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -12,7 +12,7 @@ is_dimorphic = FALSE burn_modifier = 2 biological_state = (BIO_FLESH|BIO_BLOODED) - wing_types = NONE + wing_types = null /obj/item/bodypart/arm/left/snail limb_id = SPECIES_SNAIL @@ -58,7 +58,7 @@ limb_id = SPECIES_ABDUCTOR is_dimorphic = FALSE should_draw_greyscale = FALSE - wing_types = NONE + wing_types = null /obj/item/bodypart/chest/abductor/get_butt_sprite() return icon('icons/mob/butts.dmi', BUTT_SPRITE_GREY) @@ -177,7 +177,7 @@ limb_id = SPECIES_ZOMBIE is_dimorphic = FALSE should_draw_greyscale = FALSE - wing_types = NONE + wing_types = null /obj/item/bodypart/arm/left/zombie limb_id = SPECIES_ZOMBIE @@ -216,7 +216,7 @@ limb_id = SPECIES_PODPERSON is_dimorphic = TRUE burn_modifier = 1.25 - wing_types = NONE + wing_types = null /obj/item/bodypart/chest/pod/get_butt_sprite() return icon('icons/mob/butts.dmi', BUTT_SPRITE_FLOWERPOT) @@ -292,7 +292,7 @@ is_dimorphic = FALSE should_draw_greyscale = FALSE burn_modifier = 1.5 - wing_types = NONE + wing_types = null /obj/item/bodypart/arm/left/shadow limb_id = SPECIES_SHADOW @@ -380,7 +380,7 @@ is_dimorphic = TRUE bodypart_traits = list(TRAIT_NO_JUMPSUIT) burn_modifier = 1.25 - wing_types = NONE + wing_types = null /obj/item/bodypart/arm/left/mushroom limb_id = SPECIES_MUSHROOM @@ -483,7 +483,7 @@ should_draw_greyscale = FALSE dmg_overlay_type = null bodypart_traits = list(TRAIT_NO_JUMPSUIT) - wing_types = NONE + wing_types = null /obj/item/bodypart/chest/golem/Initialize(mapload) worn_belt_offset = new( diff --git a/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm index 0125601bda5..b668008e44c 100644 --- a/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm @@ -28,7 +28,7 @@ brute_modifier = 1.5 //Plasmemes are weak burn_modifier = 1.5 //Plasmemes are weak bodypart_flags = BODYPART_UNHUSKABLE - wing_types = NONE + wing_types = null /obj/item/bodypart/chest/plasmaman/get_butt_sprite() return icon('icons/mob/butts.dmi', BUTT_SPRITE_PLASMA) diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index 6c933c53a4b..1bb5e1b7e2b 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -12,6 +12,38 @@ /datum/surgery_step/close, ) +//So far, this surgery type should be the only way carbon mobs can be fishing spots, also because the comp doesn't allow dupes. +/datum/surgery/organ_manipulation/next_step(mob/living/user, modifiers) + . = ..() + if(!.) + return + if(!ispath(steps[status], /datum/surgery_step/manipulate_organs)) + //The manipulate_organs step either hasn't been reached yet or we're already past it. + if(!HAS_TRAIT(target, TRAIT_FISHING_SPOT)) + return + remove_fishing_spot() + return + + if(HAS_TRAIT(target, TRAIT_FISHING_SPOT)) + return + target.AddComponent(/datum/component/fishing_spot, /datum/fish_source/surgery) + +/datum/surgery/organ_manipulation/Destroy() + if(HAS_TRAIT(target, TRAIT_FISHING_SPOT) && ispath(steps[status], /datum/surgery_step/manipulate_organs)) + remove_fishing_spot() + return ..() + +/** + * The target is a fishing spot, but we're past the step that allows us to fish organs from him, so we need + * to check if there are other organ manipulation surgeries that still meet this criteria before we remove + * the component + */ +/datum/surgery/organ_manipulation/proc/remove_fishing_spot() + for(var/datum/surgery/organ_manipulation/manipulation in target.surgeries) + if(ispath(manipulation.steps[manipulation.status], /datum/surgery_step/manipulate_organs)) + return + qdel(target.GetComponent(/datum/component/fishing_spot)) + /datum/surgery/organ_manipulation/soft possible_locs = list(BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) steps = list( @@ -57,7 +89,7 @@ /datum/surgery/organ_manipulation/mechanic name = "Hardware Manipulation" requires_bodypart_type = BODYTYPE_ROBOTIC - surgery_flags = SURGERY_SELF_OPERABLE | SURGERY_REQUIRE_LIMB + surgery_flags = SURGERY_SELF_OPERABLE | SURGERY_REQUIRE_LIMB | SURGERY_CHECK_TOOL_BEHAVIOUR possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD) steps = list( /datum/surgery_step/mechanic_open, @@ -69,32 +101,6 @@ /datum/surgery_step/mechanic_close, ) -/datum/surgery/organ_manipulation/mechanic/next_step(mob/living/user, modifiers) - if(location != user.zone_selected) - return FALSE - if(user.combat_mode) - return FALSE - if(step_in_progress) - return TRUE - - var/try_to_fail = FALSE - if(LAZYACCESS(modifiers, RIGHT_CLICK)) - try_to_fail = TRUE - - var/datum/surgery_step/step = get_surgery_step() - if(isnull(step)) - return FALSE - var/obj/item/tool = user.get_active_held_item() - if(tool) - tool = tool.get_proxy_attacker_for(target, user) - if(step.try_op(user, target, user.zone_selected, tool, src, try_to_fail)) - return TRUE - if(tool && tool.tool_behaviour) //Mechanic organ manipulation isn't done with just surgery tools - to_chat(user, span_warning("This step requires a different tool!")) - return TRUE - - return FALSE - /datum/surgery/organ_manipulation/mechanic/soft possible_locs = list( BODY_ZONE_PRECISE_GROIN, @@ -176,17 +182,15 @@ to_chat(user, span_warning("[target_organ] seems to have been chewed on, you can't use this!")) return SURGERY_STEP_FAIL - if(istype(target_organ, /obj/item/organ/brain)) if(istype(target_organ, /obj/item/organ/brain/cybernetic/ipc) && !isipc(target) && target_organ.zone != BODY_ZONE_HEAD) to_chat(user, span_warning("There is no room for [target_organ] in [target]'s [target.parse_zone_with_bodypart(target_zone)]!")) return SURGERY_STEP_FAIL - else if(isipc(target) && target_organ.zone == BODY_ZONE_HEAD) to_chat(user, span_warning("There is no room for [target_organ] in [target]'s [target.parse_zone_with_bodypart(target_zone)]!")) return SURGERY_STEP_FAIL - if(!can_use_organ(user, meatslab)) + if(!can_use_organ(meatslab)) return SURGERY_STEP_FAIL if (target_zone == BODY_ZONE_PRECISE_EYES) @@ -206,7 +210,7 @@ var/list/unfiltered_organs = target.get_organs_for_zone(target_zone) var/list/organs = list() for(var/organ in unfiltered_organs) - if(can_use_organ(user, organ)) + if(can_use_organ(organ)) organs.Add(organ) if (target_zone == BODY_ZONE_PRECISE_EYES) target_zone = check_zone(target_zone) @@ -305,7 +309,7 @@ return ..() ///You can never use this MUHAHAHAHAHAHAH (because its the byond version of abstract) -/datum/surgery_step/manipulate_organs/proc/can_use_organ(mob/user, obj/item/organ/organ) +/datum/surgery_step/manipulate_organs/proc/can_use_organ(obj/item/organ/organ) return FALSE ///Surgery step for internal organs, like hearts and brains @@ -314,7 +318,7 @@ name = "manipulate organs (hemostat/organ)" ///only operate on internal organs -/datum/surgery_step/manipulate_organs/internal/can_use_organ(mob/user, obj/item/organ/organ) +/datum/surgery_step/manipulate_organs/internal/can_use_organ(obj/item/organ/organ) return !(organ.organ_flags & ORGAN_EXTERNAL) ///prosthetic surgery gives full effectiveness to crowbars (and hemostats) @@ -328,7 +332,7 @@ name = "manipulate features (hemostat/feature)" ///Only operate on external organs -/datum/surgery_step/manipulate_organs/external/can_use_organ(mob/user, obj/item/organ/organ) +/datum/surgery_step/manipulate_organs/external/can_use_organ(obj/item/organ/organ) return (organ.organ_flags & ORGAN_EXTERNAL) ///prosthetic surgery gives full effectiveness to crowbars (and hemostats) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index e2f05a9ed56..b8784dfdea0 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -126,10 +126,6 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) return owner.remove_status_effect(status, type) -/obj/item/organ/proc/on_owner_examine(datum/source, mob/user, list/examine_list) - SIGNAL_HANDLER - return - /obj/item/organ/proc/on_find(mob/living/finder) return diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 296b40beea9..83fb98e4844 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -8,7 +8,7 @@ w_class = WEIGHT_CLASS_SMALL /// How many times you can use the autosurgeon before it becomes useless - var/uses = INFINITE + var/uses = INFINITY /// What organ will the autosurgeon sub-type will start with. ie, CMO autosurgeon start with a medi-hud. var/starting_organ /// The organ currently loaded in the autosurgeon, ready to be implanted. @@ -40,7 +40,7 @@ to_chat(user, span_alert("[src] already has an implant stored.")) return - if(uses == 0) + if(uses <= 0) to_chat(user, span_alert("[src] is used up and cannot be loaded with more implants.")) return @@ -69,7 +69,7 @@ to_chat(user, span_alert("[src] currently has no implant stored.")) return - if(!uses) + if(uses <= 0) to_chat(user, span_alert("[src] has already been used. The tools are dull and won't reactivate.")) return @@ -96,9 +96,8 @@ playsound(target.loc, 'sound/items/weapons/circsawhit.ogg', 50, vary = TRUE) update_appearance() - if(uses) - uses-- - if(uses == 0) + uses-- + if(uses <= 0) desc = "[initial(desc)] Looks like it's been used up." /obj/item/autosurgeon/attack_self(mob/user)//when the object it used... @@ -129,9 +128,8 @@ stored_organ = null screwtool.play_tool_sound(src) - if (uses) - uses-- - if(!uses) + uses-- + if(uses <= 0) desc = "[initial(desc)] Looks like it's been used up." update_appearance(UPDATE_ICON) return TRUE diff --git a/code/modules/surgery/organs/external/_visual_organs.dm b/code/modules/surgery/organs/external/_visual_organs.dm index 0b01a174d09..26b089333b2 100644 --- a/code/modules/surgery/organs/external/_visual_organs.dm +++ b/code/modules/surgery/organs/external/_visual_organs.dm @@ -212,13 +212,13 @@ Unlike normal organs, we're actually inside a persons limbs at all times ///Store our old datum here for if our antennae are healed var/original_sprite_datum -/obj/item/organ/antennae/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/antennae/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_antennae)) RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_antennae)) -/obj/item/organ/antennae/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/antennae/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL)) diff --git a/code/modules/surgery/organs/external/spines.dm b/code/modules/surgery/organs/external/spines.dm index 23c29358a0d..77e0f63e6b0 100644 --- a/code/modules/surgery/organs/external/spines.dm +++ b/code/modules/surgery/organs/external/spines.dm @@ -16,13 +16,13 @@ organ_flags = parent_type::organ_flags | ORGAN_EXTERNAL -/obj/item/organ/spines/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/spines/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) // If we have a tail, attempt to add a tail spines overlay var/obj/item/organ/tail/our_tail = receiver.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL) our_tail?.try_insert_tail_spines(our_tail.bodypart_owner) return ..() -/obj/item/organ/spines/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/spines/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) // If we have a tail, remove any tail spines overlay var/obj/item/organ/tail/our_tail = organ_owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL) our_tail?.remove_tail_spines(our_tail.bodypart_owner) diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm index 8004f88a680..e6c382387a2 100644 --- a/code/modules/surgery/organs/external/tails.dm +++ b/code/modules/surgery/organs/external/tails.dm @@ -22,11 +22,10 @@ ///The overlay for tail spines, if any var/datum/bodypart_overlay/mutant/tail_spines/tail_spines_overlay -/obj/item/organ/tail/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/tail/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() - if(.) - receiver.clear_mood_event("tail_lost") - receiver.clear_mood_event("tail_balance_lost") + receiver.clear_mood_event("tail_lost") + receiver.clear_mood_event("tail_balance_lost") if(!special) // if some admin wants to give someone tail moodles for tail shenanigans, they can spawn it and do it by hand original_owner ||= WEAKREF(receiver) @@ -77,7 +76,7 @@ bodypart.remove_bodypart_overlay(tail_spines_overlay) QDEL_NULL(tail_spines_overlay) -/obj/item/organ/tail/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/tail/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(wag_flags & WAG_WAGGING) diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index 63759761548..77589445834 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -33,14 +33,14 @@ QDEL_NULL(fly) return ..() -/obj/item/organ/wings/functional/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/wings/functional/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() if(QDELETED(fly)) fly = new fly.Grant(receiver) -/obj/item/organ/wings/functional/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/wings/functional/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() fly?.Remove(organ_owner) if(wings_open) @@ -145,7 +145,7 @@ if (get_dir(source, backup) == movement_dir || source.loc == backup.loc) return - if (!can_fly(source) || !source.client.intended_direction) + if (!can_fly(source) || !source.client.intended_direction || (source.client.intended_direction & get_dir(source, backup))) return return COMPONENT_PREVENT_SPACEMOVE_HALT diff --git a/code/modules/surgery/organs/external/wings/moth_wings.dm b/code/modules/surgery/organs/external/wings/moth_wings.dm index 2753c88f2c4..265a9ee7512 100644 --- a/code/modules/surgery/organs/external/wings/moth_wings.dm +++ b/code/modules/surgery/organs/external/wings/moth_wings.dm @@ -82,7 +82,7 @@ if (get_dir(source, backup) == movement_dir || source.loc == backup.loc) return - if (!allow_flight() || !source.client.intended_direction) + if (!allow_flight() || !source.client.intended_direction || (source.client.intended_direction & get_dir(source, backup))) return return COMPONENT_PREVENT_SPACEMOVE_HALT diff --git a/code/modules/surgery/organs/internal/_internal_organ.dm b/code/modules/surgery/organs/internal/_internal_organ.dm index 0527e6d7980..20b9f8f90f1 100644 --- a/code/modules/surgery/organs/internal/_internal_organ.dm +++ b/code/modules/surgery/organs/internal/_internal_organ.dm @@ -14,7 +14,7 @@ STOP_PROCESSING(SSobj, src) -/obj/item/organ/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE) +/obj/item/organ/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags) . = ..() if((organ_flags & ORGAN_VITAL) && !special && !HAS_TRAIT(organ_owner, TRAIT_GODMODE)) diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm index a07b8caed6c..7d0b249bdbc 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm @@ -278,6 +278,35 @@ /obj/item/organ/cyberimp/arm/toolset/l zone = BODY_ZONE_L_ARM +//The order of the item list for this implant is not alphabetized due to it actually affecting how it shows up playerside when opening the implant +/obj/item/organ/cyberimp/arm/paperwork + name = "integrated paperwork implant" + desc = "A highly sought out implant among heads of personnel, and other high up command staff in Nanotrasen. This implant allows the user to always have the tools necesarry for paperwork handy" + icon_state = "toolkit_engineering" + actions_types = list(/datum/action/item_action/organ_action/toggle/toolkit) + items_to_create = list( + /obj/item/pen/fountain, + /obj/item/clipboard, + /obj/item/taperecorder, + /obj/item/lighter, + /obj/item/laser_pointer, + /obj/item/stamp, + /obj/item/stamp/denied, + ) + +/obj/item/organ/cyberimp/arm/paperwork/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/cyberimp/arm/paperwork/emag_act(mob/user, obj/item/card/emag/emag_card) + for(var/datum/weakref/created_item in items_list) + var/obj/potential_tool = created_item.resolve() + if(istype(/obj/item/stamp/chameleon, potential_tool)) + return FALSE + + balloon_alert(user, "experimental stamp unlocked") + items_list += WEAKREF(new /obj/item/stamp/chameleon(src)) + return TRUE + /obj/item/organ/cyberimp/arm/toolset/emag_act(mob/user, obj/item/card/emag/emag_card) for(var/datum/weakref/created_item in items_list) var/obj/potential_knife = created_item.resolve() diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm index 6a1e4272cd5..8b77ef5ec78 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm @@ -292,7 +292,7 @@ organ_owner.AddElement(/datum/element/forced_gravity, 1) add_organ_trait(TRAIT_STURDY_FRAME) -/obj/item/organ/cyberimp/chest/spine/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/cyberimp/chest/spine/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() remove_organ_trait(TRAIT_BOULDER_BREAKER) if(stone_overlay) diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm index d720953a24c..204247e4de8 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm @@ -27,13 +27,13 @@ eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT) balloon_alert(eye_owner, "hud enabled") -/obj/item/organ/cyberimp/eyes/hud/mob_insert(mob/living/carbon/eye_owner, special = FALSE, movement_flags) +/obj/item/organ/cyberimp/eyes/hud/on_mob_insert(mob/living/carbon/eye_owner, special = FALSE, movement_flags) . = ..() eye_owner.add_traits(HUD_traits, ORGAN_TRAIT) toggled_on = TRUE -/obj/item/organ/cyberimp/eyes/hud/mob_remove(mob/living/carbon/eye_owner, special, movement_flags) +/obj/item/organ/cyberimp/eyes/hud/on_mob_remove(mob/living/carbon/eye_owner, special, movement_flags) . = ..() eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT) toggled_on = FALSE diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index f565fc3b42c..3443cf9249e 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -52,7 +52,7 @@ . = ..() update_temp_deafness() -/obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_MOB_SAY) REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE) @@ -242,7 +242,7 @@ /obj/item/organ/ears/cybernetic/xray name = "wall-penetrating cybernetic ears" icon_state = "ears-c-u" - desc = "Throguh the power of modern engineering, allows the user to hear speech through walls. The user becomes extra vulnerable to loud noises, however" + desc = "Through the power of modern engineering, allows the user to hear speech through walls. The user becomes extra vulnerable to loud noises, however" // Same sensitivity as felinid ears damage_multiplier = 2 diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index a842873b4b9..13785fea077 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -41,10 +41,6 @@ var/eye_color_right = "" //set to a hex code to override a mob's right eye color var/eye_icon = 'icons/mob/human/human_face.dmi' var/eye_icon_state = "eyes" - /// The color of the previous left eye before this one was inserted - var/old_eye_color_left = "fff" - /// The color of the previous right eye before this one was inserted - var/old_eye_color_right = "fff" /// Glasses cannot be worn over these eyes. Currently unused var/no_glasses = FALSE @@ -55,19 +51,14 @@ /// Scarring on this organ var/scarring = NONE -/obj/item/organ/eyes/mob_insert(mob/living/carbon/receiver, special, movement_flags) - // If we don't do this before everything else, heterochromia will be reset leading to eye_color_right no longer being accurate - if(ishuman(receiver)) - var/mob/living/carbon/human/human_recipient = receiver - old_eye_color_left = human_recipient.eye_color_left - old_eye_color_right = human_recipient.eye_color_right - +/obj/item/organ/eyes/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() - receiver.cure_blind(NO_EYES) apply_damaged_eye_effects() refresh(receiver, call_update = TRUE) RegisterSignal(receiver, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) + if (scarring) + apply_scarring_effects() /// Refreshes the visuals of the eyes /// If call_update is TRUE, we also will call update_body @@ -79,14 +70,11 @@ return var/mob/living/carbon/human/affected_human = eye_owner - if(initial(eye_color_left)) - affected_human.eye_color_left = eye_color_left - else - eye_color_left = affected_human.eye_color_left - if(initial(eye_color_right)) - affected_human.eye_color_right = eye_color_right - else - eye_color_right = affected_human.eye_color_right + if(eye_color_left) + affected_human.add_eye_color_left(eye_color_left, EYE_COLOR_ORGAN_PRIORITY, update_body = FALSE) + if(eye_color_right) + affected_human.add_eye_color_right(eye_color_right, EYE_COLOR_ORGAN_PRIORITY, update_body = FALSE) + if(HAS_TRAIT(affected_human, TRAIT_NIGHT_VISION) && !lighting_cutoff) lighting_cutoff = LIGHTING_CUTOFF_REAL_LOW if(CONFIG_GET(flag/native_fov) && native_fov) @@ -95,15 +83,12 @@ if(call_update) affected_human.update_body() -/obj/item/organ/eyes/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/eyes/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(ishuman(organ_owner)) var/mob/living/carbon/human/human_owner = organ_owner - if(initial(eye_color_left)) - human_owner.eye_color_left = old_eye_color_left - if(initial(eye_color_right)) - human_owner.eye_color_right = old_eye_color_right + human_owner.remove_eye_color(EYE_COLOR_ORGAN_PRIORITY, update_body = FALSE) if(native_fov) organ_owner.remove_fov_trait(type) if(!special) @@ -208,8 +193,8 @@ if(my_head.head_flags & HEAD_EYECOLOR) if(IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || (parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT))) // show the eyes as open - eye_right.color = eye_color_right - eye_left.color = eye_color_left + eye_right.color = parent.get_right_eye_color() + eye_left.color = parent.get_left_eye_color() else // show the eyes as closed, and as such color them like eyelids wound be colored var/list/base_color = rgb2num(my_head.draw_color, COLORSPACE_HSL) @@ -278,11 +263,6 @@ owner.cure_blind(EYE_SCARRING_TRAIT) owner.update_body() -/obj/item/organ/eyes/on_mob_insert(mob/living/carbon/eye_owner) - . = ..() - if (scarring) - apply_scarring_effects() - /obj/item/organ/eyes/on_mob_remove(mob/living/carbon/eye_owner) . = ..() if (scarring) @@ -568,14 +548,11 @@ deactivate(close_ui = TRUE) /// Set the initial color of the eyes on insert to be the mob's previous eye color. -/obj/item/organ/eyes/robotic/glow/mob_insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags) . = ..() - left_eye_color_string = old_eye_color_left - right_eye_color_string = old_eye_color_right + left_eye_color_string = eye_color_left + right_eye_color_string = eye_color_right update_mob_eye_color(eye_recipient) - -/obj/item/organ/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient) - . = ..() deactivate(close_ui = TRUE) eye.forceMove(eye_recipient) @@ -878,7 +855,7 @@ apply_organ_damage(-10) //heal quickly . = ..() -/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE) +/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE, movement_flags) REMOVE_TRAIT(unadapted, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT) return ..() diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index cd30506e56b..cc5becdd96f 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -163,7 +163,7 @@ accursed.AddComponent(/datum/component/manual_heart, pump_delay = pump_delay, blood_loss = blood_loss, heal_brute = heal_brute, heal_burn = heal_burn, heal_oxy = heal_oxy) -/obj/item/organ/heart/cursed/on_mob_remove(mob/living/carbon/accursed, special = FALSE) +/obj/item/organ/heart/cursed/on_mob_remove(mob/living/carbon/accursed, special = FALSE, movement_flags) . = ..() qdel(accursed.GetComponent(/datum/component/manual_heart)) @@ -247,7 +247,7 @@ addtimer(VARSET_CALLBACK(src, stabilization_available, TRUE), 5 MINUTES, TIMER_DELETE_ME) // Largely a sanity check -/obj/item/organ/heart/cybernetic/on_mob_remove(mob/living/carbon/heart_owner, special = FALSE) +/obj/item/organ/heart/cybernetic/on_mob_remove(mob/living/carbon/heart_owner, special = FALSE, movement_flags) . = ..() if(HAS_TRAIT_FROM(heart_owner, TRAIT_NOSOFTCRIT, ORGAN_TRAIT)) REMOVE_TRAIT(heart_owner, TRAIT_NOSOFTCRIT, ORGAN_TRAIT) diff --git a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm index df60b8243ef..19f0aef7a52 100644 --- a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm +++ b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm @@ -28,6 +28,10 @@ ///If the core is removable once socketed. var/core_removable = TRUE +/obj/item/organ/heart/cybernetic/anomalock/Destroy() + QDEL_NULL(core) + return ..() + /obj/item/organ/heart/cybernetic/anomalock/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(!core) @@ -39,7 +43,7 @@ RegisterSignal(organ_owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), PROC_REF(activate_survival)) RegisterSignal(organ_owner, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act)) -/obj/item/organ/heart/cybernetic/anomalock/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/heart/cybernetic/anomalock/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(!core) return diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm index 5c6602834cb..0e34fc581c3 100644 --- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm +++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm @@ -21,14 +21,14 @@ add_atom_colour(ethereal_color, FIXED_COLOUR_PRIORITY) update_appearance() -/obj/item/organ/heart/ethereal/mob_insert(mob/living/carbon/heart_owner, special = FALSE, movement_flags) +/obj/item/organ/heart/ethereal/on_mob_insert(mob/living/carbon/heart_owner, special = FALSE, movement_flags) . = ..() RegisterSignal(heart_owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change)) RegisterSignal(heart_owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(on_owner_fully_heal)) RegisterSignal(heart_owner, COMSIG_QDELETING, PROC_REF(owner_deleted)) -/obj/item/organ/heart/ethereal/mob_remove(mob/living/carbon/heart_owner, special, movement_flags) +/obj/item/organ/heart/ethereal/on_mob_remove(mob/living/carbon/heart_owner, special, movement_flags) UnregisterSignal(heart_owner, list(COMSIG_MOB_STATCHANGE, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_QDELETING)) REMOVE_TRAIT(heart_owner, TRAIT_CORPSELOCKED, SPECIES_TRAIT) stop_crystalization_process(heart_owner) diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm index 866813d02aa..e3d6d517044 100755 --- a/code/modules/surgery/organs/internal/liver/_liver.dm +++ b/code/modules/surgery/organs/internal/liver/_liver.dm @@ -60,14 +60,15 @@ qdel(GetComponent(/datum/component/squeak)) /// Registers COMSIG_SPECIES_HANDLE_CHEMICAL from owner -/obj/item/organ/liver/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/liver/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() RegisterSignal(organ_owner, COMSIG_SPECIES_HANDLE_CHEMICAL, PROC_REF(handle_chemical)) + RegisterSignal(organ_owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_owner_examine)) /// Unregisters COMSIG_SPECIES_HANDLE_CHEMICAL from owner -/obj/item/organ/liver/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/liver/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() - UnregisterSignal(organ_owner, COMSIG_SPECIES_HANDLE_CHEMICAL) + UnregisterSignal(organ_owner, list(COMSIG_SPECIES_HANDLE_CHEMICAL, COMSIG_ATOM_EXAMINE)) /** * This proc can be overriden by liver subtypes so they can handle certain chemicals in special ways. @@ -188,7 +189,7 @@ if(SPT_PROB(3, seconds_per_tick)) owner.emote("drool") -/obj/item/organ/liver/on_owner_examine(datum/source, mob/user, list/examine_list) +/obj/item/organ/liver/proc/on_owner_examine(datum/source, mob/user, list/examine_list) if(!ishuman(owner) || !(organ_flags & ORGAN_FAILING)) return diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index 53535cfe131..2a604c46cbc 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -154,7 +154,7 @@ add_gas_reaction(/datum/gas/zauker, while_present = PROC_REF(too_much_zauker)) ///Simply exists so that you don't keep any alerts from your previous lack of lungs. -/obj/item/organ/lungs/mob_insert(mob/living/carbon/receiver, special = FALSE, movement_flags) +/obj/item/organ/lungs/on_mob_insert(mob/living/carbon/receiver, special = FALSE, movement_flags) . = ..() receiver.clear_alert(ALERT_NOT_ENOUGH_OXYGEN) @@ -163,7 +163,7 @@ receiver.clear_alert(ALERT_NOT_ENOUGH_PLASMA) receiver.clear_alert(ALERT_NOT_ENOUGH_N2O) -/obj/item/organ/lungs/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/lungs/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() // This is very "manual" I realize, but it's useful to ensure cleanup for gases we're removing happens // Avoids stuck alerts and such diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index 768f3483c03..40d3265684d 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -246,13 +246,13 @@ disgusted.throw_alert(ALERT_DISGUST, /atom/movable/screen/alert/disgusted) disgusted.add_mood_event("disgust", /datum/mood_event/disgusted) -/obj/item/organ/stomach/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/stomach/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() receiver.hud_used?.hunger?.update_appearance() -/obj/item/organ/stomach/mob_remove(mob/living/carbon/stomach_owner, special, movement_flags) +/obj/item/organ/stomach/on_mob_remove(mob/living/carbon/stomach_owner, special, movement_flags) if(ishuman(stomach_owner)) - var/mob/living/carbon/human/human_owner = owner + var/mob/living/carbon/human/human_owner = stomach_owner human_owner.clear_alert(ALERT_DISGUST) human_owner.clear_mood_event("disgust") stomach_owner.hud_used?.hunger?.update_appearance() diff --git a/code/modules/surgery/organs/internal/stomach/stomach_golem.dm b/code/modules/surgery/organs/internal/stomach/stomach_golem.dm index c4fa888f6cb..dc6f28787aa 100644 --- a/code/modules/surgery/organs/internal/stomach/stomach_golem.dm +++ b/code/modules/surgery/organs/internal/stomach/stomach_golem.dm @@ -11,11 +11,11 @@ /// How slow are you if you have absolutely nothing in the tank? var/max_hunger_slowdown = 4 -/obj/item/organ/stomach/golem/on_mob_insert(mob/living/carbon/organ_owner, special) +/obj/item/organ/stomach/golem/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) . = ..() RegisterSignal(owner, COMSIG_CARBON_ATTEMPT_EAT, PROC_REF(try_eating)) -/obj/item/organ/stomach/golem/on_mob_remove(mob/living/carbon/organ_owner, special) +/obj/item/organ/stomach/golem/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() UnregisterSignal(organ_owner, COMSIG_CARBON_ATTEMPT_EAT) organ_owner.remove_movespeed_modifier(/datum/movespeed_modifier/golem_hunger) diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index 8af7ae075dc..f468df4f531 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -124,7 +124,7 @@ food_taste_reaction = FOOD_LIKED return food_taste_reaction -/obj/item/organ/tongue/mob_insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/tongue/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() if(modifies_speech) @@ -138,7 +138,7 @@ REMOVE_TRAIT(receiver, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) apply_tongue_effects() -/obj/item/organ/tongue/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/tongue/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() temp_say_mod = "" diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm index 5d9e9b36f46..fa76c8180cd 100644 --- a/code/modules/surgery/organs/organ_movement.dm +++ b/code/modules/surgery/organs/organ_movement.dm @@ -15,12 +15,15 @@ /obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags) SHOULD_CALL_PARENT(TRUE) - mob_insert(receiver, special, movement_flags) + if(!mob_insert(receiver, special, movement_flags)) + return FALSE bodypart_insert(limb_owner = receiver, movement_flags = movement_flags) if(!special && !(receiver.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS)) receiver.update_body_parts() + return TRUE + /* * Remove the organ from the select mob. * @@ -44,15 +47,17 @@ * movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags */ /obj/item/organ/proc/mob_insert(mob/living/carbon/receiver, special, movement_flags) - SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_OVERRIDE(TRUE) if(!iscarbon(receiver)) - stack_trace("Tried to insert organ into non-carbon: [receiver.type]") - return + //We try to insert the organ in a corgi when running the test, expecting it to return FALSE. + if(!PERFORM_ALL_TESTS(organ_sanity)) + stack_trace("Tried to insert organ into non-carbon: [receiver.type]") + return FALSE if(owner == receiver) stack_trace("Organ receiver is already organ owner") - return + return FALSE var/obj/item/organ/replaced = receiver.get_organ_slot(slot) if(replaced) @@ -78,7 +83,7 @@ receiver.organs_slot[slot] = src owner = receiver - on_mob_insert(receiver, special) + on_mob_insert(receiver, special, movement_flags) return TRUE @@ -99,7 +104,6 @@ if(!special) organ_owner.hud_used?.update_locked_slots() - RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_owner_examine)) SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, organ_owner) SEND_SIGNAL(organ_owner, COMSIG_CARBON_GAIN_ORGAN, src, special) @@ -141,7 +145,7 @@ * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment */ /obj/item/organ/proc/mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags) - SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_OVERRIDE(TRUE) if(organ_owner) if(organ_owner.organs_slot[slot] == src) @@ -150,7 +154,7 @@ owner = null - on_mob_remove(organ_owner, special) + on_mob_remove(organ_owner, special, movement_flags) return TRUE @@ -172,7 +176,6 @@ for(var/datum/status_effect/effect as anything in organ_effects) organ_owner.remove_status_effect(effect, type) - UnregisterSignal(organ_owner, COMSIG_ATOM_EXAMINE) SEND_SIGNAL(src, COMSIG_ORGAN_REMOVED, organ_owner) SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special) ADD_TRAIT(src, TRAIT_USED_ORGAN, ORGAN_TRAIT) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index d23267fa326..8b869d3e20b 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -4,8 +4,7 @@ ///The description of the surgery, what it does. var/desc - ///From __DEFINES/surgery.dm - ///Selection: SURGERY_IGNORE_CLOTHES | SURGERY_SELF_OPERABLE | SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_REQUIRES_REAL_LIMB | SURGERY_MORBID_CURIOSITY + ///Bitfield for flags that determine different behaviors and requirement for the surgery. See __DEFINES/surgery.dm var/surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB ///The surgery step we're currently on, increases each time we do a step. var/status = 1 @@ -120,7 +119,7 @@ if(LAZYACCESS(modifiers, RIGHT_CLICK)) try_to_fail = TRUE - var/datum/surgery_step/step = get_surgery_step() + var/datum/surgery_step/step = GLOB.surgery_steps[steps[status]] if(isnull(step)) return FALSE var/obj/item/tool = user.get_active_held_item() @@ -128,16 +127,15 @@ tool = tool.get_proxy_attacker_for(target, user) if(step.try_op(user, target, user.zone_selected, tool, src, try_to_fail)) return TRUE - if(tool && tool.item_flags & SURGICAL_TOOL) //Just because you used the wrong tool it doesn't mean you meant to whack the patient with it + if(!tool) + return FALSE + //Just because you used the wrong tool it doesn't mean you meant to whack the patient with it + if((surgery_flags & SURGERY_CHECK_TOOL_BEHAVIOUR) ? tool.tool_behaviour : (tool.item_flags & SURGICAL_TOOL)) to_chat(user, span_warning("This step requires a different tool!")) return TRUE return FALSE -/datum/surgery/proc/get_surgery_step() - var/step_type = steps[status] - return new step_type - /datum/surgery/proc/get_surgery_next_step() if(status < steps.len) var/step_type = steps[status + 1] diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm index 1168b6c619a..f9e05f9e95a 100644 --- a/code/modules/tgui/external.dm +++ b/code/modules/tgui/external.dm @@ -42,7 +42,7 @@ * * required user mob The mob interacting with the UI. * - * return list Statuic Data to be sent to the UI. + * return list Static Data to be sent to the UI. */ /datum/proc/ui_static_data(mob/user) return list() diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm index a7a09a1a502..193dbcfa2a5 100644 --- a/code/modules/transport/tram/tram_controller.dm +++ b/code/modules/transport/tram/tram_controller.dm @@ -144,7 +144,7 @@ tram_registration.active = FALSE SSblackbox.record_feedback("amount", "tram_destroyed", 1) SSpersistence.save_tram_history(specific_transport_id) - ..() + return ..() /** * Register transport modules to the controller diff --git a/code/modules/transport/transport_module.dm b/code/modules/transport/transport_module.dm index 3e4a5be979f..b0497ed3b2e 100644 --- a/code/modules/transport/transport_module.dm +++ b/code/modules/transport/transport_module.dm @@ -136,7 +136,7 @@ /obj/structure/transport/linear/proc/add_item_on_transport(datum/source, atom/movable/new_transport_contents) SIGNAL_HANDLER - var/static/list/blacklisted_types = typecacheof(list(/obj/structure/fluff/tram_rail, /obj/effect/decal/cleanable, /obj/structure/transport/linear, /mob/camera)) + var/static/list/blacklisted_types = typecacheof(list(/obj/structure/fluff/tram_rail, /obj/effect/decal/cleanable, /obj/structure/transport/linear, /mob/eye)) if(is_type_in_typecache(new_transport_contents, blacklisted_types) || new_transport_contents.invisibility == INVISIBILITY_ABSTRACT || HAS_TRAIT(new_transport_contents, TRAIT_UNDERFLOOR)) //prevents the tram from stealing things like landmarks return FALSE if(new_transport_contents in transport_contents) diff --git a/code/modules/tutorials/tutorials/switch_hands.dm b/code/modules/tutorials/tutorials/switch_hands.dm index f1bcbbb3b71..1d8fbd72f3c 100644 --- a/code/modules/tutorials/tutorials/switch_hands.dm +++ b/code/modules/tutorials/tutorials/switch_hands.dm @@ -38,7 +38,7 @@ /datum/tutorial/switch_hands/proc/create_hand_preview(initial_screen_loc) hand_preview = animate_ui_element( - "hand_[hand_to_watch % 2 == 0 ? "r" : "l"]", + "hand_[user.held_index_to_dir(hand_to_watch)]", initial_screen_loc, ui_hand_position(hand_to_watch), TIME_TO_START_MOVING_HAND_ICON, @@ -50,7 +50,7 @@ switch (stage) if (STAGE_SHOULD_SWAP_HAND) - var/hand_name = hand_to_watch % 2 == 0 ? "right" : "left" + var/hand_name = IS_RIGHT_INDEX(hand_to_watch) ? "right" : "left" show_instruction(keybinding_message( /datum/keybinding/mob/swap_hands, "Press '%KEY%' to use your [hand_name] hand", diff --git a/code/modules/unit_tests/README.md b/code/modules/unit_tests/README.md index 9fe97b8b16d..d53a6eb9586 100644 --- a/code/modules/unit_tests/README.md +++ b/code/modules/unit_tests/README.md @@ -39,7 +39,14 @@ Open `code/_compile_options.dm` and uncomment the following line. //#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between ``` -Then, run tgstation.dmb in Dream Daemon. Don't bother trying to connect, you won't need to. You'll be able to see the outputs of all the tests. You'll get to see which tests failed and for what reason. If they all pass, you're set! +There are 3 ways to run unit tests + +- Run tgstation.dmb in Dream Daemon. Don't bother trying to connect, you won't need to. You'll be able to see the outputs of all the tests. You'll get to see which tests failed and for what reason. If they all pass, you're set! + +- Launch game from VS Code. Launch the game as normal & you will see the output of your unit tests in your fancy chat window. This is preferred as you can use the debugger to step through each line of your unit test & can use the games inbuilt debugging tools to further aid in testing + +- Use VS Code Tgstation Test Explorer Extension. This allows you to run tests without launching the game & can also run focused tests(either a single or a selected group) + ## How to think about tests diff --git a/code/modules/unit_tests/component_tests.dm b/code/modules/unit_tests/component_tests.dm index f609e73c4b7..1fae3982510 100644 --- a/code/modules/unit_tests/component_tests.dm +++ b/code/modules/unit_tests/component_tests.dm @@ -1,12 +1,7 @@ /datum/unit_test/component_duping/Run() var/list/bad_dms = list() - var/list/bad_dts = list() for(var/t in typesof(/datum/component)) var/datum/component/comp = t if(!isnum(initial(comp.dupe_mode))) bad_dms += t - var/dupe_type = initial(comp.dupe_type) - if(dupe_type && !ispath(dupe_type)) - bad_dts += t - TEST_ASSERT(!length(bad_dms) && !length(bad_dts), - "Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") + TEST_ASSERT(!length(bad_dms), "Components with invalid dupe modes: ([bad_dms.Join(",")])") diff --git a/code/modules/unit_tests/fish_unit_tests.dm b/code/modules/unit_tests/fish_unit_tests.dm index 94337ae2cc2..43f06ad11e5 100644 --- a/code/modules/unit_tests/fish_unit_tests.dm +++ b/code/modules/unit_tests/fish_unit_tests.dm @@ -128,7 +128,6 @@ description = "It smells fishy." /obj/structure/aquarium/traits - reproduction_and_growth = TRUE var/obj/item/fish/testdummy/crossbreeder/crossbreeder var/obj/item/fish/testdummy/cloner/cloner var/obj/item/fish/testdummy/sterile/sterile @@ -155,7 +154,6 @@ fish_traits = list(/datum/fish_trait/no_mating) /obj/structure/aquarium/evolution - reproduction_and_growth = TRUE var/obj/item/fish/testdummy/evolve/evolve var/obj/item/fish/testdummy/evolve_two/evolve_two @@ -195,7 +193,7 @@ ///During the fish_growth unit test, we spawn a fish outside of the aquarium and check that this actually stops it from growing /datum/fish_evolution/dummy/two/growth_checks(obj/item/fish/source, seconds_per_tick, growth) . = ..() - if(!isaquarium(source.loc)) + if(!source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return COMPONENT_DONT_GROW ///A test that checks that fishing portals can be linked and function as expected @@ -353,7 +351,6 @@ TEST_ASSERT(dummy_boogaloo, "The new fish type cannot be found inside the aquarium") /obj/structure/aquarium/crab - reproduction_and_growth = TRUE //needed for growing up ///Our test subject var/obj/item/fish/chasm_crab/instant_growth/crabbie @@ -489,5 +486,36 @@ for(var/obj/item/fish/fish as anything in box) fish.randomize_size_and_weight() +/datum/unit_test/aquarium_upgrade + +/datum/unit_test/aquarium_upgrade/Run() + var/mob/living/carbon/human/dummy/user = allocate(__IMPLIED_TYPE__) + var/obj/item/aquarium_upgrade/bioelec_gen/upgrade = allocate(__IMPLIED_TYPE__) + var/obj/structure/aquarium/aquarium = allocate(upgrade::upgrade_from_type) + + var/datum/component/aquarium/comp = aquarium.GetComponent(__IMPLIED_TYPE__) + TEST_ASSERT(comp, "[aquarium.type] doesn't have an aquarium component") + comp.set_fluid_type(AQUARIUM_FLUID_AIR) + comp.fluid_temp = MAX_AQUARIUM_TEMP + aquarium.add_traits(list(TRAIT_AQUARIUM_PANEL_OPEN, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), AQUARIUM_TRAIT) + + var/type_to_check = upgrade::upgrade_to_type + var/turf/aquarium_loc = aquarium.loc + user.put_in_hands(upgrade) + upgrade.melee_attack_chain(user, aquarium) + TEST_ASSERT(QDELETED(aquarium), "Old [aquarium.type] was not deleted after upgrade") + + var/obj/structure/aquarium/upgraded_aquarium = locate(type_to_check) in aquarium_loc + TEST_ASSERT(upgraded_aquarium, "New [upgraded_aquarium.type] was not spawned after upgrade") + comp = upgraded_aquarium.GetComponent(/datum/component/aquarium) + TEST_ASSERT(comp, "New [upgraded_aquarium.type] doesn't have an aquarium component") + + TEST_ASSERT_EQUAL(comp.fluid_type, AQUARIUM_FLUID_AIR, "Inherited aquarium fluid type should be [AQUARIUM_FLUID_AIR]") + TEST_ASSERT_EQUAL(comp.fluid_temp, MAX_AQUARIUM_TEMP, "Inherited aquarium fluid temperature should be [MAX_AQUARIUM_TEMP]") + TEST_ASSERT(HAS_TRAIT(upgraded_aquarium, TRAIT_AQUARIUM_PANEL_OPEN), "The new aquarium should have its panel open") + TEST_ASSERT(HAS_TRAIT(upgraded_aquarium, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), "The 'growth and reproduction' setting for this aquarium should be disabled") + + TEST_ASSERT(QDELETED(upgrade), "Aquarium upgrade wasn't deleted afterward") + #undef FISH_REAGENT_AMOUNT #undef TRAIT_FISH_TESTING diff --git a/code/modules/unit_tests/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm index c9bfea88e5e..5530d7418a7 100644 --- a/code/modules/unit_tests/focus_only_tests.dm +++ b/code/modules/unit_tests/focus_only_tests.dm @@ -53,3 +53,6 @@ /// Catches any invalid footstep types set for humans /datum/unit_test/focus_only/humanstep_validity + +/// Checks icon states generated at runtime are valid +/datum/unit_test/focus_only/runtime_icon_states diff --git a/code/modules/unit_tests/heretic_knowledge.dm b/code/modules/unit_tests/heretic_knowledge.dm index f75fff24cee..4154e50285e 100644 --- a/code/modules/unit_tests/heretic_knowledge.dm +++ b/code/modules/unit_tests/heretic_knowledge.dm @@ -1,3 +1,4 @@ + /* * This test checks all heretic knowledge nodes and validates they are setup correctly. * We check that all knowledge is reachable by players (through the research tree) @@ -6,13 +7,13 @@ /datum/unit_test/heretic_knowledge /datum/unit_test/heretic_knowledge/Run() - + if(!GLOB.heretic_research_tree) + GLOB.heretic_research_tree = generate_heretic_research_tree() // First, we get a list of all knowledge types - // EXCLUDING types which have route unset / set to null. - // (Types without a route set are assumed to be abstract or purposefully unreachable) + // EXCLUDING all abstract types var/list/all_possible_knowledge = typesof(/datum/heretic_knowledge) for(var/datum/heretic_knowledge/knowledge_type as anything in all_possible_knowledge) - if(isnull(initial(knowledge_type.route))) + if(initial(knowledge_type.abstract_parent_type) == knowledge_type) all_possible_knowledge -= knowledge_type // Now, let's build a list of all researchable knowledge @@ -22,12 +23,11 @@ var/list/list_to_check = GLOB.heretic_start_knowledge.Copy() var/i = 0 while(i < length(list_to_check)) - var/datum/heretic_knowledge/path_to_create = list_to_check[++i] - if(!ispath(path_to_create)) - TEST_FAIL("Heretic Knowledge: Got a non-heretic knowledge datum (Got: [path_to_create]) in the list knowledges!") - var/datum/heretic_knowledge/instantiated_knowledge = new path_to_create() + var/datum/heretic_knowledge/knowledge = list_to_check[++i] + if(!ispath(knowledge)) + TEST_FAIL("Heretic Knowledge: Got a non-heretic knowledge datum (Got: [knowledge]) in the list knowledges!") // Next knowledge is a list of typepaths. - for(var/datum/heretic_knowledge/next_knowledge as anything in instantiated_knowledge.next_knowledge) + for(var/datum/heretic_knowledge/next_knowledge as anything in GLOB.heretic_research_tree[knowledge][HKT_NEXT]) if(!ispath(next_knowledge)) TEST_FAIL("Heretic Knowledge: [next_knowledge.type] has a [isnull(next_knowledge) ? "null":"invalid path"] in its next_knowledge list!") continue @@ -35,7 +35,6 @@ continue list_to_check += next_knowledge - qdel(instantiated_knowledge) // We now have a list that SHOULD contain all knowledges with a path set (list_to_check). // Let's compare it to our original list (all_possible_knowledge). If they're not identical, @@ -45,48 +44,3 @@ var/list/unreachables = all_possible_knowledge - list_to_check for(var/datum/heretic_knowledge/lost_knowledge as anything in unreachables) TEST_FAIL("Heretic Knowledge: [lost_knowledge] is unreachable by players! Add it to another knowledge's 'next_knowledge' list. If it is purposeful, set its route to 'null'.") - - -/* - * This test checks that all main heretic paths are of the same length. - * - * If any two main paths are not equal length, the test will fail and quit, reporting - * which two paths did not match. Then, whichever is erroneous can be determined manually. - */ -/datum/unit_test/heretic_main_paths - -/datum/unit_test/heretic_main_paths/Run() - // A list of path strings we don't need to check. - var/list/paths_we_dont_check = list(PATH_SIDE, PATH_START) - // An assoc list of [path string] to [number of nodes we found of that path]. - var/list/paths = list() - // The starting knowledge node, we use this to deduce what main paths we have. - var/datum/heretic_knowledge/spell/basic/starter_node = new() - - // Go through and determine what paths exist from our base node. - for(var/datum/heretic_knowledge/possible_path as anything in starter_node.next_knowledge) - paths[initial(possible_path.route)] = 0 - - qdel(starter_node) // Get rid of that starter node, we don't need it anymore. - - // Now go through all the knowledges and record how many of each main path exist. - for(var/datum/heretic_knowledge/knowledge as anything in subtypesof(/datum/heretic_knowledge)) - var/knowledge_route = initial(knowledge.route) - // null (abstract), side paths, and start paths we can skip - if(isnull(knowledge_route) || (knowledge_route in paths_we_dont_check)) - continue - - if(isnull(paths[knowledge_route])) - TEST_FAIL("Heretic Knowledge: An invalid knowledge route ([knowledge_route]) was found on [knowledge].") - continue - - paths[knowledge_route]++ - - // Now all entries in the paths list should have an equal value. - // If any two entries do not match, then one of them is incorrect, and the test fails. - for(var/main_path in paths) - for(var/other_main_path in (paths - main_path)) - TEST_ASSERT(paths[main_path] == paths[other_main_path], \ - "Heretic Knowledge: [main_path] had [paths[main_path]] knowledges, \ - which was not equal to [other_main_path]'s [paths[other_main_path]] knowledges. \ - All main paths should have the same number of knowledges!") diff --git a/code/modules/unit_tests/mob_faction.dm b/code/modules/unit_tests/mob_faction.dm index 554a1adda9d..be667de731d 100644 --- a/code/modules/unit_tests/mob_faction.dm +++ b/code/modules/unit_tests/mob_faction.dm @@ -8,10 +8,10 @@ /mob/dview, /mob/oranges_ear ) - ignored += typesof(/mob/camera/imaginary_friend) + ignored += typesof(/mob/eye/imaginary_friend) ignored += typesof(/mob/living/silicon/robot/model) - ignored += typesof(/mob/camera/ai_eye/remote/base_construction) - ignored += typesof(/mob/camera/ai_eye/remote/shuttle_docker) + ignored += typesof(/mob/eye/camera/remote/base_construction) + ignored += typesof(/mob/eye/camera/remote/shuttle_docker) for (var/mob_type in typesof(/mob) - ignored) var/mob/mob_instance = allocate(mob_type) if(!islist(mob_instance.faction)) diff --git a/code/modules/unit_tests/orderable_items.dm b/code/modules/unit_tests/orderable_items.dm index 8d3db8c3c75..e94b2985a54 100644 --- a/code/modules/unit_tests/orderable_items.dm +++ b/code/modules/unit_tests/orderable_items.dm @@ -6,19 +6,19 @@ /datum/unit_test/orderable_items/Run() var/list/all_paths = list() for (var/datum/orderable_item/orderable_item as anything in subtypesof(/datum/orderable_item)) - if(isnull(initial(orderable_item.item_path))) // don't check if they're not actual orderable items + if(isnull(initial(orderable_item.purchase_path))) // don't check if they're not actual orderable items continue if (!isnull(initial(orderable_item.desc))) //don't check if they have a custom description continue - var/item_path = initial(orderable_item.item_path) + var/purchase_path = initial(orderable_item.purchase_path) - var/obj/item/item_instance = allocate(item_path) + var/obj/item/item_instance = allocate(purchase_path) var/initial_desc = initial(item_instance.desc) - if(item_path in all_paths) + if(purchase_path in all_paths) TEST_FAIL("[orderable_item] is purchasable under two different orderable_item types,") - all_paths += item_path + all_paths += purchase_path if (item_instance.desc != initial_desc) - TEST_FAIL("[orderable_item] has an item ([item_path]) that has a dynamic description. [item_instance.desc] (dynamic description) != [initial_desc] (initial description)") + TEST_FAIL("[orderable_item] has a product ([purchase_path]) that has a dynamic description. [item_instance.desc] (dynamic description) != [initial_desc] (initial description)") diff --git a/code/modules/unit_tests/organs.dm b/code/modules/unit_tests/organs.dm index 8f05a284058..cf01436796d 100644 --- a/code/modules/unit_tests/organs.dm +++ b/code/modules/unit_tests/organs.dm @@ -10,22 +10,9 @@ var/static/list/species_changing_organs = typecacheof(list( /obj/item/organ/brain/shadow/nightmare, )) - // List of organ typepaths which are not test-able, such as certain class prototypes. - var/static/list/test_organ_blacklist = typecacheof(list( - /obj/item/organ, - /obj/item/organ, - /obj/item/organ/wings, - /obj/item/organ/cyberimp, - /obj/item/organ/cyberimp/brain, - /obj/item/organ/cyberimp/mouth, - /obj/item/organ/cyberimp/arm, - /obj/item/organ/cyberimp/chest, - /obj/item/organ/cyberimp/eyes, - /obj/item/organ/alien, - )) /datum/unit_test/organ_sanity/Run() - for(var/obj/item/organ/organ_type as anything in subtypesof(/obj/item/organ) - test_organ_blacklist) + for(var/obj/item/organ/organ_type as anything in subtypesof(/obj/item/organ) - GLOB.prototype_organs) organ_test_insert(organ_type) /datum/unit_test/organ_sanity/proc/organ_test_insert(obj/item/organ/organ_type) diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vampire.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_vampire.png similarity index 100% rename from code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vampire.png rename to code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_vampire.png diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 2f4f7a4b70a..55161807eb4 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -6,7 +6,6 @@ // If you are refactoring a simple_animal, REMOVE it from this list var/list/allowed_types = list( /mob/living/simple_animal/bot, - /mob/living/simple_animal/bot/floorbot, /mob/living/simple_animal/bot/mulebot, /mob/living/simple_animal/bot/mulebot/paranormal, /mob/living/simple_animal/bot/secbot, diff --git a/code/modules/unit_tests/status_effect_ticks.dm b/code/modules/unit_tests/status_effect_ticks.dm index 6f3c43c7ada..d60ba187abc 100644 --- a/code/modules/unit_tests/status_effect_ticks.dm +++ b/code/modules/unit_tests/status_effect_ticks.dm @@ -3,21 +3,21 @@ /datum/unit_test/status_effect_ticks/Run() for(var/datum/status_effect/checking as anything in subtypesof(/datum/status_effect)) - var/checking_tick = initial(checking.tick_interval) - if(checking_tick == -1) + var/tick_speed = initial(checking.tick_interval) + if(tick_speed == STATUS_EFFECT_NO_TICK) continue - if(checking_tick == INFINITY) - TEST_FAIL("Status effect [checking] has tick_interval set to INFINITY, this is not how you prevent ticks - use tick_interval = -1 instead.") + if(tick_speed == INFINITY) + TEST_FAIL("Status effect [checking] has tick_interval set to INFINITY, this is not how you prevent ticks - use tick_interval = STATUS_EFFECT_NO_TICK instead.") continue - if(checking_tick == 0) - TEST_FAIL("Status effect [checking] has tick_interval set to 0, this is not how you prevent ticks - use tick_interval = -1 instead.") + if(tick_speed == 0) + TEST_FAIL("Status effect [checking] has tick_interval set to 0, this is not how you prevent ticks - use tick_interval = STATUS_EFFECT_NO_TICK instead.") continue switch(initial(checking.processing_speed)) if(STATUS_EFFECT_FAST_PROCESS) - if(checking_tick < SSfastprocess.wait) - TEST_FAIL("Status effect [checking] has tick_interval set to [checking_tick], which is faster than SSfastprocess can tick ([SSfastprocess.wait]).") + if(tick_speed < SSfastprocess.wait) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSfastprocess can tick ([SSfastprocess.wait]).") if(STATUS_EFFECT_NORMAL_PROCESS) - if(checking_tick < SSprocessing.wait) - TEST_FAIL("Status effect [checking] has tick_interval set to [checking_tick], which is faster than SSprocessing can tick ([SSprocessing.wait]).") + if(tick_speed < SSprocessing.wait) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSprocessing can tick ([SSprocessing.wait]).") else TEST_FAIL("Invalid processing speed for status effect [checking] : [initial(checking.processing_speed)]") diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index a7af7b168e2..f5dee3e11ee 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -285,13 +285,13 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests()) //We have a baseturf limit of 10, adding more than 10 baseturf helpers will kill CI, so here's a future edge case to fix. returnable_list += typesof(/obj/effect/baseturf_helper) //No tauma to pass in - returnable_list += typesof(/mob/camera/imaginary_friend) + returnable_list += typesof(/mob/eye/imaginary_friend) //No heart to give returnable_list += typesof(/obj/structure/ethereal_crystal) //No linked console - returnable_list += typesof(/mob/camera/ai_eye/remote/base_construction) + returnable_list += typesof(/mob/eye/camera/remote/base_construction) //See above - returnable_list += typesof(/mob/camera/ai_eye/remote/shuttle_docker) + returnable_list += typesof(/mob/eye/camera/remote/shuttle_docker) //Hangs a ref post invoke async, which we don't support. Could put a qdeleted check but it feels hacky returnable_list += typesof(/obj/effect/anomaly/grav/high) //See above diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 0d347709536..77eaeba1114 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -200,6 +200,14 @@ /datum/uplink_item/proc/can_be_bought(datum/uplink_handler/source) return TRUE +///Real cost if this item, accounting for progression_points if uplink_handler has_progression is TRUE +/datum/uplink_item/proc/real_cost(datum/uplink_handler/source) + if (progression_minimum <= 0 || !source?.has_progression) + return cost + var percentage = CLAMP01(source.progression_points / progression_minimum) + var mult = (1 - percentage) * 3 + percentage * 1 + return ceil(cost * mult) + /datum/uplink_category/discounts name = "Discounted Gear" weight = -1 @@ -217,9 +225,13 @@ category = /datum/uplink_category/discounts purchasable_from = parent_type::purchasable_from & ~UPLINK_SPY // Probably not necessary but just in case +/datum/uplink_category/objective_special + name = "Objective-Specific Equipment" + weight = -3 + // Special equipment (Dynamically fills in uplink component) /datum/uplink_item/special_equipment - category = "Objective-Specific Equipment" + category = /datum/uplink_category/objective_special name = "Objective-Specific Equipment" desc = "Equipment necessary for accomplishing specific objectives. If you are seeing this, something has gone wrong." limited_stock = 1 diff --git a/code/modules/uplink/uplink_items/bundle.dm b/code/modules/uplink/uplink_items/bundle.dm index b6cdc2fd3d6..eac30c41a16 100644 --- a/code/modules/uplink/uplink_items/bundle.dm +++ b/code/modules/uplink/uplink_items/bundle.dm @@ -89,28 +89,26 @@ continue if(!uplink_item.surplus) continue - if(handler.not_enough_reputation(uplink_item)) - continue possible_items += uplink_item return possible_items /// picks items from the list given to proc and generates a valid uplink item that is less or equal to the amount of TC it can spend -/datum/uplink_item/bundles_tc/surplus/proc/pick_possible_item(list/possible_items, tc_budget) +/datum/uplink_item/bundles_tc/surplus/proc/pick_possible_item(list/possible_items, tc_budget, datum/uplink_handler/handler) var/datum/uplink_item/uplink_item = pick(possible_items) if(prob(100 - uplink_item.surplus)) return null - if(tc_budget < uplink_item.cost) + if(tc_budget < uplink_item.real_cost(handler)) return null return uplink_item /// fills the crate that will be given to the traitor, edit this to change the crate and how the item is filled -/datum/uplink_item/bundles_tc/surplus/proc/fill_crate(obj/structure/closet/crate/surplus_crate, list/possible_items) +/datum/uplink_item/bundles_tc/surplus/proc/fill_crate(obj/structure/closet/crate/surplus_crate, list/possible_items, datum/uplink_handler/handler) var/tc_budget = crate_tc_value while(tc_budget) - var/datum/uplink_item/uplink_item = pick_possible_item(possible_items, tc_budget) + var/datum/uplink_item/uplink_item = pick_possible_item(possible_items, tc_budget, handler) if(!uplink_item) continue - tc_budget -= uplink_item.cost + tc_budget -= uplink_item.real_cost(handler) new uplink_item.item(surplus_crate) /// overwrites item spawning proc for surplus items to spawn an appropriate crate via a podspawn @@ -142,7 +140,7 @@ crate_type = /obj/structure/closet/crate/secure/syndicrate /// edited version of fill crate for super surplus to ensure it can only be unlocked with the syndicrate key -/datum/uplink_item/bundles_tc/surplus/united/fill_crate(obj/structure/closet/crate/secure/syndicrate/surplus_crate, list/possible_items) +/datum/uplink_item/bundles_tc/surplus/united/fill_crate(obj/structure/closet/crate/secure/syndicrate/surplus_crate, list/possible_items, datum/uplink_handler/handler) if(!istype(surplus_crate)) return var/tc_budget = crate_tc_value @@ -150,7 +148,7 @@ var/datum/uplink_item/uplink_item = pick_possible_item(possible_items, tc_budget) if(!uplink_item) continue - tc_budget -= uplink_item.cost + tc_budget -= uplink_item.real_cost(handler) surplus_crate.unlock_contents += uplink_item.item /datum/uplink_item/bundles_tc/surplus_key diff --git a/code/modules/uplink/uplink_items/dangerous.dm b/code/modules/uplink/uplink_items/dangerous.dm index a1b3b82cbef..337fba00de6 100644 --- a/code/modules/uplink/uplink_items/dangerous.dm +++ b/code/modules/uplink/uplink_items/dangerous.dm @@ -39,8 +39,8 @@ pocketed when inactive. Activating it produces a loud, distinctive noise." progression_minimum = 20 MINUTES item = /obj/item/melee/energy/sword/saber - cost = 8 - purchasable_from = ~(UPLINK_CLOWN_OPS) + cost = 6 + purchasable_from = ~UPLINK_CLOWN_OPS /datum/uplink_item/dangerous/powerfist name = "Power Fist" diff --git a/code/modules/uplink/uplink_items/device_tools.dm b/code/modules/uplink/uplink_items/device_tools.dm index b869970794b..f7af1c8d3ed 100644 --- a/code/modules/uplink/uplink_items/device_tools.dm +++ b/code/modules/uplink/uplink_items/device_tools.dm @@ -243,9 +243,9 @@ active gravitational singularities or tesla balls towards it. This will not work when the engine is still \ in containment. Because of its size, it cannot be carried. Ordering this \ sends you a small beacon that will teleport the larger beacon to your location upon activation." - progression_minimum = 30 MINUTES + progression_minimum = 20 MINUTES item = /obj/item/sbeacondrop - cost = 10 + cost = 4 surplus = 0 // not while there isnt one on any station purchasable_from = ~UPLINK_ALL_SYNDIE_OPS diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm index 9b86a91b026..ca017a552fd 100644 --- a/code/modules/uplink/uplink_items/nukeops.dm +++ b/code/modules/uplink/uplink_items/nukeops.dm @@ -52,7 +52,8 @@ Note: This bundle is not at a discount. You can purchase all of these items separately. You do not NEED these items, but most operatives fail WITHOUT at \ least SOME of these items. More experienced operatives can do without." item = /obj/item/storage/box/syndie_kit/core_gear - cost = 22 //freedom 5, doormag 3, c-4 1, stimpack 5, shield modsuit module 8 + //The cost for the core kit is always equivalent to the combined costs of the included items + cost = (/datum/uplink_item/device_tools/doorjack::cost + /datum/uplink_item/implants/freedom::cost + /datum/uplink_item/explosives/c4::cost + /datum/uplink_item/device_tools/stimpack::cost + /datum/uplink_item/suits/energy_shield::cost) limited_stock = 1 cant_discount = TRUE purchasable_from = UPLINK_SERIOUS_OPS @@ -178,7 +179,7 @@ /datum/uplink_item/weapon_kits/medium_cost/sword_and_board name = "Energy Shield and Sword Case (Very Hard)" - desc = "A case containing an energy sword and energy shield. Paired together, it provides considerable defensive power without lethal potency. \ + desc = "A case containing an energy sword and energy shield. Paired together, it provides considerable defensive power without compromising lethal potency. \ Perfect for the enterprising nuclear knight. Comes with a medieval helmet for your MODsuit!" item = /obj/item/storage/toolbox/guncase/sword_and_board @@ -195,13 +196,13 @@ /datum/uplink_item/weapon_kits/medium_cost/revolvercase name = "Syndicate Revolver Case (Moderate)" desc = "Waffle Corp's modernized Syndicate revolver. Fires 7 brutal rounds of .357 Magnum. \ - A classic operative weapon, brought to the modern era. Comes with 3 additional speedloaders of .357." + A classic operative weapon, improved for the modern era. Comes with 3 additional speedloaders of .357." item = /obj/item/storage/toolbox/guncase/revolver /datum/uplink_item/ammo_nuclear/basic/revolver name = ".357 Speed Loader (Revolver)" desc = "A speed loader that contains seven additional .357 Magnum rounds; usable with the Syndicate revolver. \ - For when you really need a lot of things dead. Operatives get a discount from most of our agents!" + For when you really need a lot of things dead. Unlike field agents, operatives get a premium price for their speedloaders!" item = /obj/item/ammo_box/a357 purchasable_from = parent_type::purchasable_from | UPLINK_SPY @@ -228,7 +229,7 @@ /datum/uplink_item/weapon_kits/medium_cost/rawketlawnchair name = "Dardo-RE Rocket Propelled Grenade Launcher (Hard)" desc = "A reusable rocket propelled grenade launcher preloaded with a low-yield 84mm HE round. \ - Guaranteed to send your target out with a bang or your money back! Comes with a bouquet of additional rockets!" + Guaranteed to take your target out with a bang, or your money back! Comes with a bouquet of additional rockets!" item = /obj/item/storage/toolbox/guncase/rocketlauncher /datum/uplink_item/ammo_nuclear/basic/rocket @@ -291,8 +292,8 @@ /datum/uplink_item/weapon_kits/high_cost/carbine name = "M-90gl Carbine Case (Hard)" - desc = "A fully-loaded, specialized three-round burst carbine that fires .223 ammunition from a 30 round magazine \ - with a 40mm underbarrel grenade launcher. Use secondary-fire to fire the grenade launcher. Comes with two spare magazines \ + desc = "A fully-loaded, specialized three-round burst carbine that fires .223 ammunition from a 30 round magazine.\ + Comes with a 40mm underbarrel grenade launcher. Use secondary-fire to fire the grenade launcher. Also comes with two spare magazines \ and a box of 40mm rubber slugs." item = /obj/item/storage/toolbox/guncase/m90gl @@ -320,7 +321,7 @@ /datum/uplink_item/weapon_kits/high_cost/sniper name = "Anti-Materiel Sniper Rifle Briefcase (Hard)" - desc = "An outdated, but still extremely powerful anti-material sniper rifle. Fires .50 BMG cartridges from a 6 round magazine. \ + desc = "An outdated, but still extremely powerful anti-materiel sniper rifle. Fires .50 BMG cartridges from a 6 round magazine. \ Can be fitted with a suppressor. If anyone asks how that even works, tell them it's Nanotrasen's fault. Comes with \ 3 spare magazines; 2 regular magazines and 1 disruptor magazine. Also comes with a suit and tie." item = /obj/item/storage/briefcase/sniper @@ -374,9 +375,11 @@ /datum/uplink_item/weapon_kits/surplus_smg name = "Surplus Smart-SMG (Flukie)" - desc = "An outdated smart-SMG with limited stopping power, however it's bullets will gradually track towards whatever \ - the gun was shot at. This does require you to actually aim at the person you are shooting at before firing, but \ - surely a highly trained operative such as yourself can manage that." + desc = "A failed experimental 'smart gun'. The use of .160 rocket propelled projectiles resulted in reduced stopping power \ + but increased overally accuracy so long as the shooter vaguely aimed towards their target. The relative increase in \ + operator effort from absurd recoil contradicted advertized advantages, resulting in poor market performance. However, \ + there sure are a lots still lying around in poorly secured warehouses. So we took them. And now you can have them. \ + If you REALLY want it. All I'm saying is: good luck." item = /obj/item/gun/ballistic/automatic/smartgun cost = 2 purchasable_from = UPLINK_SERIOUS_OPS diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm index 0e1ab1302db..da1f7122dd1 100644 --- a/code/modules/vehicles/mecha/combat/durand.dm +++ b/code/modules/vehicles/mecha/combat/durand.dm @@ -35,8 +35,6 @@ . = ..() shield = new /obj/durand_shield(loc, src, plane, layer, dir) RegisterSignal(src, COMSIG_MECHA_ACTION_TRIGGER, PROC_REF(relay)) - RegisterSignal(src, COMSIG_PROJECTILE_PREHIT, PROC_REF(prehit)) - /obj/vehicle/sealed/mecha/durand/Destroy() if(shield) @@ -84,10 +82,12 @@ shield.setDir(dir) //Redirects projectiles to the shield if defense_check decides they should be blocked and returns true. -/obj/vehicle/sealed/mecha/durand/proc/prehit(obj/projectile/source, list/signal_args) - SIGNAL_HANDLER +/obj/vehicle/sealed/mecha/durand/bullet_act(obj/projectile/source, def_zone, mode) if(defense_check(source.loc) && shield) - signal_args[2] = shield + return shield.bullet_act(source, def_zone, mode) + return ..() + + /**Checks if defense mode is enabled, and if the attacker is standing in an area covered by the shield. Expects a turf. Returns true if the attack should be blocked, false if not.*/ diff --git a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm index e1c37a3d13a..329ade4f377 100644 --- a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm @@ -15,82 +15,33 @@ if(!chassis) return PROCESS_KILL -/obj/item/mecha_parts/mecha_equipment/medical/sleeper +/obj/item/mecha_parts/mecha_equipment/proc/get_reagent_data(list/datum/reagent/reagent_list) + var/list/contained_reagents = list() + if(length(reagent_list)) + for(var/datum/reagent/reagent as anything in reagent_list) + contained_reagents += list(list("name" = reagent.name, "volume" = round(reagent.volume, 0.01))) // list in a list because Byond merges the first list... + return contained_reagents + +//---- Mecha sleeper, medical subtype has the chemical functionality +/obj/item/mecha_parts/mecha_equipment/sleeper name = "mounted sleeper" - desc = "Equipment for medical exosuits. A mounted sleeper that stabilizes patients and can inject reagents from a equipped exosuit syringe gun." - icon_state = "mecha_sleeper" + desc = "A mounted sleeper that stabilizes patients." + icon_state = "mecha_sleeper_miner" energy_drain = 20 range = MECHA_MELEE equip_cooldown = 20 /// ref to the patient loaded in the sleeper var/mob/living/carbon/patient - /// amount of chems to inject into patient from other hands syringe gun - var/inject_amount = 10 -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/Destroy() +/obj/item/mecha_parts/mecha_equipment/sleeper/Destroy() for(var/atom/movable/content as anything in src) content.forceMove(get_turf(src)) return ..() -/obj/item/mecha_parts/mecha_equipment/medical/proc/get_reagent_data(list/datum/reagent/reagent_list) - var/list/contained_reagents = list() - if(length(reagent_list)) - for(var/datum/reagent/reagent as anything in reagent_list) - contained_reagents += list(list("name" = reagent.name, "volume" = round(reagent.volume, 0.01))) // list in a list because Byond merges the first list... - return contained_reagents - -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/get_snowflake_data() - var/list/data = list("snowflake_id" = MECHA_SNOWFLAKE_ID_SLEEPER) - if(isnull(patient)) - return data - var/patient_state - switch(patient.stat) - if(0) - patient_state = "Conscious" - if(1) - patient_state = "Unconscious" - if(2) - patient_state = "*dead*" - else - patient_state = "Unknown" - var/core_temp = "" - if(ishuman(patient)) - var/mob/living/carbon/human/humi = patient - core_temp = humi.bodytemperature-T0C - data["patient"] = list( - "patient_name" = patient.name, - "patient_health" = patient.health/patient.maxHealth, - "patient_state" = patient_state, - "core_temp" = core_temp, - "brute_loss" = patient.getBruteLoss(), - "burn_loss" = patient.getFireLoss(), - "toxin_loss" = patient.getToxLoss(), - "oxygen_loss" = patient.getOxyLoss(), - ) - data["has_brain_damage"] = patient.get_organ_loss(ORGAN_SLOT_BRAIN) != 0 - data["has_traumas"] = length(patient.get_traumas()) != 0 - data["contained_reagents"] = get_reagent_data(patient.reagents.reagent_list) - - var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/shooter = locate(/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun) in chassis - if(shooter) - data["injectible_reagents"] = get_reagent_data(shooter.reagents.reagent_list) - return data - -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/handle_ui_act(action, list/params) - switch(action) - if("eject") - go_out() - return TRUE - var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/shooter = locate() in chassis - if(shooter) - for(var/datum/reagent/medication in shooter.reagents.reagent_list) - if(action == ("inject_reagent_" + medication.name)) - inject_reagent(medication, shooter) - break // or maybe return TRUE? i'm not certain - - return FALSE +/obj/item/mecha_parts/mecha_equipment/sleeper/container_resist_act(mob/living/user) + go_out() -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/action(mob/source, atom/atomtarget, list/modifiers) +/obj/item/mecha_parts/mecha_equipment/sleeper/action(mob/source, atom/atomtarget, list/modifiers) if(!action_checks(atomtarget)) return if(!iscarbon(atomtarget)) @@ -112,7 +63,7 @@ log_message("[target] loaded. Life support functions engaged.", LOG_MECHA) return ..() -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/patient_insertion_check(mob/living/carbon/target, mob/user) +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/patient_insertion_check(mob/living/carbon/target, mob/user) if(!isnull(target.buckled)) to_chat(user, "[icon2html(src, user)][span_warning("[target] will not fit into the sleeper because [target.p_theyre()] buckled to [target.buckled]!")]") return FALSE @@ -124,7 +75,7 @@ return FALSE return TRUE -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/go_out() +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/go_out() if(!patient) return patient.forceMove(get_turf(src)) @@ -133,31 +84,56 @@ STOP_PROCESSING(SSobj, src) patient = null -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/detach() +/obj/item/mecha_parts/mecha_equipment/sleeper/detach() if(patient) to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_warning("Unable to detach [src] - equipment occupied!")]") return STOP_PROCESSING(SSobj, src) return ..() -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/inject_reagent(datum/reagent/R, obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG) - if(!R || !patient || !SG || !(SG in chassis.flat_equipment)) - return - var/to_inject = min(R.volume, inject_amount) - if(to_inject && patient.reagents.get_reagent_amount(R.type) + to_inject <= inject_amount*2) - to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Injecting [patient] with [to_inject] units of [R.name].")]") - log_message("Injecting [patient] with [to_inject] units of [R.name].", LOG_MECHA) - for(var/driver in chassis.return_drivers()) - log_combat(driver, patient, "injected", "[name] ([R] - [to_inject] units)") - SG.reagents.trans_to(patient, to_inject, target_id = R.type) +/obj/item/mecha_parts/mecha_equipment/sleeper/get_snowflake_data() + var/list/data = list("snowflake_id" = MECHA_SNOWFLAKE_ID_SLEEPER) + if(isnull(patient)) + return data + var/patient_state + switch(patient.stat) + if(CONSCIOUS) + patient_state = "Conscious" + if(UNCONSCIOUS) + patient_state = "Unconscious" + if(DEAD) + patient_state = "*Dead*" + if(SOFT_CRIT, HARD_CRIT) + patient_state = "Critical" + else + patient_state = "Unknown" + var/core_temp = "" + if(ishuman(patient)) + var/mob/living/carbon/human/humi = patient + core_temp = humi.bodytemperature-T0C + data["patient"] = list( + "patient_name" = patient.name, + "patient_health" = patient.health/patient.maxHealth, + "patient_state" = patient_state, + "core_temp" = core_temp, + "brute_loss" = patient.getBruteLoss(), + "burn_loss" = patient.getFireLoss(), + "toxin_loss" = patient.getToxLoss(), + "oxygen_loss" = patient.getOxyLoss(), + ) + data["contained_reagents"] = get_reagent_data(patient.reagents.reagent_list) + data["has_brain_damage"] = patient.get_organ_loss(ORGAN_SLOT_BRAIN) != 0 + data["has_traumas"] = length(patient.get_traumas()) != 0 -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/container_resist_act(mob/living/user) - go_out() + return data -/obj/item/mecha_parts/mecha_equipment/medical/sleeper/process(seconds_per_tick) - . = ..() - if(.) - return +/obj/item/mecha_parts/mecha_equipment/sleeper/handle_ui_act(action, list/params) + if(action == "eject") + go_out() + return TRUE + return FALSE + +/obj/item/mecha_parts/mecha_equipment/sleeper/process(seconds_per_tick) if(!chassis.has_charge(energy_drain)) log_message("Deactivated.", LOG_MECHA) to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_warning("[src] deactivated - no power.")]") @@ -171,17 +147,59 @@ log_message("[patient] no longer detected - Life support functions disabled.", LOG_MECHA) STOP_PROCESSING(SSobj, src) patient = null - if(ex_patient.health > 0) - ex_patient.adjustOxyLoss(-0.5 * seconds_per_tick) + ex_patient.adjustOxyLoss(-2 * seconds_per_tick) ex_patient.AdjustStun(-40 * seconds_per_tick) ex_patient.AdjustKnockdown(-40 * seconds_per_tick) ex_patient.AdjustParalyzed(-40 * seconds_per_tick) ex_patient.AdjustImmobilized(-40 * seconds_per_tick) ex_patient.AdjustUnconscious(-40 * seconds_per_tick) - if(ex_patient.reagents.get_reagent_amount(/datum/reagent/medicine/epinephrine) < 5) + if(ex_patient.reagents.get_reagent_amount(/datum/reagent/medicine/epinephrine) < 5 \ + && ex_patient.reagents.get_reagent_amount(/datum/reagent/medicine/c2/penthrite) <= 0 \ + && ex_patient.stat >= SOFT_CRIT) ex_patient.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 5) + if(ex_patient.reagents.get_reagent_amount(/datum/reagent/toxin/formaldehyde) <= 0 && ex_patient.stat == DEAD) + ex_patient.reagents.add_reagent(/datum/reagent/toxin/formaldehyde, 3) chassis.use_energy(energy_drain) +//Medical subtype with the chems +/obj/item/mecha_parts/mecha_equipment/sleeper/medical + name = "mounted sleeper" + desc = "Equipment for medical exosuits. A mounted sleeper that stabilizes patients and can inject reagents from a equipped exosuit syringe gun." + icon_state = "mecha_sleeper" + mech_flags = EXOSUIT_MODULE_MEDICAL + /// amount of chems to inject into patient from other hands syringe gun + var/inject_amount = 10 + +/obj/item/mecha_parts/mecha_equipment/sleeper/medical/get_snowflake_data() + var/list/data = ..() + if(isnull(patient)) + return data + var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/shooter = locate(/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun) in chassis + if(shooter) + data["injectible_reagents"] = get_reagent_data(shooter.reagents.reagent_list) + return data + +/obj/item/mecha_parts/mecha_equipment/sleeper/medical/handle_ui_act(action, list/params) + . = ..() + if(.) + return TRUE + var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/shooter = locate() in chassis + if(shooter) + for(var/datum/reagent/medication as anything in shooter.reagents.reagent_list) + if(action == ("inject_reagent_" + medication.name)) + inject_reagent(medication, shooter) + break // or maybe return TRUE? i'm not certain + +/obj/item/mecha_parts/mecha_equipment/sleeper/medical/proc/inject_reagent(datum/reagent/reagent_to_inject, obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG) + if(!reagent_to_inject || !patient || !SG || !(SG in chassis.flat_equipment)) + return + var/to_inject = min(reagent_to_inject.volume, inject_amount) + if(to_inject && patient.reagents.get_reagent_amount(reagent_to_inject.type) + to_inject <= inject_amount*2) + to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Injecting [patient] with [to_inject] units of [reagent_to_inject.name].")]") + log_message("Injecting [patient] with [to_inject] units of [reagent_to_inject.name].", LOG_MECHA) + for(var/driver in chassis.return_drivers()) + log_combat(driver, patient, "injected", "[name] ([reagent_to_inject] - [to_inject] units)") + SG.reagents.trans_to(patient, to_inject, target_id = reagent_to_inject.type) ///////////////////////////////// Syringe Gun /////////////////////////////////////////////////////////////// diff --git a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm index afb049d62d0..811e4653835 100644 --- a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm @@ -9,6 +9,9 @@ name = "exosuit drill" desc = "Equipment for engineering and combat exosuits. This is the drill that'll pierce the heavens!" icon_state = "mecha_drill" + equipment_slot = MECHA_UTILITY + can_be_toggled = TRUE + active = FALSE equip_cooldown = 15 energy_drain = 0.01 * STANDARD_CELL_CHARGE force = 15 @@ -32,6 +35,17 @@ ADD_TRAIT(src, TRAIT_INSTANTLY_PROCESSES_BOULDERS, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_BOULDER_BREAKER, INNATE_TRAIT) +/obj/item/mecha_parts/mecha_equipment/drill/handle_ui_act(action, list/params) + if(action != "toggle") + return + if(active) + RegisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK, PROC_REF(on_mech_click)) + log_message("Activated.", LOG_MECHA) + else + UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK) + log_message("Deactivated.", LOG_MECHA) + return TRUE + /obj/item/mecha_parts/mecha_equipment/drill/attach(obj/vehicle/sealed/mecha/new_mecha, attach_right) . = ..() RegisterSignal(chassis, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine)) @@ -64,6 +78,14 @@ return FALSE return ..() +///Redirects clicks to use the drill if possible when enabled +/obj/item/mecha_parts/mecha_equipment/drill/proc/on_mech_click(atom/mech, mob/source, atom/target, on_cooldown, adjacent) + SIGNAL_HANDLER + if(on_cooldown || !adjacent) + return + INVOKE_ASYNC(src, PROC_REF(action), source, target, null, FALSE) + return COMPONENT_CANCEL_MELEE_CLICK + /obj/item/mecha_parts/mecha_equipment/drill/action(mob/source, atom/target, list/modifiers, bumped) //If bumped, only bother drilling mineral turfs if(bumped) diff --git a/code/modules/vehicles/mecha/mech_fabricator.dm b/code/modules/vehicles/mecha/mech_fabricator.dm index 54f8cee9ed4..ad28886d99f 100644 --- a/code/modules/vehicles/mecha/mech_fabricator.dm +++ b/code/modules/vehicles/mecha/mech_fabricator.dm @@ -10,6 +10,7 @@ subsystem_type = /datum/controller/subsystem/processing/fastprocess + interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_MOUSEDROP_IGNORE_CHECKS /// Current items in the build queue. var/list/datum/design/queue = list() @@ -49,12 +50,15 @@ /// All designs in the techweb that can be fabricated by this machine, since the last update. var/list/datum/design/cached_designs - //looping sound for printing items + /// Looping sound for printing items var/datum/looping_sound/lathe_print/print_sound /// Local designs that only this mechfab have(using when mechfab emaged so it's illegal designs). var/list/datum/design/illegal_local_designs + /// Direction the produced items will drop (0 means on top of us) + var/drop_direction = SOUTH + /obj/machinery/mecha_part_fabricator/Initialize(mapload) print_sound = new(src, FALSE) rmat = AddComponent(/datum/component/remote_materials, mapload && link_on_init) @@ -136,15 +140,19 @@ . = ..() if(in_range(user, src) || isobserver(user)) . += span_notice("The status display reads: Storing up to [rmat.local_size] material units.
Material consumption at [component_coeff*100]%.
Build time reduced by [100-time_coeff*100]%.") - if(panel_open) - . += span_notice("Alt-click to rotate the output direction.") + . += span_notice("Currently configured to drop printed objects [dir2text(drop_direction)].") -/obj/machinery/mecha_part_fabricator/click_alt(mob/user) - if(!panel_open) - return CLICK_ACTION_BLOCKING - dir = turn(dir, -90) - balloon_alert(user, "rotated to [dir2text(dir)].") - return CLICK_ACTION_SUCCESS +/obj/machinery/mecha_part_fabricator/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params) + if(!can_interact(user) || (!HAS_SILICON_ACCESS(user) && !isAdminGhostAI(user)) && !Adjacent(user)) + return + if(being_built) + balloon_alert(user, "printing started!") + return + var/direction = get_dir(src, over_location) + if(!direction) + return + drop_direction = direction + balloon_alert(user, "dropping [dir2text(drop_direction)]") /obj/machinery/mecha_part_fabricator/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) @@ -267,7 +275,7 @@ /obj/machinery/mecha_part_fabricator/process() // If there's a stored part to dispense due to an obstruction, try to dispense it. if(stored_part) - var/turf/exit = get_step(src,(dir)) + var/turf/exit = get_step(src, drop_direction) if(exit.density) return TRUE @@ -305,7 +313,7 @@ being_built = null - var/turf/exit = get_step(src,(dir)) + var/turf/exit = get_step(src, drop_direction) if(exit.density) say("Error! The part outlet is obstructed.") desc = "It's trying to dispense the fabricated [dispensed_design.name], but the part outlet is obstructed." diff --git a/code/modules/vehicles/mecha/mecha_ai_interaction.dm b/code/modules/vehicles/mecha/mecha_ai_interaction.dm index 4259dff5c34..168b2e0ea02 100644 --- a/code/modules/vehicles/mecha/mecha_ai_interaction.dm +++ b/code/modules/vehicles/mecha/mecha_ai_interaction.dm @@ -99,7 +99,7 @@ mecha_flags |= SILICON_PILOT moved_inside(AI) AI.eyeobj?.forceMove(src) - AI.eyeobj?.RegisterSignal(src, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/camera/ai_eye, update_visibility)) + AI.eyeobj?.RegisterSignal(src, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/eye/camera/ai, update_visibility)) AI.controlled_equipment = src AI.remote_control = src add_occupant(AI) diff --git a/code/modules/vehicles/mecha/mecha_construction_paths.dm b/code/modules/vehicles/mecha/mecha_construction_paths.dm index 0a305a5db66..a8a7c224491 100644 --- a/code/modules/vehicles/mecha/mecha_construction_paths.dm +++ b/code/modules/vehicles/mecha/mecha_construction_paths.dm @@ -416,10 +416,10 @@ circuit_control = /obj/item/circuitboard/mecha/clarke/main circuit_periph = /obj/item/circuitboard/mecha/clarke/peripherals - inner_plating = /obj/item/stack/sheet/plasteel + inner_plating = /obj/item/stack/sheet/iron inner_plating_amount = 5 - outer_plating = /obj/item/stack/sheet/mineral/gold + outer_plating = /obj/item/stack/sheet/plasteel outer_plating_amount = 5 /datum/component/construction/mecha/clarke/get_frame_steps() diff --git a/code/modules/vehicles/mecha/mecha_movement.dm b/code/modules/vehicles/mecha/mecha_movement.dm index a4b21190a1e..130e0e807b4 100644 --- a/code/modules/vehicles/mecha/mecha_movement.dm +++ b/code/modules/vehicles/mecha/mecha_movement.dm @@ -139,6 +139,8 @@ // if we're not strafing or if we are forced to rotate or if we are holding down the key if(dir != direction && (!strafe || forcerotate || keyheld)) setDir(direction) + if(!(mecha_flags & QUIET_TURNS)) + playsound(src, turnsound, 40, TRUE) if(keyheld || !pivot_step) //If we pivot step, we don't return here so we don't just come to a stop return TRUE @@ -146,10 +148,6 @@ //Otherwise just walk normally . = try_step_multiz(direction) - //dir and olddir are the current direction of the sprite and the old direction of the sprite respectively - if (dir != olddir && !(mecha_flags & QUIET_TURNS)) - playsound(src, turnsound, 40, TRUE) - if(phasing) use_energy(phasing_energy_drain) if(strafe) diff --git a/code/modules/vehicles/mecha/working/clarke.dm b/code/modules/vehicles/mecha/working/clarke.dm index 8ccee945ed2..1ecf4a9a831 100644 --- a/code/modules/vehicles/mecha/working/clarke.dm +++ b/code/modules/vehicles/mecha/working/clarke.dm @@ -1,11 +1,11 @@ -///Lavaproof, fireproof, fast mech with low armor and higher energy consumption, cannot strafe and has an internal ore box. +///Lavaproof, fireproof, fast mech with low armor and higher energy consumption and has an internal ore box. /obj/vehicle/sealed/mecha/clarke - desc = "Combining man and machine for a better, stronger engineer. Can even resist lava!" + desc = "Combining man and machine for a better, stronger miner, Cannot strafe Can even resist lava!" name = "\improper Clarke" icon_state = "clarke" base_icon_state = "clarke" max_temperature = 65000 - max_integrity = 200 + max_integrity = 250 movedelay = 1.25 overclock_coeff = 1.25 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF @@ -15,14 +15,14 @@ equip_by_category = list( MECHA_L_ARM = null, MECHA_R_ARM = null, - MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/orebox_manager), + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/orebox_manager, /obj/item/mecha_parts/mecha_equipment/sleeper/clarke), MECHA_POWER = list(), MECHA_ARMOR = list(), ) max_equip_by_category = list( MECHA_L_ARM = 1, MECHA_R_ARM = 1, - MECHA_UTILITY = 5, + MECHA_UTILITY = 6, MECHA_POWER = 1, MECHA_ARMOR = 1, ) @@ -35,7 +35,7 @@ pivot_step = TRUE /datum/armor/mecha_clarke - melee = 20 + melee = 40 bullet = 10 laser = 20 energy = 10 @@ -55,13 +55,14 @@ /obj/vehicle/sealed/mecha/clarke/generate_actions() . = ..() initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_search_ruins) + initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/clarke_scoop_body) //Ore Box Controls ///Special equipment for the Clarke mech, handles moving ore without giving the mech a hydraulic clamp and cargo compartment. /obj/item/mecha_parts/mecha_equipment/orebox_manager name = "ore storage module" - desc = "An automated ore box management device." + desc = "An automated ore box management device, complete with a built-in boulder processor." icon_state = "mecha_bin" equipment_slot = MECHA_UTILITY detachable = FALSE @@ -109,6 +110,26 @@ log_message("Dumped [cached_ore_box].", LOG_MECHA) return TRUE +/obj/item/mecha_parts/mecha_equipment/sleeper/clarke //The Clarke subtype of the sleeper is a built-in utility module + equipment_slot = MECHA_UTILITY + detachable = FALSE + +/datum/action/vehicle/sealed/mecha/clarke_scoop_body + name = "Pick up body" + desc = "Activate to pick up a nearby body" + button_icon = 'icons/obj/devices/mecha_equipment.dmi' + button_icon_state = "mecha_sleeper_miner" + +/datum/action/vehicle/sealed/mecha/clarke_scoop_body/Trigger(trigger_flags) + var/obj/item/mecha_parts/mecha_equipment/sleeper/clarke/sleeper = locate() in chassis + var/mob/living/carbon/human/human_target + for(var/mob/living/carbon/human/body in range(1, chassis)) + if(chassis.is_driver(body) || !ishuman(body) || !chassis.Adjacent(body)) + continue + human_target = body //Non-driver, human, and adjacent + break + sleeper.action(pick(chassis.return_drivers()), human_target) //This will probably break if anyone allows multiple drivers of the Clarke mech + #define SEARCH_COOLDOWN (1 MINUTES) /datum/action/vehicle/sealed/mecha/mech_search_ruins diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm index 392c890ca8f..9b0dd29b5fe 100644 --- a/code/modules/vehicles/scooter.dm +++ b/code/modules/vehicles/scooter.dm @@ -213,7 +213,7 @@ instability = 3 icon_state = "hoverboard_holy" -/obj/vehicle/ridden/scooter/skateboard/hoverboard/make_ridable() +/obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded/make_ridable() AddElement(/datum/element/ridable, /datum/component/riding/vehicle/scooter/skateboard/hover/holy) /obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded/Initialize(mapload) diff --git a/code/modules/vehicles/secway.dm b/code/modules/vehicles/secway.dm index 6726fb02ef1..ab366da4b28 100644 --- a/code/modules/vehicles/secway.dm +++ b/code/modules/vehicles/secway.dm @@ -101,5 +101,5 @@ if(!buckled_mobs || prob(40)) return ..() for(var/mob/rider as anything in buckled_mobs) - rider.bullet_act(P) - return TRUE + return rider.bullet_act(P) + return ..() diff --git a/code/modules/vending/games.dm b/code/modules/vending/games.dm index ce51c196746..a5f2c84920d 100644 --- a/code/modules/vending/games.dm +++ b/code/modules/vending/games.dm @@ -54,9 +54,10 @@ /obj/item/storage/box/fishing_lines = 2, /obj/item/storage/box/fishing_lures = 2, /obj/item/book/manual/fish_catalog = 5, - /obj/item/fish_feed = 4, + /obj/item/reagent_containers/cup/fish_feed = 4, /obj/item/fish_analyzer = 2, /obj/item/fishing_rod/telescopic = 1, + /obj/item/fish_tank = 1, ), ), list( diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm index e98bba8e34f..cfaf732cc46 100644 --- a/code/modules/vending/wardrobes.dm +++ b/code/modules/vending/wardrobes.dm @@ -524,7 +524,7 @@ GLOBAL_VAR_INIT(roaches_deployed, FALSE) /obj/item/clothing/shoes/laceup = 2, /obj/item/radio/headset/headset_srv = 2, /obj/item/storage/box/evidence = 2, - /obj/item/fish_feed = 1, + /obj/item/reagent_containers/cup/fish_feed = 1, ) refill_canister = /obj/item/vending_refill/wardrobe/law_wardrobe payment_department = ACCOUNT_SRV diff --git a/code/modules/wiremod/components/action/laserpointer.dm b/code/modules/wiremod/components/action/laserpointer.dm index 0eb7f822db1..8693a5c1ab1 100644 --- a/code/modules/wiremod/components/action/laserpointer.dm +++ b/code/modules/wiremod/components/action/laserpointer.dm @@ -11,8 +11,8 @@ /// The input port var/datum/port/input/target_input - var/datum/port/input/image_pixel_x = 0 - var/datum/port/input/image_pixel_y = 0 + var/datum/port/input/image_pixel_x + var/datum/port/input/image_pixel_y var/max_range = 7 diff --git a/code/modules/wiremod/components/admin/getvar.dm b/code/modules/wiremod/components/admin/getvar.dm index 2fe8f18b09e..e467963fe34 100644 --- a/code/modules/wiremod/components/admin/getvar.dm +++ b/code/modules/wiremod/components/admin/getvar.dm @@ -45,7 +45,7 @@ output_value.set_datatype(expected_output_type.value) /obj/item/circuit_component/get_variable/input_received(datum/port/input/port) - var/atom/object = entity?.value + var/datum/object = entity?.value if(getvar_options.value == "Global") object = GLOB diff --git a/code/modules/wiremod/core/component_printer.dm b/code/modules/wiremod/core/component_printer.dm index cb51a0e8ab7..4f0c72148a1 100644 --- a/code/modules/wiremod/core/component_printer.dm +++ b/code/modules/wiremod/core/component_printer.dm @@ -22,7 +22,7 @@ /obj/machinery/component_printer/Initialize(mapload) . = ..() - materials = AddComponent(/datum/component/remote_materials, mapload, whitelist_typecache = typecacheof(/obj/item/circuit_component)) + materials = AddComponent(/datum/component/remote_materials, mapload) /obj/machinery/component_printer/post_machine_initialize() . = ..() @@ -66,6 +66,19 @@ return current_unlocked_designs -= added_design.build_path +/obj/machinery/component_printer/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) + //to allow quick recycling of circuits + if(istype(tool, /obj/item/circuit_component)) + var/amount_inserted = materials.insert_item(tool) + + if(amount_inserted) + to_chat(user, span_notice("[tool] worth [amount_inserted / SHEET_MATERIAL_AMOUNT] sheets of material was consumed by [src]")) + else + to_chat(user, span_warning("[tool] was rejected by [src]")) + + return amount_inserted > 0 ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_FAILURE + + return ..() /obj/machinery/component_printer/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/modules/wiremod/core/integrated_circuit.dm b/code/modules/wiremod/core/integrated_circuit.dm index 8afc963b5a3..f3d52653baf 100644 --- a/code/modules/wiremod/core/integrated_circuit.dm +++ b/code/modules/wiremod/core/integrated_circuit.dm @@ -501,11 +501,13 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) return component.disconnect() remove_component(component) + + var/mob/user = ui.user if(component.loc == src) - usr.put_in_hands(component) + user.put_in_hands(component) var/obj/machinery/component_printer/printer = linked_component_printer?.resolve() if (!isnull(printer)) - printer.attackby(component, usr) + printer.base_item_interaction(user, component) . = TRUE if("set_component_coordinates") var/component_id = text2num(params["component_id"]) diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index 29147aefa94..b31f3ce151b 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -93,8 +93,6 @@ var/datum/port/output/user_port - var/datum/weakref/user - var/obj/item/organ/cyberimp/bci/bci /obj/item/circuit_component/bci_core/populate_ports() @@ -111,19 +109,18 @@ /obj/item/circuit_component/bci_core/proc/update_charge_action() CIRCUIT_TRIGGER - var/mob/living/carbon/resolved_owner = user?.resolve() if (show_charge_meter.value) if (charge_action) return charge_action = new(src) - if (resolved_owner) - charge_action.Grant(resolved_owner) + if (bci.owner) + charge_action.Grant(bci.owner) bci.actions += charge_action else if (!charge_action) return - if (resolved_owner) - charge_action.Remove(resolved_owner) + if (bci.owner) + charge_action.Remove(bci.owner) bci.actions -= charge_action QDEL_NULL(charge_action) @@ -139,9 +136,8 @@ bci = shell if (charge_action) - var/mob/living/carbon/resolved_owner = user?.resolve() - if (resolved_owner) - charge_action.Remove(resolved_owner) + if (bci.owner) + charge_action.Remove(bci.owner) bci.actions -= charge_action QDEL_NULL(charge_action) @@ -158,14 +154,13 @@ if (!sent_message) return - var/mob/living/carbon/resolved_owner = user?.resolve() - if (isnull(resolved_owner)) + if (isnull(bci.owner)) return - if (resolved_owner.stat == DEAD) + if (bci.owner.stat == DEAD) return - to_chat(resolved_owner, "You hear a strange, robotic voice in your head... \"[span_robot("[html_encode(sent_message)]")]\"") + to_chat(bci.owner, "You hear a strange, robotic voice in your head... \"[span_robot("[html_encode(sent_message)]")]\"") /obj/item/circuit_component/bci_core/proc/on_organ_implanted(datum/source, mob/living/carbon/owner) SIGNAL_HANDLER @@ -173,7 +168,6 @@ update_charge_action() user_port.set_output(owner) - user = WEAKREF(owner) RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge)) @@ -183,7 +177,6 @@ SIGNAL_HANDLER user_port.set_output(null) - user = null UnregisterSignal(owner, list( COMSIG_ATOM_EXAMINE, diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index f6f21b7e1ee..e524bdee5cb 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -23,26 +23,19 @@ GLOB.zombie_infection_list -= src . = ..() -/obj/item/organ/zombie_infection/on_mob_insert(mob/living/carbon/M, special = FALSE, movement_flags) +/obj/item/organ/zombie_infection/on_mob_insert(mob/living/carbon/new_owner, special = FALSE, movement_flags) . = ..() - + RegisterSignal(new_owner, COMSIG_LIVING_DEATH, PROC_REF(organ_owner_died)) START_PROCESSING(SSobj, src) -/obj/item/organ/zombie_infection/on_mob_remove(mob/living/carbon/M, special = FALSE) +/obj/item/organ/zombie_infection/on_mob_remove(mob/living/carbon/new_owner, special = FALSE, movement_flags) . = ..() STOP_PROCESSING(SSobj, src) - if(iszombie(M) && old_species && !special) - M.set_species(old_species) + if(iszombie(new_owner) && old_species && !special) + new_owner.set_species(old_species) if(timer_id) deltimer(timer_id) - -/obj/item/organ/zombie_infection/on_mob_insert(mob/living/carbon/organ_owner, special) - . = ..() - RegisterSignal(organ_owner, COMSIG_LIVING_DEATH, PROC_REF(organ_owner_died)) - -/obj/item/organ/zombie_infection/on_mob_remove(mob/living/carbon/organ_owner, special) - . = ..() - UnregisterSignal(organ_owner, COMSIG_LIVING_DEATH) + UnregisterSignal(new_owner, COMSIG_LIVING_DEATH) /obj/item/organ/zombie_infection/proc/organ_owner_died(mob/living/carbon/source, gibbed) SIGNAL_HANDLER diff --git a/config/config.txt b/config/config.txt index a4fe3bbde17..47d88186107 100644 --- a/config/config.txt +++ b/config/config.txt @@ -8,6 +8,7 @@ $include resources.txt $include interviews.txt $include lua.txt $include auxtools.txt +$include map_vote.txt # You can use the @ character at the beginning of a config option to lock it from being edited in-game # Example usage: @@ -626,3 +627,9 @@ TIMEZONE_OFFSET 108000 ## Uncomment to force users to link their Discord accounts before connecting # REQUIRE_DISCORD_LINKING + +## Uncomment to allow admins with +DEBUG to start the byond-tracy profiler during the round. +#ALLOW_TRACY_START + +## Uncomment to allow admins with +DEBUG to queue the next round to run the byond-tracy profiler. +#ALLOW_TRACY_QUEUE diff --git a/config/game_options.txt b/config/game_options.txt index fa8956f6a10..4dad29735f0 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -509,6 +509,9 @@ MAXFINE 2000 ## How many played hours of DRONE_REQUIRED_ROLE required to be a Maintenance Done #DRONE_ROLE_PLAYTIME 14 +## Will drones be restricted from interacting with the supermatter and atmopherics area? +#DRONE_AREA_INTERACTION_RESTRICT 1 + ## Whether native FoV is enabled for all people. #NATIVE_FOV diff --git a/html/changelogs/AutoChangeLog-pr-88071.yml b/html/changelogs/AutoChangeLog-pr-88071.yml new file mode 100644 index 00000000000..a6556ae654e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-88071.yml @@ -0,0 +1,4 @@ +author: "SyncIt21" +delete-after: True +changes: + - bugfix: "inducers can be inserted into storage objects again" \ No newline at end of file diff --git a/html/changelogs/archive/2024-11.yml b/html/changelogs/archive/2024-11.yml index 42a40a230d7..0761d95f4b9 100644 --- a/html/changelogs/archive/2024-11.yml +++ b/html/changelogs/archive/2024-11.yml @@ -291,6 +291,10 @@ - bugfix: You can perform fish scanning experiments again. - bugfix: You can actually release fish on water turfs after catching them. - spellcheck: Fixed a typo in the name of material fishing rods. + Jacquerel: + - bugfix: Living Limbs no longer try to grab things that are under the floor. + - spellcheck: Living Limb feedback messages now don't redundantly specify that they + are flesh arms. NoselessCanine: - balance: Gas mask filters are now more effective, and their degradation is more heavily influenced by what gasses they are filtering. @@ -307,6 +311,521 @@ mind altering psychotropic eldritch brain wizards has spread amongst the conspiracy theorists. They could be anyone! Even YOU. - balance: Some additional spells (mindswap, nightmare's terrorize) are also blocked. + thegrb93: + - bugfix: Fixes oil slimes making vegetable oil instead of corn oil unit0016: - code_imp: Mappers can now opt out of automatically linking their up/down station traits. +2024-11-10: + Ghommie: + - bugfix: Fixed fishing rod duping with poly belts and shapeshift spells. + - spellcheck: Fixed a small typo when examining fishing rods. + Majkl-J: + - bugfix: Cigarettes can be injected again and have the right amount of nicotine + OrionTheFox: + - image: redid most basic drinking glass sprites, and moved several drinks to use + the same color system as beakers. Please bug report any incorrect colored drinks + or juices! + Rhials: + - bugfix: Blob Overmind/Minion/Blobbernaut speech is now logged. Beware. + SmArtKar: + - bugfix: Fixed wings and jetpacks sometimes preventing you from opening doors + SyncIt21: + - bugfix: chem master validates selected container in UI so no more href exploits + Xander3359: + - bugfix: fix blade ascension not giving you the ring of blades + grungussuss: + - qol: security helmet straps can be loosened to show hair + - sound: only insulated,nitrile, enhanced retrieval, latex, boxing, improvised gripper + gloves have an equip sound + harryob: + - bugfix: dynamic rulesets can get candidates for their roles + - bugfix: the abductor console now correctly loads images of equipment + timothymtorres: + - admin: Admins can now add/remove TRAIT_EVIL from mobs. + tontyGH: + - server: mob/camera has been renamed to mob/eye, which may break downstreams + - bugfix: Teleporting while buckled to something now works as expected + - bugfix: You can buckle to anything if you share the same tile (cause at that point + it doesn't matter if there's a wall, right?) +2024-11-11: + Dawnseer: + - spellcheck: changed throguh to through in a player facing description + Ghommie: + - bugfix: You can now use fish feed cans on aquariums while the control panel is + open. + - rscadd: Added fishing bluespace capsules to the game, which can be used to spawn + a variety of fishing spots, from freshwater to tiziran sea to hot springs, and + also lava and plasma if emagged. + - rscadd: 'Added two new fish: the zagoskian moonfish and the sacabambaspis. Moonfish + will now periodically lay moonfish eggs.' + - map: The 'crashed pod' lavaland ruin now has a hot spring, and the cursed hotspring + on icemoon now has a plastic chair and a fishing toolbox. + MTandi: + - qol: chem dispenser droplets in UI colored as their reagent + Majkl-J: + - bugfix: Losing malf no longer wipes nonmalf AI abilities + SmArtKar: + - refactor: Eye color effects are now controlled via a new overrides system, which + should prevent conflicts between different sources. + SyncIt21: + - bugfix: mortar pedestal now grinds & juices items that previously could not be + processed + - bugfix: plumbing grinder won't destroy slime extracts after grinding + - refactor: grinding & juicing code has been refactored overall. Please report bugs + on github + - code_imp: improves code for recycling circuits in component printer + - bugfix: Super matter sliver dusts fishing hooks & cannot be picked up by them + carlarctg: + - bugfix: fixed random dm modifiers & some life-like reagent reactions + carlarctg, Ghommie: + - bugfix: Fixes the randomizer fishing portal not working as intended. Fish from + it should now receive random traits. + - bugfix: Fish names are no longer partially shown in the aquarium UI if they contain + a space character. + grungussuss: + - sound: evidence bags have more sounds now + - sound: writing something now produces sound + harryob: + - bugfix: you can no longer send newscaster messages to channels you don't have + permissions for + necromanceranne: + - bugfix: Abstract nullrod types can no longer be burned or melted with acid. + - bugfix: Monk staff now properly does not block projectiles, and uses the correct + force before being wielded. +2024-11-12: + Djiq: + - refactor: Refactors heretic tree coode + EnterTheJake: + - balance: The Uplink cost of the energy sword has been lowered from 8 to 6 TC. + Ghommie: + - bugfix: AI-controlled mobs can now cross open space if they won't fall. + Melbert: + - image: The animation for being dusted now takes into account your sprite, rather + than being a normal nude spaceman + grungussuss: + - qol: you can now mitigate motion sickness from screen shake by enabling "darken + screen shake" in preferences +2024-11-13: + 00-Steven: + - bugfix: Fixed auto-reel fishing line item catching logic. + Absolucy: + - bugfix: You can no longer crush random unsuspecting people with vendors by ventcrawling + while cursed. + - qol: AFK players don't count as "watchers" for cursed stuff. + EmptyLullaby: + - bugfix: Goliath cloaks are no longer so hard on the calves that they force digitigrade + legs to disable. + Ghommie: + - bugfix: Fixed special DNA infusions from squids and pufferfish. + - bugfix: Fixed fish still being hungry when fed if in aquarium with the 'growth + and reproduction' option disabled. + - bugfix: Fixed bombing non-turfs fishing spots not spawning loot correctly. + - balance: Explosive bombing no longer spawns bottled messages/photos. + - bugfix: Fixed fish not being able to reproduce with other fish of the same type + without the crossbreeding trait. + Goat: + - map: The library's scanner on Birdshot is now close enough to connect to the computer + and was also given a book binder. + - qol: Curator console's inventory screen will now update when you change pages + or remove items. + - qol: You can now examine labelers to tell how many more labels it has. + Holoo-1: + - bugfix: fixed roundstart borgs not being synced to ai + Neocloudy: + - bugfix: examine tags now use regex for checking if a tag has "and" in it + - spellcheck: the tooltip for the morbid examine tag doesn't try to use html anymore + OrionTheFox: + - bugfix: (DeltaStation) Fixed unwired APCs in the Electronic Marketing Den, Abandoned + Garden, Security Maintenance, and Private Investigator's Office + Rhials: + - qol: Makes some minor updates to Runtimestation, including event spawn points + and a cargo bounty pad. + SmArtKar: + - bugfix: Xenobio console puts sucked up slimes into stasis so they no longer split + up inside + - bugfix: Fixed basic mob performance impact created by the factorio PR + - bugfix: Fixed hat stabilizer ignoring clothing worn_y_offset + SyncIt21: + - bugfix: flatpacker accepts circuitboards with left click + - bugfix: rcd can deconstruct tables + - bugfix: you can open panels of destructive analyzers with screwdriver right click. + Use right click or combat mode with items for default interactions + - bugfix: flatpacker & machines with local storage can be RPED'd again + harryob: + - bugfix: ghosts observing ghosts can no longer click on their screen alerts + - bugfix: certain tgui inputs no longer require 2 clicks to open + sqnztb: + - map: tramstation tool storage trash no longer routes to the barber shop + timothymtorres: + - bugfix: Fix holymelon armor not inheriting magic resistance + - bugfix: Fix broken link to issue manager guide in Github contributor guide + - bugfix: Fix missing screentips plasmaman helmets and MOD suit hat stabilizer helmets. + tmyqlfpir: + - bugfix: Airlock shells are properly assigned circuit cameras modules +2024-11-14: + 00-Steven: + - bugfix: A bitrunner avatar spawning with the exact same name as a name currently + on the records no longer updates that record to match when the hacker alias + gets applied. + - qol: Net avatar ID cards use the net avatar's name instead of being generic. + Fikou: + - bugfix: a modsuit being deleted will delete its parts correctly + - bugfix: a modsuit needs its boots out to be moved by an AI + - qol: you can extend or retract a modsuits parts from the ui panel + - qol: modsuit cores have colorcoded charge bars in the ui panel + - qol: weapon recall module makes you pick up the weapon if its on your tile + - code_imp: tether module has its own button for cutting tethers instead of stealing + it from pins + - bugfix: armor booster doesnt protect from head injury without the helmet on + LT3: + - bugfix: Server config will now read the map vote configuration file + MTandi: + - qol: Chem dispenser UI droplets now have a shadow to not blend with the background + Majkl-J: + - qol: mech fabricator output direction can now be changed with a drag pull + Rhials: + - qol: Crayon bounties are less demanding, and require less crayons for payout. + SmArtKar: + - bugfix: Fixed ayylmao's brain examine lines + - bugfix: Fixed stingbangs using wrong sprites + - bugfix: Fixed detective action palette being invisible + SyncIt21: + - bugfix: holosign creators interact with storage items correctly + grungussuss: + - sound: party popper no longer makes reagent sloshing sounds + - sound: shoes pickup volume is louder now + mc-oofert: + - bugfix: meatlovers flatbread no longer tastes raw + timothymtorres: + - bugfix: Fix using chairs in holodeck to create infinite metal + xPokee, waterpig: + - code_imp: cleaned up beserk.dm +2024-11-15: + Absolucy: + - refactor: The ORM, "ore container", and order console UIs (produce, mining, bitrunner + vendors) now load icons in a more efficient manner. + - bugfix: Admin-deleting a mob now ghostizes it beforehand, preventing a runtime + error. + Ben10Omintrix: + - bugfix: fixes seedling ai getting stuck when trying to refill water from emptied + water tanks + Dawnseer: + - bugfix: removes the gas mask check for if the gas mask mouth is covered. Now it + only checks for filters and other cigs + GremlinSeeker: + - bugfix: Syndicate Biodome fixes + Majkl-J: + - bugfix: Fixes SSWardrobe stealing a camera from the camera app when on a dummy, + causing a harddel + Melbert: + - image: Speeds up some frames of the dust animation slightly + - image: Dust/remains spawned after being dusted are now aligned towards the bottom + of the tile + - image: Bigger mobs now produce bigger piles of ash + - image: Plasmamen now dust into plasma bones + SmArtKar: + - bugfix: Many years later, projectiles finally can pass through portals - this + time without crashing the server. + - bugfix: Fixed tenacity effect printing its messages when it shouldn't be, and + not removing its' effects when it should've. + - bugfix: Fixed scanner gates saying both bypass and detection lines when malfunctioning + SyncIt21: + - bugfix: Fixed visual glitches when inserting items from an slot other than the + hands into evidence bags & other storages that have quick gather mode enabled + - bugfix: silo connection on some machines won't time out when changing FPS settings + - code_imp: improved attack chain code for silo connection + Time-Green: + - bugfix: Nooartrium heart damage can no longer be bypassed with non-organic hearts + carlarctg: + - bugfix: you now snip cuffs with right click to prevent accidental cuts + - spellcheck: 'spellecheck: existence not existance' + - balance: Significantly buffed the anomalock modules. + - balance: Anomalock modules can be used with eachother. + - balance: Antigravity module costs 2 complexity. + - balance: Teleporter module is thrice as fast at teleporting with a slightly reduced + cooldown, but has a much larger power cost. + - code_imp: Changed how teleporter tracks maximum range to be less painful to the + end user. + - refactor: Refactored LoS checks to be a proc on atom, los_check + - balance: Kinesis module's default range has been extended to 8. + - balance: Kinesis module can drag around people in critical condition or worse. + - bugfix: vampires are a human subtype & have stomachs/lungs + grungussuss: + - sound: sandstone blocks have the correct sound now + - qol: added feedback for cutting mulebot wires + larentoun: + - bugfix: Fixes a runtime when wingless creature flaps their wings + mc-oofert: + - bugfix: Added dehydrator to birdshot kitchen. Surgery theatre now has a surgery + tray and surplus prosthetics. An unwired hallway APC is now wired. 1st SM filter + is now set to Nitrogen. Chemistry closet in pharmacy now contains extra chemicals + to compensate for not having a chemical storage. Atmos air for distro mixer + now mixes correctly + timothymtorres: + - balance: Lower the telecrystal price of the singularity beacon from 10 to 4 and + reduce the timer to 20 minutes before it can be purchased. + xPokee: + - bugfix: fixed brains turning invisible after being washed +2024-11-16: + Hardly: + - bugfix: Fixes watchers not attacking their targets in melee range + TheRealSpriteMan1337: + - spellcheck: Fixed some of the typos in computers' build & repair tips, adjusted + wording + Xander3359: + - bugfix: fix furious steel and blade ascension being jank + carlarctg: + - rscadd: Adds the Perceptomatrix Helm, a hallucination anomaly core item + zxaber: + - bugfix: Firelocks opened by a mech will correctly close when the mech moves out + of range. +2024-11-17: + Fikou: + - bugfix: xeno princesses no longer lose their unique identifier + JagOfTroy: + - bugfix: 'Fixed Automatic Announcement System bug with the Research Node Announcement + message not updating properly. It will now properly save and announce whatever + input the user desires! + + :cl:' + SmArtKar: + - rscdel: Removed N-Spect scanner's and scanner gate's ability to scan people for + contraband. + - bugfix: Fixed cats not begging people for food + - bugfix: Fixed a mixup in ripley plate sprites + - bugfix: Fixed tethers being able to move immovable objects (and delete gravgens) + - bugfix: Fixed a missing arg in fish release code causing runtimes + carlarctg: + - bugfix: '[no gbp] renmoves bartender skillchip from chipped quirk' + timothymtorres: + - bugfix: Fix radio jammer screentips +2024-11-18: + BurgerBB: + - qol: Reworks the Druggy visual effect to cause less eye strain. + EOBGames: + - rscadd: DONK CO. HAS UNVEILED A NEW RANGE OF READY-DONK PRODUCTS! GET THEM AT + CARGO TODAY! + - image: 'READY-DONK: NEW LOOK, SAME GREAT TASTE' + EnterTheJake: + - rscadd: New internal sleeper module for the Clarke. + - balance: Clarke Integrity has been bumped from 200 to 250 HP, melee armor bumped + from 20 to 40. + - balance: Exo mining module can now be used to scan vents and begin the wave defense + event. + - balance: The Clarke Ore Storage Module can now collect boulders and internally + smelt them. + - balance: Mech Pka is now bundled within the Clarke tech node, it now does more + damage and comes with the AOE upgrade preinstalled. + - balance: Exo mining drills are now a utility module rather than a weapon. + - balance: Applied Bluespace Research and Controlled Plasma tech nodes are no longer + hardlocked behind surveys, they instead favor from a discount if they are completed. + - bugfix: Fixes Exo Mech modules not granting life support to housed patients. + Ghommie: + - bugfix: You can no longer deploy survival/fishing capsules by the holodeck and + permanently brick it. + - balance: Tendril chests and mystery boxes can now be fished multiple times with + a 30 minutes cooldown. + - balance: other previously fishing loot available in limited amount, such as adamantine + and runite from plasma rivers, regenerate over time. + - rscadd: Using the gibber on people with fish tails now yield some fish tail fillets. + OrionTheFox: + - code_imp: The Greytide event will now play bolt audio and update the icon when + toggling door bolts. + Sothanforax: + - sound: Added sounds for the escape menu opening/closing/being open + TheBoondock: + - sound: added ignition and roaring fire sound to plasma/tritium fire + Vekter: + - map: Removes extra privacy shutters in the HoS' office on NebulaStation + Xackii: + - qol: heretic living heart now points to the target with an arrow. + - bugfix: pinpointer no longer breaks if scanned target disappears from our reality. + aaaa1023: + - bugfix: Birdshot Atmospherics now has an Air alarm + - bugfix: Birdshot Atmospherics pumping room APC is now connected to the main grid + - bugfix: Birdshot Port Primary Hallway APC is now connected to the main grid + carlarctg: + - bugfix: actually adds the perceptomatrix to the techweb + - bugfix: Reverts change that altered pyrokinesis recipe to require firebreath instead + of fiery sweat + grungussuss: + - sound: changed the welding hat toggle sfx + - qol: some hats now "compress" your hair to fit it the headwear better + - bugfix: xenos take more damage to husk + mc-oofert: + - bugfix: You may no longer use a climbing hook to phase through catwalks and such. + On the other hand, improved climbing hook aim and you may climb onto lattices + or catwalks + - balance: makes reputation locked gear instead be very very expensive and get cheaper + as you get to that rep + - bugfix: manufacturing sort-routers can no longer crawl + sqnztb: + - map: metastation's security mail chute no longer has an extra erroneous pipe. + thegrb93: + - bugfix: Fix broken cyborg inducer + timothymtorres: + - map: Fix missing CE Office Shutter Buttons on Icebox. Also removes a duplicate + keycard auth device. +2024-11-19: + Fikou: + - bugfix: Fixed chaplain divine archer outfit rolled down sprite + Jackal-boop: + - balance: syndicate GMO researchers have increased the effectiveness of their 'hot + potato' + carlarctg: + - qol: Improved fish lure descriptions to be easier to grok + fevryk: + - rscadd: Added new weapon carpenter hammer to black market uplink + - rscadd: Added killsound feature + tontyGH: + - bugfix: Haunted 8-balls are now usable again (FINALLY) + - bugfix: Haunted 8-ball UI now correctly displays your selected answer + - qol: Made the haunted 8-ball UI look a pinch better. Just a smidge. + - code_imp: Haunted 8-balls no longer use a duplicate answer list +2024-11-20: + Ben10Omintrix: + - refactor: floorbots have been refactored, please report any bugs + - rscadd: adds repairbots to the game! + Dawnseer: + - rscadd: Unleashed a great evil upon hoplines, in the form of a paperwork implant. + Dmeto: + - map: 'removes a unused disposal pipe from Cargo, fixes mining dock intercom. + + map:adds access helper to disposals south airlock.' + EnterTheJake: + - bugfix: FIxes insulated modsuits not granting full insulation. + Gaxeer: + - bugfix: AI controlled monkeys can again activate items in active hand + Hatterhat: + - bugfix: Crate Chaos's encrypted cache is now randomly placed again. + Jewelry-x: + - bugfix: changeling memories of victims no longer include `` and `
` + Melbert: + - balance: Having a broken arm affects your accuracy with ranged weapons fired with + that arm. Utilizing a painkiller will nullify this effect, however. + - balance: Painkillers will prevent your punches from being cancelled due to having + a broken arm. You'll still take damage, though. + - balance: Being drunk now affects your accuracy with ranged weapon. The bartender + is immune to this effect via their skillchip. + - code_imp: A lot of code involving left and right hand handling has been cleaned + up, easier to read. Report any oddities, like left and rights being flipped + grungussuss: + - bugfix: mobs can no longer hear their own breathing while deaf + - bugfix: a bug regarding orderable items has been fixed + - server: drone area restrictions are now config + - balance: drones now take less time to become "un-shy" of something after it's + been touched. + larentoun: + - bugfix: 'Mafia: Psych''s reveal ability now correctly has a single use' + - bugfix: 'Mafia: Fugitive''s vest ability now correctly should have two uses' +2024-11-21: + Ghommie: + - refactor: refactored aquariums heavily. Please report any fishy bug. + - rscadd: Added portable/handheld fish tanks to the game. They can be crafted with + an aquarium kit and 5 sheets of plastic. While portable, they cannot store fish + that are too big or if there're too many already. This restriction can be removed + by using the new "bluespace fish tank kit" techweb item. + - map: Replaced the lawyer's stationary pet aquarium with a fish tank, so you can + carry McGill around. + - balance: Reduced the iron cost of stationary aquariums a little. + - bugfix: Fixed a couple nits with the water visuals not appearing on objects with + a very low layer (rendered just above the floor), as well as atmos pipes looking + a bit funky on untiled turfs beside catwalks and platings when beside connected, + under-floored pipes. + SmArtKar: + - refactor: Firestacks, burning/acid/decaying effects and (brought back after being + temporarily removed) slimed status effects now use a new "shared" particles + system, which should considerably improve client performance when encountering + a lot of burning/slimed entities. + - bugfix: Berets no longer make you go bald above your earline. + SyncIt21: + - bugfix: plumbing pill press advertises the correct maximum volume of your selected + product instead of always 50u on the UI + - code_imp: plumbing pill press validates selected container to prevent href exploits + - code_imp: plumbing pill press validates input volume to see if it's a number + grungussuss: + - sound: the glove pickup sound will no longer play for modsuits undeploying + mc-oofert: + - qol: alerts that do stuff when clicked glow gold + necromanceranne: + - balance: 'After months of rigorous training montages, handwork and dedication. + Nanotrasen can proudly say that their staff is now more capable of performing + various judo techniques to disable and incapacitate any would-be troublemakers. + They have proclaimed the Spinward ''safer than ever''. Unfortunately for Nanotrasen + and the Spinward as a whole,, most of these same techniques have been incorporated + into the Tunnel Arts through the use of Khan spies. (See: Extremely bored assistants + who are getting tired of suplexing the clown). Is this the end for the Spinward + Sector? Will anyone be able to stop Maint Khan''s advance?' + - balance: Being grabbed can be harder to break out of depending on how vulnerable + you are; being prone, staggered and wounded, or just being a pushover (the quirk) + makes it easier to keep hold of you. Particularly if more than one of these + is true. Though if you're a fishman, you probably can't get grabbed at all. + - balance: Your arm power now contributes to the stamina damage someone is dealt + when they attempt and fail to break out of your grab. + - balance: Bartenders while drunk are great at grappling. + - bugfix: The values used for determining at what point you must resist if you have + taken damage and are staggered are now correctly set to 40, not incorrectly + to 80. + timothymtorres, MrMelbert: + - bugfix: Fix lustrous brain trauma not removing during polymorph +2024-11-22: + Acantharctia: + - balance: Numb quirk now stops you from screaming and seeing the damage overlay + when damaged. Better keep a close eye on your health. + Ben10Omintrix: + - bugfix: fixes monkey AI not being able to attack + Dawnseer: + - bugfix: Turns Cap (White) into a white cap in loadout + KikoWen0: + - map: Nebula Arrivals stairs are now easier to navigate + - map: Fixed unconnected wire in dormitories on Nebula + - map: Fixed engineers having free pass to cargo department on Nebula + Melbert: + - bugfix: Monkeys no longer ignore basic rules such as "you can't escape a passive + grab if you're cuffed or in crit" + - bugfix: Fixes some atmos and record related log messages + SmArtKar: + - bugfix: Reconnected Wawa's sci to atmos distro + - bugfix: Fixed incorrect coverage descriptions + - refactor: Converted most common particle sources to use our new pooling system. + - bugfix: Fixed all hoverboards having holyboard space slowdown effect + - bugfix: Fixed limping status effect harddels + SyncIt21: + - bugfix: Labour stacker machine gives points for sheets again + carlarctg: + - rscdel: Replaces UZI domain reward with ballistic chameleon gun + - rscadd: Also added a separate possible reward for an uzi bitrunning disk. + grungussuss: + - sound: the "curse you" sfx has been replaced + - admin: admins can now control the volume that some admin triggered sounds are + played at + jlsnow301: + - bugfix: TGUI panel light mode should no longer be gross gray + mc-oofert: + - rscdel: removed quiet succumb which is pointless now that the bug got fixed + necromanceranne: + - image: Cleans up some redundant pixels on the bowman sprites. + - image: Kitchen and eating utensils now have suit storage sprites. + - code_imp: The cost for Core Equipment for nuclear operatives will always be equal + to the cost of the contained items if purchased directly from the uplink. + - bugfix: Improves the descriptions for some uplink entries. + - bugfix: Correctly applies resistance and EMP proofing to the broadcasting camera + so that it doesn't cause weird things to happen. + tontyGH: + - code_imp: made /proc/getviewsize() pure + - refactor: mob/eye/ai_eye has been restructured, now inheriting from a generic + mob/eye/camera type + - refactor: advanced cameras and their subtypes are now mob/eye/camera/remote subtypes + - code_imp: the cameranet no longer expects the user to be an AI eye + - code_imp: remote camera eyes have had their initialization streamlined + - code_imp: remote cameras handle assigning and unassigning users by themselves + now + - code_imp: remote cameras now use weakrefs instead of hard referencing owners and + origins + - code_imp: also the sentient disease is_define was removed (we don't have those + anymore) + - bugfix: AI eyes no longer assign real names to themselves, fixing their orbit + name + wallem: + - image: Updates slot machine sprites diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi index 38921a6e484..83d7c14d0cc 100644 Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index 85d450e03bd..41bdf992bbf 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/effects/particles/smoke.dmi b/icons/effects/particles/smoke.dmi index 99123beeb59..7e271e51f09 100644 Binary files a/icons/effects/particles/smoke.dmi and b/icons/effects/particles/smoke.dmi differ diff --git a/icons/hud/64x16_actions.dmi b/icons/hud/64x16_actions.dmi index 812d888846e..23865a80f03 100644 Binary files a/icons/hud/64x16_actions.dmi and b/icons/hud/64x16_actions.dmi differ diff --git a/icons/hud/fishing_hud.dmi b/icons/hud/fishing_hud.dmi index f9d2d2ff9c4..84f739f9db2 100644 Binary files a/icons/hud/fishing_hud.dmi and b/icons/hud/fishing_hud.dmi differ diff --git a/icons/hud/radial_fishing.dmi b/icons/hud/radial_fishing.dmi index d77d9064882..daa4ce8abda 100644 Binary files a/icons/hud/radial_fishing.dmi and b/icons/hud/radial_fishing.dmi differ diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index e0206b97533..d540a55d344 100755 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/icons/hud/screen_full.dmi b/icons/hud/screen_full.dmi index dfe3bd12091..be4571638b1 100644 Binary files a/icons/hud/screen_full.dmi and b/icons/hud/screen_full.dmi differ diff --git a/icons/hud/screen_gen.dmi b/icons/hud/screen_gen.dmi index 5b1c24d7789..c38725e43e4 100644 Binary files a/icons/hud/screen_gen.dmi and b/icons/hud/screen_gen.dmi differ diff --git a/icons/mob/clothing/belt_mirror.dmi b/icons/mob/clothing/belt_mirror.dmi index 01cf7b38913..95f7bc00ae9 100644 Binary files a/icons/mob/clothing/belt_mirror.dmi and b/icons/mob/clothing/belt_mirror.dmi differ diff --git a/icons/mob/clothing/ears.dmi b/icons/mob/clothing/ears.dmi index e24b1bc02b1..beca6060b70 100644 Binary files a/icons/mob/clothing/ears.dmi and b/icons/mob/clothing/ears.dmi differ diff --git a/icons/mob/clothing/head/helmet.dmi b/icons/mob/clothing/head/helmet.dmi index db48dda1fd6..05fe660a33a 100644 Binary files a/icons/mob/clothing/head/helmet.dmi and b/icons/mob/clothing/head/helmet.dmi differ diff --git a/icons/mob/clothing/under/civilian.dmi b/icons/mob/clothing/under/civilian.dmi index d172185d4a4..63788c8d1ed 100644 Binary files a/icons/mob/clothing/under/civilian.dmi and b/icons/mob/clothing/under/civilian.dmi differ diff --git a/icons/mob/dust_animation.dmi b/icons/mob/dust_animation.dmi new file mode 100644 index 00000000000..10d8418a144 Binary files /dev/null and b/icons/mob/dust_animation.dmi differ diff --git a/icons/mob/silicon/cameramob.dmi b/icons/mob/eyemob.dmi similarity index 100% rename from icons/mob/silicon/cameramob.dmi rename to icons/mob/eyemob.dmi diff --git a/icons/mob/human/hair_masks.dmi b/icons/mob/human/hair_masks.dmi index 45ecb761d9a..5dbd4917a87 100644 Binary files a/icons/mob/human/hair_masks.dmi and b/icons/mob/human/hair_masks.dmi differ diff --git a/icons/mob/inhands/items/drinks_lefthand.dmi b/icons/mob/inhands/items/drinks_lefthand.dmi index fb2d9c22dd4..8168801dc0b 100644 Binary files a/icons/mob/inhands/items/drinks_lefthand.dmi and b/icons/mob/inhands/items/drinks_lefthand.dmi differ diff --git a/icons/mob/inhands/items/drinks_righthand.dmi b/icons/mob/inhands/items/drinks_righthand.dmi index 13f9dc0a026..d5ebddb2159 100644 Binary files a/icons/mob/inhands/items/drinks_righthand.dmi and b/icons/mob/inhands/items/drinks_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/hammers_lefthand.dmi b/icons/mob/inhands/weapons/hammers_lefthand.dmi index d065edd86e7..aaae035e056 100644 Binary files a/icons/mob/inhands/weapons/hammers_lefthand.dmi and b/icons/mob/inhands/weapons/hammers_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/hammers_righthand.dmi b/icons/mob/inhands/weapons/hammers_righthand.dmi index 022b281e462..6ff58389894 100644 Binary files a/icons/mob/inhands/weapons/hammers_righthand.dmi and b/icons/mob/inhands/weapons/hammers_righthand.dmi differ diff --git a/icons/mob/rideables/mecha.dmi b/icons/mob/rideables/mecha.dmi index 5960d054137..a52a3ff5d4b 100644 Binary files a/icons/mob/rideables/mecha.dmi and b/icons/mob/rideables/mecha.dmi differ diff --git a/icons/mob/silicon/aibots.dmi b/icons/mob/silicon/aibots.dmi index 86c450ef1ce..819eecea0b7 100644 Binary files a/icons/mob/silicon/aibots.dmi and b/icons/mob/silicon/aibots.dmi differ diff --git a/icons/mob/simple/mob.dmi b/icons/mob/simple/mob.dmi index 142481e08e9..74b90ffd95b 100644 Binary files a/icons/mob/simple/mob.dmi and b/icons/mob/simple/mob.dmi differ diff --git a/icons/obj/aquarium/fish.dmi b/icons/obj/aquarium/fish.dmi index 5c6a3203dba..53a218bc780 100644 Binary files a/icons/obj/aquarium/fish.dmi and b/icons/obj/aquarium/fish.dmi differ diff --git a/icons/obj/aquarium/supplies.dmi b/icons/obj/aquarium/supplies.dmi index 08f4923a0ab..73a1f5ffe34 100644 Binary files a/icons/obj/aquarium/supplies.dmi and b/icons/obj/aquarium/supplies.dmi differ diff --git a/icons/obj/aquarium/tanks.dmi b/icons/obj/aquarium/tanks.dmi index 44f526f8c99..b4a5e06471f 100644 Binary files a/icons/obj/aquarium/tanks.dmi and b/icons/obj/aquarium/tanks.dmi differ diff --git a/icons/obj/clothing/head/helmet.dmi b/icons/obj/clothing/head/helmet.dmi index 621afe57ddc..e2137737175 100644 Binary files a/icons/obj/clothing/head/helmet.dmi and b/icons/obj/clothing/head/helmet.dmi differ diff --git a/icons/obj/devices/mecha_equipment.dmi b/icons/obj/devices/mecha_equipment.dmi index ebfa5438ae9..e22b021ae23 100644 Binary files a/icons/obj/devices/mecha_equipment.dmi and b/icons/obj/devices/mecha_equipment.dmi differ diff --git a/icons/obj/devices/new_assemblies.dmi b/icons/obj/devices/new_assemblies.dmi index 7bf96e5ba92..f80f89ce20a 100644 Binary files a/icons/obj/devices/new_assemblies.dmi and b/icons/obj/devices/new_assemblies.dmi differ diff --git a/icons/obj/drinks/drinks.dmi b/icons/obj/drinks/drinks.dmi index 4b966cb2db2..5566cb09b2f 100644 Binary files a/icons/obj/drinks/drinks.dmi and b/icons/obj/drinks/drinks.dmi differ diff --git a/icons/obj/fishing.dmi b/icons/obj/fishing.dmi index d97384f1f79..ac307ae2384 100644 Binary files a/icons/obj/fishing.dmi and b/icons/obj/fishing.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index 7b6afcc37ff..acc932a4004 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/food/lizard.dmi b/icons/obj/food/lizard.dmi index 29b35cd8a96..44dc43e84de 100644 Binary files a/icons/obj/food/lizard.dmi and b/icons/obj/food/lizard.dmi differ diff --git a/icons/obj/machines/computer.dmi b/icons/obj/machines/computer.dmi index 9fcc46bf9b4..34f632044c1 100644 Binary files a/icons/obj/machines/computer.dmi and b/icons/obj/machines/computer.dmi differ diff --git a/icons/obj/machines/manufactorio.dmi b/icons/obj/machines/manufactorio.dmi index 31abf6f6a20..58b08e4ab44 100644 Binary files a/icons/obj/machines/manufactorio.dmi and b/icons/obj/machines/manufactorio.dmi differ diff --git a/icons/obj/medical/reagent_fillings.dmi b/icons/obj/medical/reagent_fillings.dmi index 0da57e714a5..163f41641dd 100644 Binary files a/icons/obj/medical/reagent_fillings.dmi and b/icons/obj/medical/reagent_fillings.dmi differ diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi index 48d01c3371b..5f850703d19 100644 Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ diff --git a/icons/obj/service/janitor.dmi b/icons/obj/service/janitor.dmi index 9f28c6bbe04..1ec087a7667 100644 Binary files a/icons/obj/service/janitor.dmi and b/icons/obj/service/janitor.dmi differ diff --git a/icons/obj/weapons/hammer.dmi b/icons/obj/weapons/hammer.dmi index 751e6267798..2b4e0cc8204 100644 Binary files a/icons/obj/weapons/hammer.dmi and b/icons/obj/weapons/hammer.dmi differ diff --git a/icons/turf/beach.dmi b/icons/turf/beach.dmi index aeb18a4fe61..183ed04f453 100644 Binary files a/icons/turf/beach.dmi and b/icons/turf/beach.dmi differ diff --git a/icons/turf/decals.dmi b/icons/turf/decals.dmi index 2c113e27d2c..c5664199ef5 100644 Binary files a/icons/turf/decals.dmi and b/icons/turf/decals.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index 09e08985946..ff9d1e62e9b 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/sound/attributions.txt b/sound/attributions.txt index dab6cedf7eb..a6cbf21f836 100644 --- a/sound/attributions.txt +++ b/sound/attributions.txt @@ -50,7 +50,7 @@ https://freesound.org/s/431740/ (CC 0) chainsaw_stop.ogg is adapted from kyles "chainsaw sawing short cuts +stop.flac" (CC 0) https://freesound.org/people/kyles/sounds/453256/ -clock.mp3 is adapted from Tetrisrocker's "Clock", which uses CC Attributions 0 +clock.ogg is adapted from Tetrisrocker's "Clock", which uses CC Attributions 0 https://freesound.org/people/Tetrisrocker/sounds/458627/ ark_activation.ogg is taken from ScottFerguson1's "Cucko Clock edited version", which is licensed under CC Attribution 3.0 @@ -191,6 +191,10 @@ liquid_pour2.ogg and liquid_pour3.ogg were cut from https://freesound.org/people/MattRuthSound/sounds/561896/ https://freesound.org/people/MattRuthSound/sounds/561895/ + +roaring_fire.ogg made from: 10835 big fire loop.wav by Robinhood76 -- https://freesound.org/s/612277/ -- License: Attribution NonCommercial 4.0 +fire_puff made from: Bonfire Being Lit by samararaine -- https://freesound.org/s/186374/ -- License: Creative Commons 0 + ayylien.ogg was made by remixing: SCIRetro_Energy Swells Synth_Funky Audio_Sonics Spices by Funky_Audio under CC0 -- https://freesound.org/people/realtheremin/sounds/119011/ scifi_scare_a.aiff by realtheremin under CC0 -- https://freesound.org/people/Funky_Audio/sounds/729392/ @@ -204,8 +208,12 @@ Bottle Tap.wav by alex_alexalex -- https://freesound.org/s/395492/ -- License: A beaker_place.ogg was made by cutting and lowering pitch: place glass object.wav by milpower -- https://freesound.org/s/353105/ -- License: Creative Commons 0 + + glass_reverse.ogg is adapted from a combination of: https://freesound.org/people/C_Rogers/sounds/203368/ -- glass-shattering-hit_01.ogg by C_Rogers on freesound.org (CC0) https://freesound.org/people/Czarcazas/sounds/330800/ -- Audio reversal/fading of Shattering Glass (Small) by Czarcazas -- https://freesound.org/s/330800/ -- License: Attribution 3.0 sound/effects/bonk.ogg - recorded by oranges on a coke zero bottle, edited by ninjanomnom, released to public domain + +sound\items\weapons\hammer_death_scream.ogg - Undefeatablesos' scream recorded by Niron3206, edited by Niron3206, License: Creative Commons 0 diff --git a/sound/effects/fire_puff.ogg b/sound/effects/fire_puff.ogg new file mode 100644 index 00000000000..c238584f55a Binary files /dev/null and b/sound/effects/fire_puff.ogg differ diff --git a/sound/effects/fireclip1.ogg b/sound/effects/fireclip1.ogg new file mode 100644 index 00000000000..89ba2395a4d Binary files /dev/null and b/sound/effects/fireclip1.ogg differ diff --git a/sound/effects/fireclip2.ogg b/sound/effects/fireclip2.ogg new file mode 100644 index 00000000000..c60990cd5bd Binary files /dev/null and b/sound/effects/fireclip2.ogg differ diff --git a/sound/effects/fireclip3.ogg b/sound/effects/fireclip3.ogg new file mode 100644 index 00000000000..cd686089598 Binary files /dev/null and b/sound/effects/fireclip3.ogg differ diff --git a/sound/effects/fireclip4.ogg b/sound/effects/fireclip4.ogg new file mode 100644 index 00000000000..9ae01a8d3ad Binary files /dev/null and b/sound/effects/fireclip4.ogg differ diff --git a/sound/effects/fireclip5.ogg b/sound/effects/fireclip5.ogg new file mode 100644 index 00000000000..855cc4b820d Binary files /dev/null and b/sound/effects/fireclip5.ogg differ diff --git a/sound/effects/fireclip6.ogg b/sound/effects/fireclip6.ogg new file mode 100644 index 00000000000..061cc42993d Binary files /dev/null and b/sound/effects/fireclip6.ogg differ diff --git a/sound/effects/fireclip7.ogg b/sound/effects/fireclip7.ogg new file mode 100644 index 00000000000..3a0f9840f3c Binary files /dev/null and b/sound/effects/fireclip7.ogg differ diff --git a/sound/effects/magic/attribution.txt b/sound/effects/magic/attribution.txt new file mode 100644 index 00000000000..448707cd70c --- /dev/null +++ b/sound/effects/magic/attribution.txt @@ -0,0 +1,2 @@ +curse.ogg made by sadboysuss +license: CC-BY-SA \ No newline at end of file diff --git a/sound/effects/magic/curse.ogg b/sound/effects/magic/curse.ogg index b6af218a658..b9134da2c5a 100644 Binary files a/sound/effects/magic/curse.ogg and b/sound/effects/magic/curse.ogg differ diff --git a/sound/effects/roaring_fire.ogg b/sound/effects/roaring_fire.ogg new file mode 100644 index 00000000000..b3927b69feb Binary files /dev/null and b/sound/effects/roaring_fire.ogg differ diff --git a/sound/effects/writing_pen/attribution.txt b/sound/effects/writing_pen/attribution.txt new file mode 100644 index 00000000000..7af5720c2fc --- /dev/null +++ b/sound/effects/writing_pen/attribution.txt @@ -0,0 +1,2 @@ +writing_pen made by sadboysuss +license: CC-BY-SA 3.0 \ No newline at end of file diff --git a/sound/effects/writing_pen/writing_pen1.ogg b/sound/effects/writing_pen/writing_pen1.ogg new file mode 100644 index 00000000000..1041e026e6b Binary files /dev/null and b/sound/effects/writing_pen/writing_pen1.ogg differ diff --git a/sound/effects/writing_pen/writing_pen2.ogg b/sound/effects/writing_pen/writing_pen2.ogg new file mode 100644 index 00000000000..855cf2c852e Binary files /dev/null and b/sound/effects/writing_pen/writing_pen2.ogg differ diff --git a/sound/effects/writing_pen/writing_pen3.ogg b/sound/effects/writing_pen/writing_pen3.ogg new file mode 100644 index 00000000000..2e9362c99db Binary files /dev/null and b/sound/effects/writing_pen/writing_pen3.ogg differ diff --git a/sound/effects/writing_pen/writing_pen4.ogg b/sound/effects/writing_pen/writing_pen4.ogg new file mode 100644 index 00000000000..cbd095ef3f2 Binary files /dev/null and b/sound/effects/writing_pen/writing_pen4.ogg differ diff --git a/sound/effects/writing_pen/writing_pen5.ogg b/sound/effects/writing_pen/writing_pen5.ogg new file mode 100644 index 00000000000..c3382cb1f59 Binary files /dev/null and b/sound/effects/writing_pen/writing_pen5.ogg differ diff --git a/sound/effects/writing_pen/writing_pen6.ogg b/sound/effects/writing_pen/writing_pen6.ogg new file mode 100644 index 00000000000..cffdc2810be Binary files /dev/null and b/sound/effects/writing_pen/writing_pen6.ogg differ diff --git a/sound/effects/writing_pen/writing_pen7.ogg b/sound/effects/writing_pen/writing_pen7.ogg new file mode 100644 index 00000000000..9f753efff68 Binary files /dev/null and b/sound/effects/writing_pen/writing_pen7.ogg differ diff --git a/sound/items/duct_tape/duct_tape_rip.ogg b/sound/items/duct_tape/duct_tape_rip.ogg index bf1746ef7c9..8a5df8ffdd8 100644 Binary files a/sound/items/duct_tape/duct_tape_rip.ogg and b/sound/items/duct_tape/duct_tape_rip.ogg differ diff --git a/sound/items/duct_tape/duct_tape_snap.ogg b/sound/items/duct_tape/duct_tape_snap.ogg index 354061af121..af253fc7ef2 100644 Binary files a/sound/items/duct_tape/duct_tape_snap.ogg and b/sound/items/duct_tape/duct_tape_snap.ogg differ diff --git a/sound/items/evidence_bag/attribution.txt b/sound/items/evidence_bag/attribution.txt new file mode 100644 index 00000000000..4cb126eb7cb --- /dev/null +++ b/sound/items/evidence_bag/attribution.txt @@ -0,0 +1,2 @@ +evidence_bag sounds made by sadboysuss +license: CC-BY-SA 3.0 \ No newline at end of file diff --git a/sound/items/evidence_bag/evidence_bag_drop.ogg b/sound/items/evidence_bag/evidence_bag_drop.ogg new file mode 100644 index 00000000000..6752ae5aa94 Binary files /dev/null and b/sound/items/evidence_bag/evidence_bag_drop.ogg differ diff --git a/sound/items/evidence_bag/evidence_bag_pickup.ogg b/sound/items/evidence_bag/evidence_bag_pickup.ogg new file mode 100644 index 00000000000..4027d9d0e8e Binary files /dev/null and b/sound/items/evidence_bag/evidence_bag_pickup.ogg differ diff --git a/sound/items/evidence_bag/evidence_bag_unzip.ogg b/sound/items/evidence_bag/evidence_bag_unzip.ogg new file mode 100644 index 00000000000..f30e9159cdb Binary files /dev/null and b/sound/items/evidence_bag/evidence_bag_unzip.ogg differ diff --git a/sound/items/evidence_bag/evidence_bag_zip.ogg b/sound/items/evidence_bag/evidence_bag_zip.ogg new file mode 100644 index 00000000000..6b6c2caba55 Binary files /dev/null and b/sound/items/evidence_bag/evidence_bag_zip.ogg differ diff --git a/sound/items/handling/shoes/sneakers_pickup1.ogg b/sound/items/handling/shoes/sneakers_pickup1.ogg index 9670ec324e3..7d86eaa562f 100644 Binary files a/sound/items/handling/shoes/sneakers_pickup1.ogg and b/sound/items/handling/shoes/sneakers_pickup1.ogg differ diff --git a/sound/items/weapons/hammer_death_scream.ogg b/sound/items/weapons/hammer_death_scream.ogg new file mode 100644 index 00000000000..cff35f23281 Binary files /dev/null and b/sound/items/weapons/hammer_death_scream.ogg differ diff --git a/sound/misc/escape_menu/attribution.txt b/sound/misc/escape_menu/attribution.txt new file mode 100644 index 00000000000..c4f5fc0206b --- /dev/null +++ b/sound/misc/escape_menu/attribution.txt @@ -0,0 +1,3 @@ +esc_middle, esc_open and esc_close are all modified from the following respectively: +Tape Recorder.wav by Pogotron -- https://freesound.org/s/61075/ -- License: Sampling+ +cassette tape deck slot open close hard nice spring various and spinout end.flac by kyles -- https://freesound.org/s/635487/ -- License: Creative Commons 0 diff --git a/sound/misc/escape_menu/esc_close.ogg b/sound/misc/escape_menu/esc_close.ogg new file mode 100644 index 00000000000..30946e77edd Binary files /dev/null and b/sound/misc/escape_menu/esc_close.ogg differ diff --git a/sound/misc/escape_menu/esc_middle.ogg b/sound/misc/escape_menu/esc_middle.ogg new file mode 100644 index 00000000000..4a8b3134926 Binary files /dev/null and b/sound/misc/escape_menu/esc_middle.ogg differ diff --git a/sound/misc/escape_menu/esc_open.ogg b/sound/misc/escape_menu/esc_open.ogg new file mode 100644 index 00000000000..4ad958d0bcb Binary files /dev/null and b/sound/misc/escape_menu/esc_open.ogg differ diff --git a/sound/voice/repairbot/brick.ogg b/sound/voice/repairbot/brick.ogg new file mode 100644 index 00000000000..78dae8633a3 Binary files /dev/null and b/sound/voice/repairbot/brick.ogg differ diff --git a/sound/voice/repairbot/cantanymore.ogg b/sound/voice/repairbot/cantanymore.ogg new file mode 100644 index 00000000000..4fc4fb216f7 Binary files /dev/null and b/sound/voice/repairbot/cantanymore.ogg differ diff --git a/sound/voice/repairbot/entropy.ogg b/sound/voice/repairbot/entropy.ogg new file mode 100644 index 00000000000..ab6a4f6a067 Binary files /dev/null and b/sound/voice/repairbot/entropy.ogg differ diff --git a/sound/voice/repairbot/fixit.ogg b/sound/voice/repairbot/fixit.ogg new file mode 100644 index 00000000000..20e095589cb Binary files /dev/null and b/sound/voice/repairbot/fixit.ogg differ diff --git a/sound/voice/repairbot/fixtouch.ogg b/sound/voice/repairbot/fixtouch.ogg new file mode 100644 index 00000000000..86ca24ffe3f Binary files /dev/null and b/sound/voice/repairbot/fixtouch.ogg differ diff --git a/sound/voice/repairbot/passionproject.ogg b/sound/voice/repairbot/passionproject.ogg new file mode 100644 index 00000000000..6f1fba1d452 Binary files /dev/null and b/sound/voice/repairbot/passionproject.ogg differ diff --git a/sound/voice/repairbot/patchingholes.ogg b/sound/voice/repairbot/patchingholes.ogg new file mode 100644 index 00000000000..6c65fc22b96 Binary files /dev/null and b/sound/voice/repairbot/patchingholes.ogg differ diff --git a/sound/voice/repairbot/pay.ogg b/sound/voice/repairbot/pay.ogg new file mode 100644 index 00000000000..0aa2b68868c Binary files /dev/null and b/sound/voice/repairbot/pay.ogg differ diff --git a/sound/voice/repairbot/strings.ogg b/sound/voice/repairbot/strings.ogg new file mode 100644 index 00000000000..bc897eacccd Binary files /dev/null and b/sound/voice/repairbot/strings.ogg differ diff --git a/strings/fishing_tips.txt b/strings/fishing_tips.txt index af67d15e27e..836cab85da5 100644 --- a/strings/fishing_tips.txt +++ b/strings/fishing_tips.txt @@ -54,3 +54,4 @@ Fish can grow in size and weight if you fed them somewhat frequently. Giving the Feeding a fish mutagen can triple the probability of generating evolved offsprings, provided it has an evolution. You can print fishing rods of different materials from an autolathe, which can inrease or decrease fishing difficulty, casting range, experience gained and can have other, special effects. Albeit scarcely, it's possible to catch fish made of the same materials of a custom material fishing rod. Equipping a shiny fishing hook and the quality of the bait can improve your odds. +You can use a fishing rod to snatch random organs during the "manipulate organs" step of the "organ manipulation" surgery. \ No newline at end of file diff --git a/strings/tips.txt b/strings/tips.txt index 836dee19a7b..2c5fc29ef87 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -55,6 +55,7 @@ As a Heretic, the Path of Cosmos allows you to take rightful ownership of the ve As a Janitor Cyborg, you are the bane of all slaughter demons and even Bubblegum himself. Cleaning up blood stains will severely gimp them. As a Janitor, if someone steals your janicart, you can instead use your space cleaner spray, grenades, water sprayer, exact bloody revenge or order another from Cargo. As a Janitor, mousetraps can be used to create bombs or booby-trap containers. +As a Janitor, you can command cleanbots by pointing at a tile and saying mop/clean. As a Medical Cyborg, you can fully perform surgery and even augment people. As a Medical Doctor, almost every type of wound can be treated at least temporarily with gauze. When in doubt, wrap it up! As a Medical Doctor, corpses placed inside a freezer or morgue tray will have their organs frozen preventing decay. If you don't have time to revive multiple dead bodies, transfer them to the morgue temporarily! diff --git a/tgstation.dme b/tgstation.dme index 22bc32801bf..6d2f54a2083 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -244,6 +244,7 @@ #include "code\__DEFINES\time.dm" #include "code\__DEFINES\tools.dm" #include "code\__DEFINES\toys.dm" +#include "code\__DEFINES\tracy.dm" #include "code\__DEFINES\trader.dm" #include "code\__DEFINES\transport.dm" #include "code\__DEFINES\tts.dm" @@ -436,6 +437,7 @@ #include "code\__HELPERS\heap.dm" #include "code\__HELPERS\hearted.dm" #include "code\__HELPERS\honkerblast.dm" +#include "code\__HELPERS\hud.dm" #include "code\__HELPERS\icon_smoothing.dm" #include "code\__HELPERS\icons.dm" #include "code\__HELPERS\jatum.dm" @@ -1049,6 +1051,7 @@ #include "code\datums\components\amputating_limbs.dm" #include "code\datums\components\anti_magic.dm" #include "code\datums\components\appearance_on_aggro.dm" +#include "code\datums\components\aquarium.dm" #include "code\datums\components\aquarium_content.dm" #include "code\datums\components\area_based_godmode.dm" #include "code\datums\components\area_sound_manager.dm" @@ -1156,6 +1159,7 @@ #include "code\datums\components\interaction_booby_trap.dm" #include "code\datums\components\irradiated.dm" #include "code\datums\components\item_equipped_movement_rustle.dm" +#include "code\datums\components\item_killsound.dm" #include "code\datums\components\itembound.dm" #include "code\datums\components\itempicky.dm" #include "code\datums\components\jetpack.dm" @@ -1883,6 +1887,7 @@ #include "code\datums\status_effects\_status_effect_helpers.dm" #include "code\datums\status_effects\agent_pinpointer.dm" #include "code\datums\status_effects\buffs.dm" +#include "code\datums\status_effects\death_sound.dm" #include "code\datums\status_effects\drug_effects.dm" #include "code\datums\status_effects\gas.dm" #include "code\datums\status_effects\grouped_effect.dm" @@ -2270,6 +2275,7 @@ #include "code\game\objects\effects\poster_motivational.dm" #include "code\game\objects\effects\powerup.dm" #include "code\game\objects\effects\rcd.dm" +#include "code\game\objects\effects\shared_particle_holder.dm" #include "code\game\objects\effects\spiderwebs.dm" #include "code\game\objects\effects\step_triggers.dm" #include "code\game\objects\effects\wanted_poster.dm" @@ -3203,6 +3209,7 @@ #include "code\modules\antagonists\heretic\items\labyrinth_handbook.dm" #include "code\modules\antagonists\heretic\items\madness_mask.dm" #include "code\modules\antagonists\heretic\items\unfathomable_curio.dm" +#include "code\modules\antagonists\heretic\knowledge\_heretic_paths.dm" #include "code\modules\antagonists\heretic\knowledge\ash_lore.dm" #include "code\modules\antagonists\heretic\knowledge\blade_lore.dm" #include "code\modules\antagonists\heretic\knowledge\cosmic_lore.dm" @@ -3799,6 +3806,7 @@ #include "code\modules\client\preferences_menu.dm" #include "code\modules\client\preferences_savefile.dm" #include "code\modules\client\preferences\_preference.dm" +#include "code\modules\client\preferences\accessibility.dm" #include "code\modules\client\preferences\addict.dm" #include "code\modules\client\preferences\admin.dm" #include "code\modules\client\preferences\age.dm" @@ -3814,7 +3822,6 @@ #include "code\modules\client\preferences\broadcast_login_logout.dm" #include "code\modules\client\preferences\chipped.dm" #include "code\modules\client\preferences\clothing.dm" -#include "code\modules\client\preferences\darkened_flash.dm" #include "code\modules\client\preferences\food_allergy.dm" #include "code\modules\client\preferences\fps.dm" #include "code\modules\client\preferences\gender.dm" @@ -3942,6 +3949,7 @@ #include "code\modules\clothing\head\mind_monkey_helmet.dm" #include "code\modules\clothing\head\moth.dm" #include "code\modules\clothing\head\papersack.dm" +#include "code\modules\clothing\head\perceptomatrix.dm" #include "code\modules\clothing\head\pirate.dm" #include "code\modules\clothing\head\religious.dm" #include "code\modules\clothing\head\soft_caps.dm" @@ -4669,7 +4677,7 @@ #include "code\modules\mapfluff\ruins\generic.dm" #include "code\modules\mapfluff\ruins\lavaland_ruin_code.dm" #include "code\modules\mapfluff\ruins\icemoonruin_code\commsagent.dm" -#include "code\modules\mapfluff\ruins\icemoonruin_code\hotsprings.dm" +#include "code\modules\mapfluff\ruins\icemoonruin_code\cursed_spring.dm" #include "code\modules\mapfluff\ruins\icemoonruin_code\lavaland_incursion.dm" #include "code\modules\mapfluff\ruins\icemoonruin_code\library.dm" #include "code\modules\mapfluff\ruins\icemoonruin_code\mailroom.dm" @@ -4803,7 +4811,6 @@ #include "code\modules\mob\mob_update_icons.dm" #include "code\modules\mob\status_procs.dm" #include "code\modules\mob\transform_procs.dm" -#include "code\modules\mob\camera\camera.dm" #include "code\modules\mob\dead\dead.dm" #include "code\modules\mob\dead\new_player\latejoin_menu.dm" #include "code\modules\mob\dead\new_player\login.dm" @@ -4818,6 +4825,9 @@ #include "code\modules\mob\dead\observer\observer_movement.dm" #include "code\modules\mob\dead\observer\observer_say.dm" #include "code\modules\mob\dead\observer\orbit.dm" +#include "code\modules\mob\eye\eye.dm" +#include "code\modules\mob\eye\camera\camera.dm" +#include "code\modules\mob\eye\camera\remote.dm" #include "code\modules\mob\living\blood.dm" #include "code\modules\mob\living\damage_procs.dm" #include "code\modules\mob\living\death.dm" @@ -4872,6 +4882,9 @@ #include "code\modules\mob\living\basic\bots\hygienebot\hygienebot_ai.dm" #include "code\modules\mob\living\basic\bots\medbot\medbot.dm" #include "code\modules\mob\living\basic\bots\medbot\medbot_ai.dm" +#include "code\modules\mob\living\basic\bots\repairbot\repairbot.dm" +#include "code\modules\mob\living\basic\bots\repairbot\repairbot_abilities.dm" +#include "code\modules\mob\living\basic\bots\repairbot\repairbot_ai.dm" #include "code\modules\mob\living\basic\bots\vibebot\vibebot.dm" #include "code\modules\mob\living\basic\bots\vibebot\vibebot_abilities.dm" #include "code\modules\mob\living\basic\bots\vibebot\vibebot_ai.dm" @@ -5330,7 +5343,6 @@ #include "code\modules\mob\living\simple_animal\bot\bot_announcement.dm" #include "code\modules\mob\living\simple_animal\bot\construction.dm" #include "code\modules\mob\living\simple_animal\bot\ed209bot.dm" -#include "code\modules\mob\living\simple_animal\bot\floorbot.dm" #include "code\modules\mob\living\simple_animal\bot\mulebot.dm" #include "code\modules\mob\living\simple_animal\bot\secbot.dm" #include "code\modules\mob\living\simple_animal\bot\SuperBeepsky.dm" diff --git a/tgui/package.json b/tgui/package.json index 57d02a389d2..f89241c5a3f 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -45,7 +45,7 @@ "mini-css-extract-plugin": "^2.9.2", "prettier": "^3.2.5", "sass": "^1.80.6", - "sass-loader": "^14.2.1", + "sass-loader": "^16.0.3", "style-loader": "^4.0.0", "swc-loader": "^0.2.6", "typescript": "^5.6.3", diff --git a/tgui/packages/tgui-dev-server/link/client.cjs b/tgui/packages/tgui-dev-server/link/client.cjs index b0e6f7bc9d4..052b3312f50 100644 --- a/tgui/packages/tgui-dev-server/link/client.cjs +++ b/tgui/packages/tgui-dev-server/link/client.cjs @@ -8,112 +8,109 @@ let socket; const queue = []; const subscribers = []; -const ensureConnection = () => { - if (process.env.NODE_ENV !== 'production') { - if (!window.WebSocket) { - return; +function ensureConnection() { + if (process.env.NODE_ENV === 'production') return; + + if (socket && socket.readyState !== WebSocket.CLOSED) return; + + if (!window.WebSocket) return; + + const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; + + socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`); + + socket.onopen = () => { + // Empty the message queue + while (queue.length !== 0) { + const msg = queue.shift(); + socket.send(msg); } - if (!socket || socket.readyState === WebSocket.CLOSED) { - const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; - socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`); - socket.onopen = () => { - // Empty the message queue - while (queue.length !== 0) { - const msg = queue.shift(); - socket.send(msg); - } - }; - socket.onmessage = (event) => { - const msg = JSON.parse(event.data); - for (let subscriber of subscribers) { - subscriber(msg); - } - }; + }; + + socket.onmessage = (event) => { + const msg = JSON.parse(event.data); + for (let subscriber of subscribers) { + subscriber(msg); } - } + }; - window.onunload = () => socket && socket.close(); -}; + window.onunload = () => socket?.close(); +} const subscribe = (fn) => subscribers.push(fn); +const primitiveReviver = (value) => { + if (typeof value === 'number' && !Number.isFinite(value)) { + return { + __number__: String(value), + }; + } + if (typeof value === 'undefined') { + return { + __undefined__: true, + }; + } + return value; +}; + /** * A json serializer which handles circular references and other junk. */ const serializeObject = (obj) => { let refs = []; - const primitiveReviver = (value) => { - if (typeof value === 'number' && !Number.isFinite(value)) { - return { - __number__: String(value), - }; + + const objectReviver = (key, value) => { + if (typeof value !== 'object') { + return primitiveReviver(value); } - if (typeof value === 'undefined') { + + if (value === null) { + return value; + } + // Circular reference + if (refs.includes(value)) { + return '[circular ref]'; + } + refs.push(value); + // Error object + const isError = + value instanceof Error || + (value.code && value.message && value.message.includes('Error')); + if (isError) { return { - __undefined__: true, + __error__: true, + string: String(value), + stack: value.stack, }; } - return value; - }; - const objectReviver = (key, value) => { - if (typeof value === 'object') { - if (value === null) { - return value; - } - // Circular reference - if (refs.includes(value)) { - return '[circular ref]'; - } - refs.push(value); - // Error object - // prettier-ignore - const isError = value instanceof Error || ( - value.code && value.message && value.message.includes('Error') - ); - if (isError) { - return { - __error__: true, - string: String(value), - stack: value.stack, - }; - } - // Array - if (Array.isArray(value)) { - return value.map(primitiveReviver); - } - return value; + // Array + if (Array.isArray(value)) { + return value.map(primitiveReviver); } - return primitiveReviver(value); + return value; }; + const json = JSON.stringify(obj, objectReviver); refs = null; return json; }; const sendMessage = (msg) => { - if (process.env.NODE_ENV !== 'production') { - const json = serializeObject(msg); - // Send message using WebSocket - if (window.WebSocket) { - ensureConnection(); - if (socket.readyState === WebSocket.OPEN) { - socket.send(json); - } else { - // Keep only 100 latest messages in the queue - if (queue.length > 100) { - queue.shift(); - } - queue.push(json); - } - } - // Send message using plain HTTP request. - else { - const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; - const req = new XMLHttpRequest(); - req.open('POST', `http://${DEV_SERVER_IP}:3001`, true); - req.timeout = 250; - req.send(json); + if (process.env.NODE_ENV === 'production') return; + + const json = serializeObject(msg); + // Send message using WebSocket + if (!window.WebSocket) return; + + ensureConnection(); + if (socket.readyState === WebSocket.OPEN) { + socket.send(json); + } else { + // Keep only 100 latest messages in the queue + if (queue.length > 100) { + queue.shift(); } + queue.push(json); } }; @@ -140,33 +137,33 @@ const setupHotReloading = () => { ) { return; } - if (module.hot) { - ensureConnection(); - sendLogEntry(0, null, 'setting up hot reloading'); - subscribe((msg) => { - const { type } = msg; - sendLogEntry(0, null, 'received', type); - if (type === 'hotUpdate') { - const status = module.hot.status(); - if (status !== 'idle') { - sendLogEntry(0, null, 'hot reload status:', status); - return; - } - module.hot - .check({ - ignoreUnaccepted: true, - ignoreDeclined: true, - ignoreErrored: true, - }) - .then((modules) => { - sendLogEntry(0, null, 'outdated modules', modules); - }) - .catch((err) => { - sendLogEntry(0, null, 'reload error', err); - }); - } - }); - } + if (!module.hot) return; + + ensureConnection(); + sendLogEntry(0, null, 'setting up hot reloading'); + subscribe(({ type }) => { + sendLogEntry(0, null, 'received', type); + if (type !== 'hotUpdate') return; + + const status = module.hot.status(); + if (status !== 'idle') { + sendLogEntry(0, null, 'hot reload status:', status); + return; + } + + module.hot + .check({ + ignoreUnaccepted: true, + ignoreDeclined: true, + ignoreErrored: true, + }) + .then((modules) => { + sendLogEntry(0, null, 'outdated modules', modules); + }) + .catch((err) => { + sendLogEntry(0, null, 'reload error', err); + }); + }); }; module.exports = { diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js index 2a1f551bf6e..832b964d3fc 100644 --- a/tgui/packages/tgui-dev-server/link/server.js +++ b/tgui/packages/tgui-dev-server/link/server.js @@ -4,7 +4,6 @@ * @license MIT */ -import http from 'http'; import { inspect } from 'util'; import { createLogger, directLog } from '../logging.js'; @@ -26,7 +25,6 @@ class LinkServer { logger.log('setting up'); this.wss = null; this.setupWebSocketLink(); - this.setupHttpLink(); } // WebSocket-based client link @@ -46,29 +44,6 @@ class LinkServer { logger.log(`listening on port ${port} (WebSocket)`); } - // One way HTTP-based client link for IE8 - setupHttpLink() { - const port = 3001; - this.httpServer = http.createServer((req, res) => { - if (req.method === 'POST') { - let body = ''; - req.on('data', (chunk) => { - body += chunk.toString(); - }); - req.on('end', () => { - const msg = deserializeObject(body); - this.handleLinkMessage(null, msg); - res.end(); - }); - return; - } - res.write('Hello'); - res.end(); - }); - this.httpServer.listen(port); - logger.log(`listening on port ${port} (HTTP)`); - } - handleLinkMessage(ws, msg) { const { type, payload } = msg; if (type === 'log') { @@ -77,17 +52,20 @@ class LinkServer { if (level <= 0 && !DEBUG) { return; } - // prettier-ignore - directLog(ns, ...args.map(arg => { - if (typeof arg === 'object') { - return inspect(arg, { - depth: Infinity, - colors: true, - compact: 8, - }); - } - return arg; - })); + + directLog( + ns, + ...args.map((arg) => { + if (typeof arg === 'object') { + return inspect(arg, { + depth: Infinity, + colors: true, + compact: 8, + }); + } + return arg; + }), + ); return; } if (type === 'relay') { diff --git a/tgui/packages/tgui-dev-server/logging.js b/tgui/packages/tgui-dev-server/logging.js index 4ec09008ff5..7f76f5dc50e 100644 --- a/tgui/packages/tgui-dev-server/logging.js +++ b/tgui/packages/tgui-dev-server/logging.js @@ -7,7 +7,7 @@ const inception = Date.now(); // Runtime detection -const isNode = process && process.release && process.release.name === 'node'; +const isNode = process?.release?.name === 'node'; let isChrome = false; try { isChrome = window.navigator.userAgent.toLowerCase().includes('chrome'); diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index 01ed38de1f4..e5f58d1c801 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -4,6 +4,7 @@ "version": "5.0.3", "type": "module", "dependencies": { + "@types/ws": "^8.5.13", "axios": "^1.7.7", "glob": "^7.2.3", "source-map": "^0.7.4", diff --git a/tgui/packages/tgui-panel/package.json b/tgui/packages/tgui-panel/package.json index 325e2fde7ca..f761ce3c616 100644 --- a/tgui/packages/tgui-panel/package.json +++ b/tgui/packages/tgui-panel/package.json @@ -11,6 +11,7 @@ "react-dom": "^18.3.1", "tgui": "workspace:*", "tgui-dev-server": "workspace:*", - "tgui-polyfill": "workspace:*" + "tgui-polyfill": "workspace:*", + "tgui-styles": "^0.0.10" } } diff --git a/tgui/packages/tgui-panel/styles/components/Chat.scss b/tgui/packages/tgui-panel/styles/components/Chat.scss index b439ace8c58..882f86a7aaa 100644 --- a/tgui/packages/tgui-panel/styles/components/Chat.scss +++ b/tgui/packages/tgui-panel/styles/components/Chat.scss @@ -8,7 +8,7 @@ @use '~tgui/styles/base.scss'; @use '~tgui/styles/colors.scss'; -$text-color: #abc6ec !default; +$text-color: hsl(210, 50%, 80%) !default; $color-bg-section: base.$color-bg-section !default; .Chat { @@ -21,11 +21,11 @@ $color-bg-section: base.$color-bg-section !default; font-size: 0.7em; padding: 0.2em 0.3em; line-height: 1; - color: white; + color: hsl(0, 0%, 100%); text-align: center; white-space: nowrap; vertical-align: middle; - background-color: crimson; + background-color: hsl(348, 83%, 47%); border-radius: 10px; transition: font-size 200ms ease-out; @@ -68,12 +68,12 @@ $color-bg-section: base.$color-bg-section !default; } .Chat__highlight { - color: #000; + color: hsl(0, 0%, 0%); } .Chat__highlight--restricted { - color: #fff; - background-color: #a00; + color: hsl(0, 0%, 100%); + background-color: hsl(0, 100%, 33.3%); font-weight: bold; } @@ -83,7 +83,7 @@ $color-bg-section: base.$color-bg-section !default; .ChatMessage--highlighted { position: relative; - border-left: math.div(1em, 6) solid rgba(255, 221, 68); + border-left: math.div(1em, 6) solid hsl(50, 100%, 63.5%); padding-left: 0.5em; &:after { @@ -93,7 +93,7 @@ $color-bg-section: base.$color-bg-section !default; bottom: 0; left: 0; right: 0; - background-color: rgba(255, 221, 68, 0.1); + background-color: hsla(50, 100%, 63.5%, 0.1); // Make this click-through since this is an overlay pointer-events: none; } diff --git a/tgui/packages/tgui-panel/styles/components/Notifications.scss b/tgui/packages/tgui-panel/styles/components/Notifications.scss index f3669967fd7..2e99e59a20a 100644 --- a/tgui/packages/tgui-panel/styles/components/Notifications.scss +++ b/tgui/packages/tgui-panel/styles/components/Notifications.scss @@ -11,8 +11,8 @@ } .Notification { - color: #fff; - background-color: crimson; + color: hsl(0, 0%, 100%); + background-color: hsl(348, 83%, 47%); padding: 0.5em; margin: 1em 0; diff --git a/tgui/packages/tgui-panel/styles/components/Ping.scss b/tgui/packages/tgui-panel/styles/components/Ping.scss index 251b0fd9539..292caf11105 100644 --- a/tgui/packages/tgui-panel/styles/components/Ping.scss +++ b/tgui/packages/tgui-panel/styles/components/Ping.scss @@ -23,6 +23,6 @@ $border-color: rgba(140, 140, 140, 0.5) !default; left: 0.5em; width: 0.5em; height: 0.5em; - background-color: #888; + background-color: hsl(0, 0%, 53.3%); border-radius: 0.25em; } diff --git a/tgui/packages/tgui-panel/styles/main.scss b/tgui/packages/tgui-panel/styles/main.scss index 08e60d18ee6..60198a654b9 100644 --- a/tgui/packages/tgui-panel/styles/main.scss +++ b/tgui/packages/tgui-panel/styles/main.scss @@ -5,11 +5,13 @@ @use 'sass:meta'; @use 'sass:color'; +// Components +@use 'pkg:tgui-styles'; @use '~tgui/styles/colors.scss'; @use '~tgui/styles/base.scss' with ( - $color-bg: #202020, - $color-bg-section: color.adjust(#202020, $lightness: -5%), + $color-bg: hsl(0, 0%, 12.5%), + $color-bg-section: color.adjust(hsl(0, 0%, 12.5%), $lightness: -5%), $color-bg-grad-spread: 0% ); @@ -23,29 +25,6 @@ @include meta.load-css('~tgui/styles/atomic/outline.scss'); @include meta.load-css('~tgui/styles/atomic/text.scss'); -// Components -@include meta.load-css('~tgui/styles/components/BlockQuote.scss'); -@include meta.load-css('~tgui/styles/components/Button.scss'); -@include meta.load-css('~tgui/styles/components/ColorBox.scss'); -@include meta.load-css('~tgui/styles/components/Dimmer.scss'); -@include meta.load-css('~tgui/styles/components/Divider.scss'); -@include meta.load-css('~tgui/styles/components/Dropdown.scss'); -@include meta.load-css('~tgui/styles/components/Flex.scss'); -@include meta.load-css('~tgui/styles/components/Input.scss'); -@include meta.load-css('~tgui/styles/components/Knob.scss'); -@include meta.load-css('~tgui/styles/components/LabeledList.scss'); -@include meta.load-css('~tgui/styles/components/Modal.scss'); -@include meta.load-css('~tgui/styles/components/NoticeBox.scss'); -@include meta.load-css('~tgui/styles/components/NumberInput.scss'); -@include meta.load-css('~tgui/styles/components/ProgressBar.scss'); -@include meta.load-css('~tgui/styles/components/Section.scss'); -@include meta.load-css('~tgui/styles/components/Slider.scss'); -@include meta.load-css('~tgui/styles/components/Stack.scss'); -@include meta.load-css('~tgui/styles/components/Table.scss'); -@include meta.load-css('~tgui/styles/components/Tabs.scss'); -@include meta.load-css('~tgui/styles/components/TextArea.scss'); -@include meta.load-css('~tgui/styles/components/Tooltip.scss'); - // Components specific to tgui-panel @include meta.load-css('./components/Chat.scss'); @include meta.load-css('./components/Ping.scss'); diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index c96385fba8b..c7c5d47e1a7 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -27,15 +27,15 @@ img.icon { } a { - color: #397ea5; + color: hsl(201.7, 48.6%, 43.5%); } a.visited { - color: #7c00e6; + color: hsl(272.3, 100%, 45.1%); } a:visited { - color: #7c00e6; + color: hsl(272.3, 100%, 45.1%); } a.popt { @@ -139,7 +139,7 @@ a.popt { } .highlightPopup input.highlightColor { - background-color: #ffff00; + background-color: hsl(60, 100%, 50%); } .highlightPopup input.highlightTermSubmit { @@ -199,7 +199,7 @@ a.popt { /* MOTD */ .motd { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); font-family: Verdana, sans-serif; white-space: normal; } @@ -210,7 +210,7 @@ a.popt { .motd h4, .motd h5, .motd h6 { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); text-decoration: underline; } @@ -219,7 +219,7 @@ a.popt { .motd a:visited, .motd a:active, .motd a:hover { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); } /* ADD HERE FOR BOLD */ @@ -255,13 +255,13 @@ h3, h4, h5, h6 { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); font-family: Georgia, Verdana, sans-serif; } h1.alert, h2.alert { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); } em { @@ -270,7 +270,7 @@ em { } .ooc { - color: #cca300; + color: hsl(47.9, 100%, 40%); font-weight: bold; } @@ -280,22 +280,22 @@ em { } .adminobserverooc { - color: #0099cc; + color: hsl(195, 100%, 40%); font-weight: bold; } .adminooc { - color: #3d5bc3; + color: hsl(226.6, 52.8%, 50.2%); font-weight: bold; } .adminsay { - color: #ff4500; + color: hsl(16.2, 100%, 50%); font-weight: bold; } .admin { - color: #5975da; + color: hsl(227, 63.5%, 60.2%); font-weight: bold; } @@ -322,91 +322,91 @@ em { } .deadsay { - color: #e2c1ff; + color: hsl(271.9, 100%, 87.8%); } .binarysay { - color: #1e90ff; + color: hsl(209.6, 100%, 55.9%); } .binarysay a { - color: #00ff00; + color: hsl(120, 100%, 50%); } .binarysay a:active, .binarysay a:visited { - color: #88ff88; + color: hsl(120, 100%, 76.7%); } /* RADIO COLORS */ /* IF YOU CHANGE THIS KEEP IT IN SYNC WITH TGUI CONSTANTS */ .radio { - color: #1ecc43; + color: hsl(132.8, 74.4%, 45.9%); } .sciradio { - color: #c68cfa; + color: hsl(271.6, 91.7%, 76.5%); } .comradio { - color: #fcdf03; + color: hsl(53, 97.6%, 50%); } .secradio { - color: #dd3535; + color: hsl(0, 71.2%, 53.7%); } .medradio { - color: #57b8f0; + color: hsl(202, 83.6%, 64.1%); } .engradio { - color: #f37746; + color: hsl(17, 87.8%, 61.4%); } .suppradio { - color: #b88646; + color: hsl(33.7, 44.9%, 49.8%); } .servradio { - color: #6ca729; + color: hsl(88.1, 60.6%, 40.8%); } .syndradio { - color: #8f4a4b; + color: hsl(359.1, 31.8%, 42.5%); } .gangradio { - color: #ac2ea1; + color: hsl(305.2, 57.8%, 42.7%); } .centcomradio { - color: #2681a5; + color: hsl(197, 62.6%, 39.8%); } .aiprivradio { - color: #d65d95; + color: hsl(332.2, 59.6%, 60.2%); } .enteradio { - color: #79c5a8; + color: hsl(157.1, 39.6%, 62.4%); } .redteamradio { - color: #ff4444 !important; + color: hsl(0, 100%, 63.3%) !important; } .blueteamradio { - color: #3434fd !important; + color: hsl(240, 98%, 59.8%) !important; } .greenteamradio { - color: #34fd34 !important; + color: hsl(120, 98%, 59.8%) !important; } .yellowteamradio { - color: #fdfd34 !important; + color: hsl(60, 98%, 59.8%) !important; } .yell { @@ -414,122 +414,122 @@ em { } .alert { - color: #d82020; + color: hsl(0, 74.2%, 48.6%); } .userdanger { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-weight: bold; font-size: 185%; } .bolddanger { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-weight: bold; } .danger { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); } .tinydanger { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-size: 85%; } .smalldanger { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-size: 90%; } .warning { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-style: italic; } .alertwarning { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; } .boldwarning { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-style: italic; font-weight: bold; } .announce { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-weight: bold; } .boldannounce { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-weight: bold; } .minorannounce { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-weight: bold; font-size: 185%; } .minoralert { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); font-size: 125%; } .priorityannounce { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); font-weight: bold; font-size: 225%; } .prioritytitle { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-weight: bold; font-size: 185%; } .priorityalert { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-size: 140%; } .greenannounce { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); font-weight: bold; } .rose { - color: #ff5050; + color: hsl(0, 100%, 65.7%); } .info { - color: #9ab0ff; + color: hsl(226.9, 100%, 80.2%); } .notice { - color: #6685f5; + color: hsl(227, 87.7%, 68%); } .tinynotice { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-size: 85%; } .tinynoticeital { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-style: italic; font-size: 85%; } .smallnotice { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-size: 90%; } .smallnoticeital { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-style: italic; font-size: 90%; } @@ -540,218 +540,218 @@ em { } .boldnotice { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-weight: bold; } .hear { - color: #6685f5; + color: hsl(227, 87.7%, 68%); font-style: italic; } .adminnotice { - color: #6685f5; + color: hsl(227, 87.7%, 68%); } .adminhelp { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; } .unconscious { - color: #a4bad6; + color: hsl(213.6, 37.9%, 74.1%); font-weight: bold; } .suicide { - color: #ff5050; + color: hsl(0, 100%, 65.7%); font-style: italic; } .green { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); } .grey { - color: #838383; + color: hsl(0, 0%, 51.4%); } .red { - color: #ff0000; + color: hsl(0, 100%, 50%); } .blue { - color: #215cff; + color: hsl(224.1, 100%, 56.5%); } .nicegreen { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); } .boldnicegreen { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); font-weight: bold; } .blob { - color: #ee4000; + color: hsl(16.1, 100%, 46.7%); } .blobannounce { - color: #556b2f; + color: hsl(82, 39%, 30.2%); font-weight: bold; font-size: 185%; } .cult { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); } .cult_italic { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-style: italic; } .cult_bold { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-style: italic; font-weight: bold; } .cult_bold_italic { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 185%; } .cult_large { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 185%; } .narsie { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 925%; } .narsiesmall { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 370%; } .colossus { - color: #7f282a; + color: hsl(358.6, 52.1%, 32.7%); font-size: 310%; } .hierophant { - color: #b441ee; + color: hsl(279.9, 83.6%, 59.4%); font-weight: bold; font-style: italic; } .hierophant_warning { - color: #c56bf1; + color: hsl(280.3, 82.7%, 68.2%); font-style: italic; } .purple { - color: #9956d3; + color: hsl(272.2, 58.7%, 58.2%); } .holoparasite { - color: #88809c; + color: hsl(257.1, 12.4%, 55.7%); } .revennotice { - color: #c099e2; + color: hsl(272.1, 55.7%, 74.3%); } .revenboldnotice { - color: #c099e2; + color: hsl(272.1, 55.7%, 74.3%); font-weight: bold; } .revenbignotice { - color: #c099e2; + color: hsl(272.1, 55.7%, 74.3%); font-weight: bold; font-size: 185%; } .revenminor { - color: #823abb; + color: hsl(273.5, 52.7%, 48%); } .revenwarning { - color: #760fbb; + color: hsl(275.9, 85.1%, 39.6%); font-style: italic; } .revendanger { - color: #760fbb; + color: hsl(275.9, 85.1%, 39.6%); font-weight: bold; font-size: 185%; } .deconversion_message { - color: #a947ff; + color: hsl(272, 100%, 63.9%); font-size: 185%; font-style: italic; } .ghostalert { - color: #6600ff; + color: hsl(264, 100%, 50%); font-style: italic; font-weight: bold; } .alien { - color: #855d85; + color: hsl(300, 17.7%, 44.3%); } .noticealien { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); } .alertalien { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); font-weight: bold; } .changeling { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); font-style: italic; } .alertsyndie { - color: #ff0000; + color: hsl(0, 100%, 50%); font-size: 185%; font-weight: bold; } .spiderbroodmother { - color: #8800ff; + color: hsl(272, 100%, 50%); font-weight: bold; font-size: 185%; } .spiderbreacher { - color: #e8b670; + color: hsl(35, 72.3%, 67.5%); font-weight: bold; font-size: 140%; } .spiderscout { - color: #231d98; + color: hsl(242.9, 68%, 35.5%); font-weight: bold; font-size: 120%; } .interface { - color: #750e75; + color: hsl(300, 78.6%, 25.7%); } .sans { @@ -767,7 +767,7 @@ em { } .tape_recorder { - color: #ff0000; + color: hsl(0, 100%, 50%); font-family: 'Courier New', cursive, sans-serif; } @@ -802,17 +802,17 @@ em { } .greentext { - color: #059223; + color: hsl(132.8, 93.4%, 29.6%); font-size: 185%; } .redtext { - color: #c51e1e; + color: hsl(0, 73.6%, 44.5%); font-size: 185%; } .clown { - color: #ff70c1; + color: hsl(326, 100%, 72%); font-size: 160%; font-family: 'Comic Sans MS', cursive, sans-serif; font-weight: bold; @@ -824,13 +824,13 @@ em { } .his_grace { - color: #15d512; + color: hsl(119.1, 84.4%, 45.3%); font-family: 'Courier New', cursive, sans-serif; font-style: italic; } .hypnophrase { - color: #202020; + color: hsl(0, 0%, 12.5%); font-weight: bold; animation: hypnocolor 1500ms infinite; animation-direction: alternate; @@ -838,43 +838,43 @@ em { @keyframes hypnocolor { 0% { - color: #202020; + color: hsl(0, 0%, 12.5%); } 25% { - color: #4b02ac; + color: hsl(265.8, 97.7%, 34.1%); } 50% { - color: #9f41f1; + color: hsl(272, 86.3%, 60%); } 75% { - color: #541c9c; + color: hsl(266.2, 69.6%, 36.1%); } 100% { - color: #7adbf3; + color: hsl(191.9, 83.4%, 71.6%); } } .phobia { - color: #dd0000; + color: hsl(0, 100%, 43.3%); font-weight: bold; animation: phobia 750ms infinite; } @keyframes phobia { 0% { - color: #f75a5a; + color: hsl(0, 90.8%, 66.1%); } 50% { - color: #dd0000; + color: hsl(0, 100%, 43.3%); } 100% { - color: #f75a5a; + color: hsl(0, 90.8%, 66.1%); } } @@ -892,7 +892,7 @@ em { } .memo { - color: #638500; + color: hsl(75.3, 100%, 26.1%); text-align: center; } @@ -902,35 +902,35 @@ em { } .abductor { - color: #c204c2; + color: hsl(300, 96%, 38.8%); font-style: italic; } .mind_control { - color: #df3da9; + color: hsl(320, 71.7%, 55.7%); font-size: 100%; font-weight: bold; font-style: italic; } .slime { - color: #00ced1; + color: hsl(180.9, 100%, 41%); } .drone { - color: #848482; + color: hsl(60, 0.8%, 51.4%); } .monkey { - color: #975032; + color: hsl(17.8, 50.2%, 39.4%); } .swarmer { - color: #2c75ff; + color: hsl(219.2, 100%, 58.6%); } .resonate { - color: #298f85; + color: hsl(174.1, 55.4%, 36.1%); } .upside_down { @@ -954,7 +954,7 @@ em { } .internal.boldnshit { - color: #3d5bc3; + color: hsl(226.6, 52.8%, 50.2%); font-weight: bold; } @@ -983,8 +983,8 @@ em { } .examine_block { - background: #1b1c1e; - border: 1px solid #a4bad6; + background: hsl(220, 5.3%, 11.2%); + border: 1px solid hsl(213.6, 37.9%, 74.1%); margin: 0.5em; padding: 0.5em 0.75em; } @@ -1006,7 +1006,7 @@ em { .separator::after { content: ''; flex: 1; - border-bottom: 1px solid #a4bad6; + border-bottom: 1px solid hsl(213.6, 37.9%, 74.1%); } .separator:not(:empty)::before { @@ -1036,51 +1036,51 @@ em { } $alert-stripe-colors: ( - 'default': #00283a, - 'green': #003d00, - 'blue': #00283a, - 'pink': #30001b, - 'yellow': #574a00, - 'orange': #593400, - 'red': #420000, - 'purple': #2c0030, - 'grey': #252525, + 'default': hsl(198.6, 100%, 11.4%), + 'green': hsl(120, 100%, 12%), + 'blue': hsl(198.6, 100%, 11.4%), + 'pink': hsl(326.2, 100%, 9.4%), + 'yellow': hsl(51, 100%, 17.1%), + 'orange': hsl(35.1, 100%, 17.5%), + 'red': hsl(0, 100%, 12.9%), + 'purple': hsl(295, 100%, 9.4%), + 'grey': hsl(0, 0%, 14.5%), ); $alert-stripe-alternate-colors: ( - 'default': #003045, - 'green': #004700, - 'blue': #003045, - 'pink': #400025, - 'yellow': #4d4100, - 'orange': #6b4200, - 'red': #520000, - 'purple': #38003d, - 'grey': #292929, + 'default': hsl(198.3, 100%, 13.5%), + 'green': hsl(120, 100%, 13.9%), + 'blue': hsl(198.3, 100%, 13.5%), + 'pink': hsl(325.3, 100%, 12.5%), + 'yellow': hsl(50.6, 100%, 15.1%), + 'orange': hsl(37, 100%, 21%), + 'red': hsl(0, 100%, 16.1%), + 'purple': hsl(295.1, 100%, 12%), + 'grey': hsl(0, 0%, 16.1%), ); $alert-major-header-colors: ( - 'default': #33d5ff, - 'green': #00ff80, - 'blue': #33d5ff, - 'pink': #ff5297, - 'yellow': #fff4e0, - 'orange': #feefe7, - 'red': #ff5297, - 'purple': #c7a1f7, - 'grey': #ff5297, + 'default': hsl(192.4, 100%, 60%), + 'green': hsl(150.1, 100%, 50%), + 'blue': hsl(192.4, 100%, 60%), + 'pink': hsl(336.1, 100%, 66.1%), + 'yellow': hsl(38.7, 100%, 93.9%), + 'orange': hsl(20.9, 92%, 95.1%), + 'red': hsl(336.1, 100%, 66.1%), + 'purple': hsl(266.5, 84.3%, 80%), + 'grey': hsl(336.1, 100%, 66.1%), ); $alert-subheader-header-colors: ( - 'default': #ff5297, - 'green': #ff85b5, - 'blue': #ff5297, - 'pink': #33d5ff, - 'yellow': #33d5ff, - 'orange': #33d5ff, - 'red': #33d5ff, - 'purple': #33d5ff, - 'grey': #33d5ff, + 'default': hsl(336.1, 100%, 66.1%), + 'green': hsl(336.4, 100%, 76.1%), + 'blue': hsl(336.1, 100%, 66.1%), + 'pink': hsl(192.4, 100%, 60%), + 'yellow': hsl(192.4, 100%, 60%), + 'orange': hsl(192.4, 100%, 60%), + 'red': hsl(192.4, 100%, 60%), + 'purple': hsl(192.4, 100%, 60%), + 'grey': hsl(192.4, 100%, 60%), ); $border-width: 4; @@ -1107,8 +1107,8 @@ $border-width-px: $border-width * 1px; } .major_announcement_text { - color: #eaeaea; - background-color: #131313; + color: hsl(0, 0%, 91.8%); + background-color: hsl(0, 0%, 7.5%); font-weight: bold; font-size: 100%; text-align: left; @@ -1129,8 +1129,8 @@ $border-width-px: $border-width * 1px; } .minor_announcement_text { - background-color: #202020; - color: #eaeaea; + background-color: hsl(0, 0%, 12.5%); + color: hsl(0, 0%, 91.8%); padding: 0.5rem 0.5rem; text-align: left; font-size: 100%; @@ -1143,18 +1143,18 @@ $border-width-px: $border-width * 1px; } .ooc_alert { - background: #4d4100; - border: 1px solid #cca300; + background: hsl(50.6, 100%, 15.1%); + border: 1px solid hsl(47.9, 100%, 40%); margin: 0.5em; padding: 0.5em 0.5em 0.5em 0.2em; - color: #ffffff; + color: hsl(0, 0%, 100%); font-weight: bold; display: flex; flex-direction: column; } .ooc_announcement_text { - color: #cca300; + color: hsl(47.9, 100%, 40%); padding: 0.5em 0 0 0.35em; display: flex; flex-direction: column; @@ -1162,7 +1162,7 @@ $border-width-px: $border-width * 1px; @each $color-name, $color-value in $alert-stripe-colors { .chat_alert_#{$color-name} { - color: #ffffff; + color: hsl(0, 0%, 100%); padding: 0.5rem 0.5rem; box-shadow: none; font-weight: bold; diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index 07d91b9fc89..01c0044ee81 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -11,7 +11,7 @@ body { padding: 0; margin: 0; height: 100%; - color: #000000; + color: hsl(0, 0%, 0%); } body { @@ -45,15 +45,15 @@ img.icon { } a { - color: #0000ff; + color: hsl(240, 100%, 50%); } a.visited { - color: #ff00ff; + color: hsl(300, 100%, 50%); } a:visited { - color: #ff00ff; + color: hsl(300, 100%, 50%); } a.popt { @@ -157,7 +157,7 @@ a.popt { } .highlightPopup input.highlightColor { - background-color: #ffff00; + background-color: hsl(60, 100%, 50%); } .highlightPopup input.highlightTermSubmit { @@ -217,7 +217,7 @@ a.popt { /* MOTD */ .motd { - color: #638500; + color: hsl(75.3, 100%, 26.1%); font-family: Verdana, sans-serif; white-space: normal; } @@ -228,7 +228,7 @@ a.popt { .motd h4, .motd h5, .motd h6 { - color: #638500; + color: hsl(75.3, 100%, 26.1%); text-decoration: underline; } @@ -237,7 +237,7 @@ a.popt { .motd a:visited, .motd a:active, .motd a:hover { - color: #638500; + color: hsl(75.3, 100%, 26.1%); } /* ADD HERE FOR BOLD */ @@ -273,13 +273,13 @@ h3, h4, h5, h6 { - color: #0000ff; + color: hsl(240, 100%, 50%); font-family: Georgia, Verdana, sans-serif; } h1.alert, h2.alert { - color: #000000; + color: hsl(0, 0%, 0%); } em { @@ -288,7 +288,7 @@ em { } .ooc { - color: #002eb8; + color: hsl(225, 100%, 36.1%); font-weight: bold; } @@ -298,22 +298,22 @@ em { } .adminobserverooc { - color: #0099cc; + color: hsl(195, 100%, 40%); font-weight: bold; } .adminooc { - color: #700038; + color: hsl(330, 100%, 22%); font-weight: bold; } .adminsay { - color: #ff4500; + color: hsl(16.2, 100%, 50%); font-weight: bold; } .admin { - color: #4473ff; + color: hsl(224.9, 100%, 63.3%); font-weight: bold; } @@ -339,90 +339,90 @@ em { } .deadsay { - color: #5c00e6; + color: hsl(264, 100%, 45.1%); } .binarysay { - color: #20c20e; - background-color: #000000; + color: hsl(114, 86.5%, 40.8%); + background-color: hsl(0, 0%, 0%); display: block; } .binarysay a { - color: #00ff00; + color: hsl(120, 100%, 50%); } .binarysay a:active, .binarysay a:visited { - color: #88ff88; + color: hsl(120, 100%, 76.7%); } .radio { - color: #008000; + color: hsl(120, 100%, 25.1%); } .sciradio { - color: #993399; + color: hsl(300, 50%, 40%); } .comradio { - color: #948f02; + color: hsl(57.9, 97.3%, 29.4%); } .secradio { - color: #a30000; + color: hsl(0, 100%, 32%); } .medradio { - color: #337296; + color: hsl(201.8, 49.3%, 39.4%); } .engradio { - color: #fb5613; + color: hsl(17.3, 96.7%, 52.9%); } .suppradio { - color: #a8732b; + color: hsl(34.6, 59.2%, 41.4%); } .servradio { - color: #6eaa2c; + color: hsl(88.6, 58.9%, 42%); } .syndradio { - color: #6d3f40; + color: hsl(358.7, 26.7%, 33.7%); } .gangradio { - color: #ac2ea1; + color: hsl(305.2, 57.8%, 42.7%); } .centcomradio { - color: #686868; + color: hsl(0, 0%, 40.8%); } .aiprivradio { - color: #ff00ff; + color: hsl(300, 100%, 50%); } .enteradio { - color: #5c8a87; + color: hsl(176.1, 20%, 45.1%); } .redteamradio { - color: #ff0000 !important; + color: hsl(0, 100%, 50%) !important; } .blueteamradio { - color: #0000ff !important; + color: hsl(240, 100%, 50%) !important; } .greenteamradio { - color: #00ff00 !important; + color: hsl(120, 100%, 50%) !important; } .yellowteamradio { - color: #d1ba22 !important; + color: hsl(52.1, 72%, 47.6%) !important; } .yell { @@ -430,345 +430,345 @@ em { } .alert { - color: #ff0000; + color: hsl(0, 100%, 50%); } h1.alert, h2.alert { - color: #000000; + color: hsl(0, 0%, 0%); } .userdanger { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; font-size: 185%; } .bolddanger { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; } .danger { - color: #ff0000; + color: hsl(0, 100%, 50%); } .tinydanger { - color: #ff0000; + color: hsl(0, 100%, 50%); font-size: 85%; } .smalldanger { - color: #ff0000; + color: hsl(0, 100%, 50%); font-size: 90%; } .warning { - color: #ff0000; + color: hsl(0, 100%, 50%); font-style: italic; } .alertwarning { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; } .boldwarning { - color: #ff0000; + color: hsl(0, 100%, 50%); font-style: italic; font-weight: bold; } .announce { - color: #228b22; + color: hsl(120, 60.7%, 33.9%); font-weight: bold; } .boldannounce { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; } .minorannounce { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; font-size: 185%; } .minoralert { - color: #000000; + color: hsl(0, 0%, 0%); font-size: 125%; } .priorityannounce { - color: #000000; + color: hsl(0, 0%, 0%); font-weight: bold; font-size: 225%; } .prioritytitle { - color: #0000ff; + color: hsl(240, 100%, 50%); font-weight: bold; font-size: 185%; } .priorityalert { - color: #ff0000; + color: hsl(0, 100%, 50%); font-size: 140%; } .greenannounce { - color: #00ff00; + color: hsl(120, 100%, 50%); font-weight: bold; } .rose { - color: #ff5050; + color: hsl(0, 100%, 65.7%); } .info { - color: #0000cc; + color: hsl(240, 100%, 40%); } .notice { - color: #000099; + color: hsl(240, 100%, 30%); } .tinynotice { - color: #000099; + color: hsl(240, 100%, 30%); font-size: 85%; } .tinynoticeital { - color: #000099; + color: hsl(240, 100%, 30%); font-style: italic; font-size: 85%; } .smallnotice { - color: #000099; + color: hsl(240, 100%, 30%); font-size: 90%; } .smallnoticeital { - color: #000099; + color: hsl(240, 100%, 30%); font-style: italic; font-size: 90%; } .boldnotice { - color: #000099; + color: hsl(240, 100%, 30%); font-weight: bold; } .hear { - color: #000099; + color: hsl(240, 100%, 30%); font-style: italic; } .adminnotice { - color: #0000ff; + color: hsl(240, 100%, 50%); } .adminhelp { - color: #ff0000; + color: hsl(0, 100%, 50%); font-weight: bold; } .unconscious { - color: #0000ff; + color: hsl(240, 100%, 50%); font-weight: bold; } .suicide { - color: #ff5050; + color: hsl(0, 100%, 65.7%); font-style: italic; } .green { - color: #03ff39; + color: hsl(132.9, 100%, 50.6%); } .grey { - color: #838383; + color: hsl(0, 0%, 51.4%); } .red { - color: #ff0000; + color: hsl(0, 100%, 50%); } .blue { - color: #0000ff; + color: hsl(240, 100%, 50%); } .nicegreen { - color: #14a833; + color: hsl(132.6, 78.7%, 36.9%); } .boldnicegreen { - color: #14a833; + color: hsl(132.6, 78.7%, 36.9%); font-weight: bold; } .cult { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); } .cultitalic { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-style: italic; } .cult_bold { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-style: italic; font-weight: bold; } .cult_bold_italic { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 185%; } .cult_large { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 185%; } .blob { - color: #ee4000; + color: hsl(16.1, 100%, 46.7%); } .blobannounce { - color: #323f1c; + color: hsl(82.3, 38.5%, 17.8%); font-weight: bold; font-size: 185%; } .narsie { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 925%; } .narsiesmall { - color: #973e3b; + color: hsl(2, 43.8%, 41.2%); font-weight: bold; font-size: 370%; } .colossus { - color: #7f282a; + color: hsl(358.6, 52.1%, 32.7%); font-size: 310%; } .hierophant { - color: #660099; + color: hsl(280, 100%, 30%); font-weight: bold; font-style: italic; } .hierophant_warning { - color: #660099; + color: hsl(280, 100%, 30%); font-style: italic; } .purple { - color: #5e2d79; + color: hsl(278.7, 45.8%, 32.5%); } .holoparasite { - color: #35333a; + color: hsl(257.1, 6.4%, 21.4%); } .revennotice { - color: #1d2953; + color: hsl(226.7, 48.2%, 22%); } .revenboldnotice { - color: #1d2953; + color: hsl(226.7, 48.2%, 22%); font-weight: bold; } .revenbignotice { - color: #1d2953; + color: hsl(226.7, 48.2%, 22%); font-weight: bold; font-size: 185%; } .revenminor { - color: #823abb; + color: hsl(273.5, 52.7%, 48%); } .revenwarning { - color: #760fbb; + color: hsl(275.9, 85.1%, 39.6%); font-style: italic; } .revendanger { - color: #760fbb; + color: hsl(275.9, 85.1%, 39.6%); font-weight: bold; font-size: 185%; } .deconversion_message { - color: #5000a0; + color: hsl(270, 100%, 31.4%); font-size: 185%; font-style: italic; } .ghostalert { - color: #5c00e6; + color: hsl(264, 100%, 45.1%); font-style: italic; font-weight: bold; } .alien { - color: #543354; + color: hsl(300, 24.4%, 26.5%); } .noticealien { - color: #00c000; + color: hsl(120, 100%, 37.6%); } .alertalien { - color: #00c000; + color: hsl(120, 100%, 37.6%); font-weight: bold; } .changeling { - color: #800080; + color: hsl(300, 100%, 25.1%); font-style: italic; } .alertsyndie { - color: #ff0000; + color: hsl(0, 100%, 50%); font-size: 185%; font-weight: bold; } .spiderbroodmother { - color: #4d004d; + color: hsl(300, 100%, 15.1%); font-weight: bold; font-size: 185%; } .spiderbreacher { - color: #804b02; + color: hsl(34.8, 96.9%, 25.5%); font-weight: bold; font-size: 140%; } .spiderscout { - color: #0c0674; + color: hsl(243.3, 90.2%, 23.9%); font-weight: bold; font-size: 120%; } .interface { - color: #330033; + color: hsl(300, 100%, 10%); } .sans { @@ -784,7 +784,7 @@ h2.alert { } .tape_recorder { - color: #800000; + color: hsl(0, 100%, 25.1%); font-family: 'Courier New', cursive, sans-serif; } @@ -819,17 +819,17 @@ h2.alert { } .greentext { - color: #00ff00; + color: hsl(120, 100%, 50%); font-size: 185%; } .redtext { - color: #ff0000; + color: hsl(0, 100%, 50%); font-size: 185%; } .clown { - color: #ff69bf; + color: hsl(325.6, 100%, 70.6%); font-size: 160%; font-family: 'Comic Sans MS', cursive, sans-serif; font-weight: bold; @@ -841,13 +841,13 @@ h2.alert { } .his_grace { - color: #15d512; + color: hsl(119.1, 84.4%, 45.3%); font-family: 'Courier New', cursive, sans-serif; font-style: italic; } .hypnophrase { - color: #0d0d0d; + color: hsl(0, 0%, 5.1%); font-weight: bold; animation: hypnocolor 1500ms infinite; animation-direction: alternate; @@ -855,43 +855,43 @@ h2.alert { @keyframes hypnocolor { 0% { - color: #0d0d0d; + color: hsl(0, 0%, 5.1%); } 25% { - color: #410194; + color: hsl(266.1, 98.7%, 29.2%); } 50% { - color: #7f17d8; + color: hsl(272.3, 80.8%, 46.9%); } 75% { - color: #410194; + color: hsl(266.1, 98.7%, 29.2%); } 100% { - color: #3bb5d3; + color: hsl(191.8, 63.3%, 52.9%); } } .phobia { - color: #dd0000; + color: hsl(0, 100%, 43.3%); font-weight: bold; animation: phobia 750ms infinite; } @keyframes phobia { 0% { - color: #0d0d0d; + color: hsl(0, 0%, 5.1%); } 50% { - color: #dd0000; + color: hsl(0, 100%, 43.3%); } 100% { - color: #0d0d0d; + color: hsl(0, 0%, 5.1%); } } @@ -909,7 +909,7 @@ h2.alert { } .memo { - color: #638500; + color: hsl(75.3, 100%, 26.1%); text-align: center; } @@ -919,35 +919,35 @@ h2.alert { } .abductor { - color: #800080; + color: hsl(300, 100%, 25.1%); font-style: italic; } .mind_control { - color: #a00d6f; + color: hsl(320, 85%, 33.9%); font-size: 100%; font-weight: bold; font-style: italic; } .slime { - color: #00ced1; + color: hsl(180.9, 100%, 41%); } .drone { - color: #848482; + color: hsl(60, 0.8%, 51.4%); } .monkey { - color: #975032; + color: hsl(17.8, 50.2%, 39.4%); } .swarmer { - color: #2c75ff; + color: hsl(219.2, 100%, 58.6%); } .resonate { - color: #298f85; + color: hsl(174.1, 55.4%, 36.1%); } .upside_down { @@ -1000,8 +1000,8 @@ h2.alert { } .examine_block { - background: #f2f7fa; - border: 1px solid #111a27; + background: hsl(202.5, 44.4%, 96.5%); + border: 1px solid hsl(215.5, 39.3%, 11%); margin: 0.5em; padding: 0.5em 0.75em; } @@ -1023,7 +1023,7 @@ h2.alert { .separator::after { content: ''; flex: 1; - border-bottom: 1px solid #111a27; + border-bottom: 1px solid hsl(215.5, 39.3%, 11%); } .separator:not(:empty)::before { @@ -1053,51 +1053,51 @@ h2.alert { } $alert-stripe-colors: ( - 'default': #b3bfff, - 'green': #adffad, - 'blue': #b3bfff, - 'pink': #ffb3df, - 'yellow': #fff3b3, - 'orange': #ffe2b3, - 'red': #ffb3b3, - 'purple': #fac2ff, - 'grey': #e3e3e3, + 'default': hsl(230.5, 100%, 85.1%), + 'green': hsl(120, 100%, 83.9%), + 'blue': hsl(230.5, 100%, 85.1%), + 'pink': hsl(325.3, 100%, 85.1%), + 'yellow': hsl(50.5, 100%, 85.1%), + 'orange': hsl(37.1, 100%, 85.1%), + 'red': hsl(0, 100%, 85.1%), + 'purple': hsl(295.1, 100%, 88%), + 'grey': hsl(0, 0%, 89%), ); $alert-stripe-alternate-colors: ( - 'default': #bdc8ff, - 'green': #bdffbd, - 'blue': #bdc8ff, - 'pink': #ffc2e5, - 'yellow': #fff5c2, - 'orange': #ffe8c2, - 'red': #ffc2c2, - 'purple': #fbd1ff, - 'grey': #ebebeb, + 'default': hsl(230, 100%, 87.1%), + 'green': hsl(120, 100%, 87.1%), + 'blue': hsl(230, 100%, 87.1%), + 'pink': hsl(325.6, 100%, 88%), + 'yellow': hsl(50.2, 100%, 88%), + 'orange': hsl(37.4, 100%, 88%), + 'red': hsl(0, 100%, 88%), + 'purple': hsl(294.8, 100%, 91%), + 'grey': hsl(0, 0%, 92.2%), ); $alert-major-header-colors: ( - 'default': #003061, - 'green': #005229, - 'blue': #003061, - 'pink': #800033, - 'yellow': #754900, - 'orange': #823208, - 'red': #800029, - 'purple': #450d8c, - 'grey': #800033, + 'default': hsl(210.3, 100%, 19%), + 'green': hsl(150, 100%, 16.1%), + 'blue': hsl(210.3, 100%, 19%), + 'pink': hsl(336.1, 100%, 25.1%), + 'yellow': hsl(37.4, 100%, 22.9%), + 'orange': hsl(20.7, 88.4%, 27.1%), + 'red': hsl(340.8, 100%, 25.1%), + 'purple': hsl(266.5, 83%, 30%), + 'grey': hsl(336.1, 100%, 25.1%), ); $alert-subheader-header-colors: ( - 'default': #6b0020, - 'green': #6b0020, - 'blue': #6b0020, - 'pink': #002c85, - 'yellow': #002c85, - 'orange': #002c85, - 'red': #002c85, - 'purple': #002c85, - 'grey': #002c85, + 'default': hsl(342.1, 100%, 21%), + 'green': hsl(342.1, 100%, 21%), + 'blue': hsl(342.1, 100%, 21%), + 'pink': hsl(220.2, 100%, 26.1%), + 'yellow': hsl(220.2, 100%, 26.1%), + 'orange': hsl(220.2, 100%, 26.1%), + 'red': hsl(220.2, 100%, 26.1%), + 'purple': hsl(220.2, 100%, 26.1%), + 'grey': hsl(220.2, 100%, 26.1%), ); $border-width: 4; @@ -1124,8 +1124,8 @@ $border-width-px: $border-width * 1px; } .major_announcement_text { - color: #131313; - background-color: #eaeaea; + color: hsl(0, 0%, 7.5%); + background-color: hsl(0, 0%, 91.8%); font-weight: bold; font-size: 100%; text-align: left; @@ -1146,8 +1146,8 @@ $border-width-px: $border-width * 1px; } .minor_announcement_text { - background-color: #eaeaea; - color: #202020; + background-color: hsl(0, 0%, 91.8%); + color: hsl(0, 0%, 12.5%); padding: 0.5rem 0.5rem; text-align: left; font-size: 100%; @@ -1160,18 +1160,18 @@ $border-width-px: $border-width * 1px; } .ooc_alert { - background: #bdc8ff; - border: 1px solid #002eb8; + background: hsl(230, 100%, 87.1%); + border: 1px solid hsl(225, 100%, 36.1%); margin: 0.5em; padding: 0.5em 0.5em 0.5em 0.2em; - color: #00283a; + color: hsl(198.6, 100%, 11.4%); font-weight: bold; display: flex; flex-direction: column; } .ooc_announcement_text { - color: #002eb8; + color: hsl(225, 100%, 36.1%); padding: 0.5em 0 0 0.35em; display: flex; flex-direction: column; @@ -1179,7 +1179,7 @@ $border-width-px: $border-width * 1px; @each $color-name, $color-value in $alert-stripe-colors { .chat_alert_#{$color-name} { - color: #ffffff; + color: hsl(0, 0%, 100%); padding: 0.5rem 0.5rem; box-shadow: none; font-weight: bold; diff --git a/tgui/packages/tgui-panel/styles/themes/light.scss b/tgui/packages/tgui-panel/styles/themes/light.scss index f8bfd67ee93..0d8e301001f 100644 --- a/tgui/packages/tgui-panel/styles/themes/light.scss +++ b/tgui/packages/tgui-panel/styles/themes/light.scss @@ -7,21 +7,20 @@ @use 'sass:meta'; @use '~tgui/styles/colors.scss' with ( - $primary: #ffffff, + $primary: hsl(0, 0%, 100%), $bg-lightness: -25%, $fg-lightness: -10%, - $label: #3b3b3b, + $label: hsl(0, 0%, 23.1%), // Makes button look actually grey due to weird maths. - $grey: #ffffff, + $grey: hsl(0, 0%, 100%), // Commenting out color maps will adjust all colors based on the lightness // settings above, but will add extra 10KB to the theme. // $fg-map-keys: (), // $bg-map-keys: (), ); @use '~tgui/styles/base.scss' with ( - $color-fg: #000000, - $color-bg: #eeeeee, - $color-bg-section: #ffffff, + $color-fg: hsl(0, 0%, 0%), + $color-bg: hsl(0, 0%, 93.3%), $color-bg-grad-spread: 0% ); @@ -33,7 +32,7 @@ // Components @include meta.load-css( - '~tgui/styles/components/Tabs.scss', + 'pkg:tgui-styles/components/Tabs', $with: ( 'text-color': rgba(0, 0, 0, 0.5), 'color-default': rgba(0, 0, 0, 1), @@ -41,35 +40,39 @@ 'tab-color-hovered': rgba(0, 0, 0, 0.075) ) ); - @include meta.load-css('~tgui/styles/components/Section.scss'); @include meta.load-css( - '~tgui/styles/components/Button.scss', + 'pkg:tgui-styles/components/Section', + $with: ('background-color': hsl(0, 0%, 100%)) + ); + + @include meta.load-css( + 'pkg:tgui-styles/components/Button', $with: ( - 'color-default': #bbbbbb, - 'color-disabled': #363636, - 'color-selected': #0668b8, - 'color-caution': #be6209, - 'color-danger': #9a9d00, + 'color-default': hsl(0, 0%, 73.3%), + 'color-disabled': hsl(0, 0%, 21.2%), + 'color-selected': hsl(204, 94%, 35.3%), + 'color-caution': hsl(28, 91%, 38.2%), + 'color-danger': hsl(62, 100%, 30.2%), 'color-transparent-text': rgba(0, 0, 0, 0.5) ) ); @include meta.load-css( - '~tgui/styles/components/Input.scss', + 'pkg:tgui-styles/components/Input', $with: ( 'border-color': colors.fg(colors.$label), - 'background-color': #e6e6e6 + 'background-color': hsl(0, 0%, 90.2%) ) ); - @include meta.load-css('~tgui/styles/components/NumberInput.scss'); - @include meta.load-css('~tgui/styles/components/TextArea.scss'); - @include meta.load-css('~tgui/styles/components/Knob.scss'); - @include meta.load-css('~tgui/styles/components/Slider.scss'); - @include meta.load-css('~tgui/styles/components/ProgressBar.scss'); + @include meta.load-css('pkg:tgui-styles/components/NumberInput'); + @include meta.load-css('pkg:tgui-styles/components/TextArea'); + @include meta.load-css('pkg:tgui-styles/components/Knob'); + @include meta.load-css('pkg:tgui-styles/components/Slider'); + @include meta.load-css('pkg:tgui-styles/components/ProgressBar'); // Components specific to tgui-panel @include meta.load-css( '../components/Chat.scss', - $with: ('text-color': #000000) + $with: ('text-color': hsl(0, 0%, 0%)) ); // Layouts diff --git a/tgui/packages/tgui-say/package.json b/tgui/packages/tgui-say/package.json index 5c8eaf390a9..d8c09604c1b 100644 --- a/tgui/packages/tgui-say/package.json +++ b/tgui/packages/tgui-say/package.json @@ -9,6 +9,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tgui": "workspace:*", - "tgui-polyfill": "workspace:*" + "tgui-polyfill": "workspace:*", + "tgui-styles": "^0.0.10" } } diff --git a/tgui/packages/tgui-say/styles/colors.scss b/tgui/packages/tgui-say/styles/colors.scss index 178e6757d57..e8760064b74 100644 --- a/tgui/packages/tgui-say/styles/colors.scss +++ b/tgui/packages/tgui-say/styles/colors.scss @@ -1,32 +1,32 @@ @use 'sass:map'; -$background: #131313; -$button: #1f1f1f; -$lightMode: #ffffff; -$lightBorder: #bbbbbb; -$lightHover: #eaeaea; +$background: hsl(0, 0%, 7.5%); +$button: hsl(0, 0%, 12.2%); +$lightMode: hsl(0, 0%, 100%); +$lightBorder: hsl(0, 0%, 73.3%); +$lightHover: hsl(0, 0%, 91.8%); $_channel_map: ( - 'Admin': #ffbbff, - 'AI': #d65d95, - 'CCom': #2681a5, - 'Cling': #4c701f, - 'Cmd': #fcdf03, - 'Engi': #f37746, - 'Hive': #855d85, - 'io': #1e90ff, - 'Me': #5975da, - 'Med': #57b8f0, - 'OOC': #cca300, - 'LOOC': #6fbeff, - 'Ent': #5c8a87, - 'Radio': #1ecc43, - 'Say': #a4bad6, - 'Sci': #c68cfa, - 'Sec': #dd3535, - 'Supp': #b88646, - 'Svc': #6ca729, - 'Synd': #8f4a4b, + 'Admin': hsl(300, 100%, 86.7%), + 'AI': hsl(332.2, 59.6%, 60.2%), + 'CCom': hsl(197, 62.6%, 39.8%), + 'Cling': hsl(86.7, 56.6%, 28%), + 'Cmd': hsl(53, 97.6%, 50%), + 'Engi': hsl(17, 87.8%, 61.4%), + 'Hive': hsl(300, 17.7%, 44.3%), + 'io': hsl(209.6, 100%, 55.9%), + 'Me': hsl(227, 63.5%, 60.2%), + 'Med': hsl(202, 83.6%, 64.1%), + 'OOC': hsl(47.9, 100%, 40%), + 'LOOC': hsl(207, 100%, 72%), + 'Ent': hsl(176.1, 20%, 45.1%), + 'Radio': hsl(132.8, 74.4%, 45.9%), + 'Say': hsl(213.6, 37.9%, 74.1%), + 'Sci': hsl(271.6, 91.7%, 76.5%), + 'Sec': hsl(0, 71.2%, 53.7%), + 'Supp': hsl(33.7, 44.9%, 49.8%), + 'Svc': hsl(88.1, 60.6%, 40.8%), + 'Synd': hsl(359.1, 31.8%, 42.5%), ); $channel_keys: map.keys($_channel_map) !default; diff --git a/tgui/packages/tgui-say/styles/main.scss b/tgui/packages/tgui-say/styles/main.scss index 8c7ca885c90..8e164aa21fa 100644 --- a/tgui/packages/tgui-say/styles/main.scss +++ b/tgui/packages/tgui-say/styles/main.scss @@ -7,7 +7,7 @@ // Atomic styles @include meta.load-css('~tgui/styles/atomic/text.scss'); // External styles -@include meta.load-css('~tgui/styles/components/TextArea.scss'); +@include meta.load-css('pkg:tgui-styles/components/TextArea'); // Local styles @include meta.load-css('./button.scss'); @include meta.load-css('./content.scss'); diff --git a/tgui/packages/tgui/components/DraggableControl.jsx b/tgui/packages/tgui/components/DraggableControl.jsx index 6c432d05492..c7f822ad3ed 100644 --- a/tgui/packages/tgui/components/DraggableControl.jsx +++ b/tgui/packages/tgui/components/DraggableControl.jsx @@ -136,15 +136,12 @@ export class DraggableControl extends Component { } } else if (this.inputRef) { const input = this.inputRef.current; - if (input) { - input.value = internalValue; - // IE8: Dies when trying to focus a hidden element - // (Error: Object does not support this action) - try { - input.focus(); - input.select(); - } catch {} - } + input.value = internalValue; + + setTimeout(() => { + input.focus(); + input.select(); + }, 0); } }; } diff --git a/tgui/packages/tgui/interfaces/AbductorConsole.jsx b/tgui/packages/tgui/interfaces/AbductorConsole.tsx similarity index 74% rename from tgui/packages/tgui/interfaces/AbductorConsole.jsx rename to tgui/packages/tgui/interfaces/AbductorConsole.tsx index bb2f1dffccf..d8d33053627 100644 --- a/tgui/packages/tgui/interfaces/AbductorConsole.jsx +++ b/tgui/packages/tgui/interfaces/AbductorConsole.tsx @@ -5,10 +5,33 @@ import { Section, Tabs, } from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; import { useBackend, useSharedState } from '../backend'; import { Window } from '../layouts'; -import { GenericUplink } from './Uplink/GenericUplink'; +import { GenericUplink, Item } from './Uplink/GenericUplink'; + +type AbductorConsoleData = { + categories: { name: string; items: ConsoleItem[] }[]; + + compactMode: BooleanLike; + experiment: BooleanLike; + points?: number; + credits?: number; + pad: BooleanLike; + gizmo: BooleanLike; + vest: BooleanLike; + vest_mode?: number; + vest_lock?: BooleanLike; +}; + +type ConsoleItem = { + name: string; + cost: number; + desc: string; + icon: string; + icon_state: string; +}; export const AbductorConsole = (props) => { const [tab, setTab] = useSharedState('tab', 1); @@ -47,15 +70,15 @@ export const AbductorConsole = (props) => { }; const Abductsoft = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { experiment, points, credits, categories } = data; if (!experiment) { return No Experiment Machine Detected; } - const categoriesList = []; - const items = []; + const categoriesList: string[] = []; + const items: Item[] = []; for (let i = 0; i < categories.length; i++) { const category = categories[i]; categoriesList.push(category.name); @@ -67,7 +90,9 @@ const Abductsoft = (props) => { category: category.name, cost: `${item.cost} Credits`, desc: item.desc, - disabled: credits < item.cost, + disabled: (credits || 0) < item.cost, + icon: item.icon, + icon_state: item.icon_state, }); } } @@ -92,7 +117,7 @@ const Abductsoft = (props) => { }; const EmergencyTeleporter = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { pad, gizmo } = data; if (!pad) { @@ -115,10 +140,11 @@ const EmergencyTeleporter = (props) => { @@ -126,7 +152,7 @@ const EmergencyTeleporter = (props) => { }; const VestSettings = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { vest, vest_mode, vest_lock } = data; if (!vest) { @@ -139,25 +165,25 @@ const VestSettings = (props) => { buttons={ } > - diff --git a/tgui/packages/tgui/interfaces/ApcControl.jsx b/tgui/packages/tgui/interfaces/ApcControl.jsx index caeb5ee1bed..16c74b8d955 100644 --- a/tgui/packages/tgui/interfaces/ApcControl.jsx +++ b/tgui/packages/tgui/interfaces/ApcControl.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useState } from 'react'; import { Box, @@ -11,6 +10,7 @@ import { Table, Tabs, } from 'tgui-core/components'; +import { flow } from 'tgui-core/fp'; import { useBackend, useLocalState } from '../backend'; import { Window } from '../layouts'; diff --git a/tgui/packages/tgui/interfaces/Aquarium.tsx b/tgui/packages/tgui/interfaces/Aquarium.tsx index f527bee09ea..bbdc7532634 100644 --- a/tgui/packages/tgui/interfaces/Aquarium.tsx +++ b/tgui/packages/tgui/interfaces/Aquarium.tsx @@ -1,4 +1,3 @@ -import { capitalizeFirst } from 'common/string'; import { Box, Button, @@ -12,6 +11,7 @@ import { Stack, } from 'tgui-core/components'; import { BooleanLike } from 'tgui-core/react'; +import { capitalizeFirst } from 'tgui-core/string'; import { useBackend } from '../backend'; import { Window } from '../layouts'; @@ -116,7 +116,7 @@ const FishInfo = (props) => { ml={1} style={{ fontSize: '13px', fontWeight: 'bold' }} > - {fish.fish_name.toUpperCase()} + {fish.fish_name} 0 ? -4 : 1}> {(fish.fish_health > 0 && ( diff --git a/tgui/packages/tgui/interfaces/BigManipulator.tsx b/tgui/packages/tgui/interfaces/BigManipulator.tsx index 9b0f6a3a3f6..ffca97b2534 100644 --- a/tgui/packages/tgui/interfaces/BigManipulator.tsx +++ b/tgui/packages/tgui/interfaces/BigManipulator.tsx @@ -1,4 +1,4 @@ -import { BooleanLike } from 'common/react'; +import { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../backend'; import { Button, Section, Stack, Table } from '../components'; diff --git a/tgui/packages/tgui/interfaces/Biogenerator.tsx b/tgui/packages/tgui/interfaces/Biogenerator.tsx index f87eb73d903..a978287c045 100644 --- a/tgui/packages/tgui/interfaces/Biogenerator.tsx +++ b/tgui/packages/tgui/interfaces/Biogenerator.tsx @@ -12,8 +12,7 @@ import { Table, Tabs, } from 'tgui-core/components'; -import { BooleanLike } from 'tgui-core/react'; -import { classes } from 'tgui-core/react'; +import { BooleanLike, classes } from 'tgui-core/react'; import { useBackend } from '../backend'; import { Window } from '../layouts'; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.tsx b/tgui/packages/tgui/interfaces/ChemDispenser.tsx index 5eb446e26cd..6beaf45acb0 100644 --- a/tgui/packages/tgui/interfaces/ChemDispenser.tsx +++ b/tgui/packages/tgui/interfaces/ChemDispenser.tsx @@ -18,6 +18,7 @@ type DispensableReagent = { title: string; id: string; pH: number; + color: string; pHCol: string; }; @@ -43,7 +44,7 @@ export const ChemDispenser = (props) => { const { act, data } = useBackend(); const recording = !!data.recordingRecipe; const { recipeReagents = [], recipes = [], beaker } = data; - const [hasCol, setHasCol] = useState(false); + const [showPhCol, setShowPhCol] = useState(false); const beakerTransferAmounts = beaker ? beaker.transferAmounts : []; const recordedContents = @@ -84,8 +85,8 @@ export const ChemDispenser = (props) => { icon="cog" tooltip="Color code the reagents by pH" tooltipPosition="bottom-start" - selected={hasCol} - onClick={() => setHasCol(!hasCol)} + selected={showPhCol} + onClick={() => setShowPhCol(!showPhCol)} /> } @@ -186,17 +187,15 @@ export const ChemDispenser = (props) => { ))} diff --git a/tgui/packages/tgui/interfaces/CrewManifest.jsx b/tgui/packages/tgui/interfaces/CrewManifest.jsx index 6557dda31f4..4039eb8fc3a 100644 --- a/tgui/packages/tgui/interfaces/CrewManifest.jsx +++ b/tgui/packages/tgui/interfaces/CrewManifest.jsx @@ -1,5 +1,5 @@ import { Icon, Section, Table, Tooltip } from 'tgui-core/components'; -import { classes } from '../../common/react'; +import { classes } from 'tgui-core/react'; import { useBackend } from '../backend'; import { Window } from '../layouts'; diff --git a/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx b/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx index e7b66093d20..113704af7e6 100644 --- a/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx +++ b/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx @@ -1,6 +1,3 @@ -import { BooleanLike } from 'common/react'; - -import { useBackend } from '../backend'; import { Box, Button, @@ -14,7 +11,10 @@ import { Stack, Table, Tooltip, -} from '../components'; +} from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../backend'; import { ButtonCheckbox } from '../components/Button'; import { Window } from '../layouts'; diff --git a/tgui/packages/tgui/interfaces/DeathmatchPanel.tsx b/tgui/packages/tgui/interfaces/DeathmatchPanel.tsx index 252a6e51231..3731d927c03 100644 --- a/tgui/packages/tgui/interfaces/DeathmatchPanel.tsx +++ b/tgui/packages/tgui/interfaces/DeathmatchPanel.tsx @@ -1,6 +1,3 @@ -import { BooleanLike } from 'common/react'; - -import { useBackend } from '../backend'; import { Button, Dropdown, @@ -10,7 +7,10 @@ import { Stack, Table, Tooltip, -} from '../components'; +} from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; type Lobby = { diff --git a/tgui/packages/tgui/interfaces/DecalPainter.tsx b/tgui/packages/tgui/interfaces/DecalPainter.tsx index dd835773960..5b45db3a4dd 100644 --- a/tgui/packages/tgui/interfaces/DecalPainter.tsx +++ b/tgui/packages/tgui/interfaces/DecalPainter.tsx @@ -1,5 +1,6 @@ +import { Button, ColorBox, Flex, Section } from 'tgui-core/components'; + import { useBackend } from '../backend'; -import { Button, ColorBox, Flex, Section } from '../components'; import { Window } from '../layouts'; type DecalInfo = { diff --git a/tgui/packages/tgui/interfaces/DestinationTagger.tsx b/tgui/packages/tgui/interfaces/DestinationTagger.tsx index 5326f93e01b..37fe5d3c155 100644 --- a/tgui/packages/tgui/interfaces/DestinationTagger.tsx +++ b/tgui/packages/tgui/interfaces/DestinationTagger.tsx @@ -1,7 +1,7 @@ import { map, sortBy } from 'common/collections'; +import { Button, Section, Stack } from 'tgui-core/components'; import { useBackend } from '../backend'; -import { Button, Section, Stack } from '../components'; import { Window } from '../layouts'; type DestinationTaggerData = { diff --git a/tgui/packages/tgui/interfaces/DestructiveAnalyzer.tsx b/tgui/packages/tgui/interfaces/DestructiveAnalyzer.tsx index 85a895a03e9..fda2dd32c88 100644 --- a/tgui/packages/tgui/interfaces/DestructiveAnalyzer.tsx +++ b/tgui/packages/tgui/interfaces/DestructiveAnalyzer.tsx @@ -1,7 +1,7 @@ -import { BooleanLike } from 'common/react'; +import { Box, Button, Image, NoticeBox, Section } from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../backend'; -import { Box, Button, Image, NoticeBox, Section } from '../components'; import { Window } from '../layouts'; type Data = { diff --git a/tgui/packages/tgui/interfaces/DisposalUnit.jsx b/tgui/packages/tgui/interfaces/DisposalUnit.jsx index f163c3c1c2c..08bb0a9ad19 100644 --- a/tgui/packages/tgui/interfaces/DisposalUnit.jsx +++ b/tgui/packages/tgui/interfaces/DisposalUnit.jsx @@ -1,5 +1,11 @@ +import { + Button, + LabeledList, + ProgressBar, + Section, +} from 'tgui-core/components'; + import { useBackend } from '../backend'; -import { Button, LabeledList, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; export const DisposalUnit = (props) => { diff --git a/tgui/packages/tgui/interfaces/DnaVault.tsx b/tgui/packages/tgui/interfaces/DnaVault.tsx index 2b6781d6cf7..42220a70760 100644 --- a/tgui/packages/tgui/interfaces/DnaVault.tsx +++ b/tgui/packages/tgui/interfaces/DnaVault.tsx @@ -1,6 +1,3 @@ -import { BooleanLike } from 'common/react'; - -import { useBackend } from '../backend'; import { Box, Button, @@ -8,7 +5,10 @@ import { ProgressBar, Section, Stack, -} from '../components'; +} from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; type Data = { diff --git a/tgui/packages/tgui/interfaces/DopplerArray.jsx b/tgui/packages/tgui/interfaces/DopplerArray.jsx index f62fa8de56b..14bd9b160df 100644 --- a/tgui/packages/tgui/interfaces/DopplerArray.jsx +++ b/tgui/packages/tgui/interfaces/DopplerArray.jsx @@ -1,4 +1,3 @@ -import { useBackend, useSharedState } from '../backend'; import { Box, Button, @@ -8,7 +7,9 @@ import { Section, Stack, Tabs, -} from '../components'; +} from 'tgui-core/components'; + +import { useBackend, useSharedState } from '../backend'; import { Window } from '../layouts'; export const DopplerArray = (props) => { diff --git a/tgui/packages/tgui/interfaces/EightBallVote.tsx b/tgui/packages/tgui/interfaces/EightBallVote.tsx index f85b8724ffe..c129ae0b083 100644 --- a/tgui/packages/tgui/interfaces/EightBallVote.tsx +++ b/tgui/packages/tgui/interfaces/EightBallVote.tsx @@ -1,8 +1,16 @@ -import { BooleanLike } from 'common/react'; -import { toTitleCase } from 'common/string'; +import { + Box, + Button, + Divider, + Flex, + NoticeBox, + Section, + Stack, +} from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; +import { toTitleCase } from 'tgui-core/string'; import { useBackend } from '../backend'; -import { Box, Button, NoticeBox, Section, Stack } from '../components'; import { Window } from '../layouts'; type Data = { @@ -21,12 +29,22 @@ export function EightBallVote(props) { const { data } = useBackend(); const { shaking } = data; + const idealHeight = shaking ? 265 : 70; return ( - - - {(shaking && ( - No question is currently being asked. - )) || } + + + {!shaking ? ( + + No question is currently being asked. + + ) : ( + <> + + A question is currently being asked! + + + + )} ); @@ -34,19 +52,25 @@ export function EightBallVote(props) { function EightBallVoteQuestion(props) { const { act, data } = useBackend(); - const { question, answers = [] } = data; + const { shaking, question, answers = [] } = data; return ( -
- - "{question}" - +
+ + " + {question} + " + + + + {answers.map((answer) => (
diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.jsx b/tgui/packages/tgui/interfaces/LanguageMenu.jsx index fc5a43feb61..404ac8484ef 100644 --- a/tgui/packages/tgui/interfaces/LanguageMenu.jsx +++ b/tgui/packages/tgui/interfaces/LanguageMenu.jsx @@ -1,5 +1,6 @@ +import { Button, LabeledList, Section } from 'tgui-core/components'; + import { useBackend } from '../backend'; -import { Button, LabeledList, Section } from '../components'; import { Window } from '../layouts'; export const LanguageMenu = (props) => { diff --git a/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx b/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx index fdad9955894..e584a9deed0 100644 --- a/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx +++ b/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx @@ -1,4 +1,3 @@ -import { useBackend } from '../backend'; import { Box, Button, @@ -9,7 +8,9 @@ import { Section, Stack, Tabs, -} from '../components'; +} from 'tgui-core/components'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; type Data = { diff --git a/tgui/packages/tgui/interfaces/LaunchpadRemote.tsx b/tgui/packages/tgui/interfaces/LaunchpadRemote.tsx index 5e7d1ba8516..8dfa89318ed 100644 --- a/tgui/packages/tgui/interfaces/LaunchpadRemote.tsx +++ b/tgui/packages/tgui/interfaces/LaunchpadRemote.tsx @@ -1,7 +1,7 @@ -import { BooleanLike } from 'common/react'; +import { NoticeBox } from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../backend'; -import { NoticeBox } from '../components'; import { Window } from '../layouts'; import { LaunchpadControl } from './LaunchpadConsole'; diff --git a/tgui/packages/tgui/interfaces/Lawpanel.tsx b/tgui/packages/tgui/interfaces/Lawpanel.tsx index 3ce64868ff8..7d72c527ab2 100644 --- a/tgui/packages/tgui/interfaces/Lawpanel.tsx +++ b/tgui/packages/tgui/interfaces/Lawpanel.tsx @@ -1,6 +1,3 @@ -import { BooleanLike } from 'common/react'; - -import { useBackend } from '../backend'; import { Button, Collapsible, @@ -11,7 +8,10 @@ import { NoticeBox, Section, Stack, -} from '../components'; +} from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; const lawtype_to_color = { diff --git a/tgui/packages/tgui/interfaces/LibraryAdmin.tsx b/tgui/packages/tgui/interfaces/LibraryAdmin.tsx index fd88fdc48fb..5e9ead80a60 100644 --- a/tgui/packages/tgui/interfaces/LibraryAdmin.tsx +++ b/tgui/packages/tgui/interfaces/LibraryAdmin.tsx @@ -1,8 +1,5 @@ import { map, sortBy } from 'common/collections'; -import { capitalize } from 'common/string'; import { useState } from 'react'; - -import { useBackend, useLocalState } from '../backend'; import { Box, Button, @@ -13,7 +10,10 @@ import { Stack, Table, TextArea, -} from '../components'; +} from 'tgui-core/components'; +import { capitalize } from 'tgui-core/string'; + +import { useBackend, useLocalState } from '../backend'; import { Window } from '../layouts'; import { PageSelect } from './LibraryConsole'; diff --git a/tgui/packages/tgui/interfaces/LibraryConsole.jsx b/tgui/packages/tgui/interfaces/LibraryConsole.jsx index de10b3a2e86..87d87f9bac8 100644 --- a/tgui/packages/tgui/interfaces/LibraryConsole.jsx +++ b/tgui/packages/tgui/interfaces/LibraryConsole.jsx @@ -1,8 +1,5 @@ import { map, sortBy } from 'common/collections'; -import { classes } from 'common/react'; import { useState } from 'react'; - -import { useBackend, useLocalState } from '../backend'; import { Box, Button, @@ -16,7 +13,10 @@ import { Section, Stack, Table, -} from '../components'; +} from 'tgui-core/components'; +import { classes } from 'tgui-core/react'; + +import { useBackend, useLocalState } from '../backend'; import { Window } from '../layouts'; import { sanitizeText } from '../sanitize'; diff --git a/tgui/packages/tgui/interfaces/LibraryScanner.jsx b/tgui/packages/tgui/interfaces/LibraryScanner.jsx index efa7156abfb..3f06b7b3ab3 100644 --- a/tgui/packages/tgui/interfaces/LibraryScanner.jsx +++ b/tgui/packages/tgui/interfaces/LibraryScanner.jsx @@ -1,5 +1,6 @@ +import { Button, NoticeBox, Section, Stack } from 'tgui-core/components'; + import { useBackend } from '../backend'; -import { Button, NoticeBox, Section, Stack } from '../components'; import { Window } from '../layouts'; export const LibraryScanner = (props) => { diff --git a/tgui/packages/tgui/interfaces/LibraryVisitor.jsx b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx index cb27e42704d..50ea9bad108 100644 --- a/tgui/packages/tgui/interfaces/LibraryVisitor.jsx +++ b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx @@ -1,6 +1,4 @@ import { map, sortBy } from 'common/collections'; - -import { useBackend } from '../backend'; import { Box, Button, @@ -10,7 +8,9 @@ import { Section, Stack, Table, -} from '../components'; +} from 'tgui-core/components'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; import { PageSelect } from './LibraryConsole'; diff --git a/tgui/packages/tgui/interfaces/LightController.tsx b/tgui/packages/tgui/interfaces/LightController.tsx index 810b644f90f..e65104fd9c5 100644 --- a/tgui/packages/tgui/interfaces/LightController.tsx +++ b/tgui/packages/tgui/interfaces/LightController.tsx @@ -1,9 +1,17 @@ import { useState } from 'react'; +import { + Box, + Button, + Knob, + Section, + Slider, + Stack, + Tabs, +} from 'tgui-core/components'; import { round } from '../../common/math'; import { BooleanLike, classes } from '../../common/react'; import { useBackend } from '../backend'; -import { Box, Button, Knob, Section, Slider, Stack, Tabs } from '../components'; import { Window } from '../layouts'; enum Direction { diff --git a/tgui/packages/tgui/interfaces/LightSpawn.tsx b/tgui/packages/tgui/interfaces/LightSpawn.tsx index 68e3430d817..75c1f2cf07d 100644 --- a/tgui/packages/tgui/interfaces/LightSpawn.tsx +++ b/tgui/packages/tgui/interfaces/LightSpawn.tsx @@ -1,8 +1,16 @@ import { useState } from 'react'; +import { + Box, + Button, + Knob, + Section, + Slider, + Stack, + Tabs, +} from 'tgui-core/components'; import { classes } from '../../common/react'; import { useBackend, useLocalState } from '../backend'; -import { Box, Button, Knob, Section, Slider, Stack, Tabs } from '../components'; import { Window } from '../layouts'; enum Direction { diff --git a/tgui/packages/tgui/interfaces/Limbgrower.jsx b/tgui/packages/tgui/interfaces/Limbgrower.jsx index 363b416f12f..8ee02d086b7 100644 --- a/tgui/packages/tgui/interfaces/Limbgrower.jsx +++ b/tgui/packages/tgui/interfaces/Limbgrower.jsx @@ -1,4 +1,3 @@ -import { useBackend, useSharedState } from '../backend'; import { Box, Button, @@ -7,7 +6,9 @@ import { LabeledList, Section, Tabs, -} from '../components'; +} from 'tgui-core/components'; + +import { useBackend, useSharedState } from '../backend'; import { Window } from '../layouts'; export const Limbgrower = (props) => { diff --git a/tgui/packages/tgui/interfaces/LingMMITalk.tsx b/tgui/packages/tgui/interfaces/LingMMITalk.tsx index 9f4effb2e66..fc1f0da7c9e 100644 --- a/tgui/packages/tgui/interfaces/LingMMITalk.tsx +++ b/tgui/packages/tgui/interfaces/LingMMITalk.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; +import { Button, ByondUi, Stack, TextArea } from 'tgui-core/components'; import { useBackend } from '../backend'; -import { Button, ByondUi, Stack, TextArea } from '../components'; import { Window } from '../layouts'; type Data = { diff --git a/tgui/packages/tgui/interfaces/LockedSafe.tsx b/tgui/packages/tgui/interfaces/LockedSafe.tsx index ba8d6bc9661..77d042912af 100644 --- a/tgui/packages/tgui/interfaces/LockedSafe.tsx +++ b/tgui/packages/tgui/interfaces/LockedSafe.tsx @@ -1,7 +1,7 @@ -import { BooleanLike } from 'common/react'; +import { Box, Flex } from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../backend'; -import { Box, Flex } from '../components'; import { Window } from '../layouts'; import { NukeKeypad } from './NuclearBomb'; diff --git a/tgui/packages/tgui/interfaces/LogViewer.tsx b/tgui/packages/tgui/interfaces/LogViewer.tsx index ffdd18a730f..0fe5daf5ec7 100644 --- a/tgui/packages/tgui/interfaces/LogViewer.tsx +++ b/tgui/packages/tgui/interfaces/LogViewer.tsx @@ -1,6 +1,4 @@ import { useState } from 'react'; - -import { useBackend } from '../backend'; import { Button, Collapsible, @@ -8,7 +6,9 @@ import { NoticeBox, Section, Stack, -} from '../components'; +} from 'tgui-core/components'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; type LogViewerData = { diff --git a/tgui/packages/tgui/interfaces/MODpaint.jsx b/tgui/packages/tgui/interfaces/MODpaint.jsx index 7a047b38426..0d9a56e459e 100644 --- a/tgui/packages/tgui/interfaces/MODpaint.jsx +++ b/tgui/packages/tgui/interfaces/MODpaint.jsx @@ -1,6 +1,3 @@ -import { capitalize } from 'common/string'; - -import { useBackend } from '../backend'; import { Box, Button, @@ -9,7 +6,10 @@ import { Section, Slider, Stack, -} from '../components'; +} from 'tgui-core/components'; +import { capitalize } from 'tgui-core/string'; + +import { useBackend } from '../backend'; import { Window } from '../layouts'; const colorToMatrix = (param) => { diff --git a/tgui/packages/tgui/interfaces/MODsuit.tsx b/tgui/packages/tgui/interfaces/MODsuit.tsx index 11d1e294824..d2b96a7735c 100644 --- a/tgui/packages/tgui/interfaces/MODsuit.tsx +++ b/tgui/packages/tgui/interfaces/MODsuit.tsx @@ -1,7 +1,4 @@ -import { BooleanLike } from 'common/react'; import { useState } from 'react'; - -import { useBackend } from '../backend'; import { AnimatedNumber, Box, @@ -18,32 +15,39 @@ import { Section, Stack, Table, -} from '../components'; +} from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../backend'; import { formatSiUnit } from '../format'; import { Window } from '../layouts'; type MODsuitData = { // Static ui_theme: string; - control: string; complexity_max: number; - parts: PartData[]; // Dynamic suit_status: SuitStatus; user_status: UserStatus; module_custom_status: ModuleCustomStatus; module_info: Module[]; + control: string; + parts: PartData[]; }; type PartData = { slot: string; name: string; + deployed: BooleanLike; + ref: string; }; type SuitStatus = { core_name: string; - cell_charge_current: number; - cell_charge_max: number; + charge_current: number; + charge_max: number; + chargebar_color: string; + chargebar_string: string; active: BooleanLike; open: BooleanLike; seconds_electrified: number; @@ -53,8 +57,8 @@ type SuitStatus = { complexity: number; selected_module: string; ai_name: string; - has_pai: boolean; - is_ai: boolean; + has_pai: BooleanLike; + is_ai: BooleanLike; link_id: string; link_freq: string; link_call: string; @@ -257,6 +261,20 @@ const ConfigurePinEntry = (props) => { ); }; +// fuck u smartkar configs werent meant to be used as actions 🖕🖕🖕 +// and really u couldnt be bothered to make this and instead used +// the pin entry? 🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕 +const ConfigureButtonEntry = (props) => { + const { name, value, module_ref } = props; + const { act } = useBackend(); + return ( + + + ))} + +
+ ); +} diff --git a/tgui/packages/tgui/interfaces/ScannerGate.jsx b/tgui/packages/tgui/interfaces/ScannerGate.jsx index 51e32cbdc6e..8eb61cea7f2 100644 --- a/tgui/packages/tgui/interfaces/ScannerGate.jsx +++ b/tgui/packages/tgui/interfaces/ScannerGate.jsx @@ -67,10 +67,6 @@ const SCANNER_GATE_ROUTES = { title: 'Scanner Mode: Nutrition', component: () => ScannerGateNutrition, }, - Contraband: { - title: 'Scanner Mode: Contraband', - component: () => ScannerGateContraband, - }, }; const ScannerGateControl = (props) => { @@ -98,7 +94,6 @@ const ScannerGateControl = (props) => { const ScannerGateOff = (props) => { const { act, data } = useBackend(); - const { contraband_enabled } = data; return ( <> Select a scanning mode below. @@ -127,11 +122,6 @@ const ScannerGateOff = (props) => { content="Nutrition" onClick={() => act('set_mode', { new_mode: 'Nutrition' })} /> -